Nook and Emscripten: A technical look at C++ GameDev in the Browser

I run an online game-jam event called Ludum Dare, and this past weekend I got together with a friend (Derek Laufman of Halfbot) and we made a game. What did we make? A platformer about size.

Nook - A game where size matters. Click the image to Play!

The game was created over a period 72 hours. Somehow, amazing to me, we managed to pull it together despite our responsibilities.

  • Me running the event (Ludum Dare)
  • Family gathering at my brothers I attended on the Saturday
  • Car appointment on Monday
  • Derek taking care of his baby

Fortunately, running Ludum Dare is only busy for me before the event, and at submission time (two of them, 3-5 hours each). During the event I’m free to sit down and do whatever I want. This weekend was the Diablo 3 beta test, and if I was a lesser man, I’d have played that. Fortunately for me, Derek came to me a couple weeks ago and suggested we make a game together, I said yes, and the rest is history.

It’s totally silly, but I’m actually proud of the fact that I managed to attend the family gathering and the car appointment. Stupid me, I could have actually cancelled/rescheduled the appointment for Tuesday or Wednesday, but I decided to go ahead with it anyway. The car appointment was my yearly service for my smartcar, and I ended up staying in the neighborhood of the dealership for about 5 hours of that day. Working on my Ludum Dare game on my laptop, I had WIFI at the dealership, and also McDonalds, where I parked myself for a couple hours. To be honest, this was probably the first time I seriously “Coffee Shop Developed”, and where I actually got some serious work done. It was fun, because it was out of the ordinary. :)

Anyways, that’s enough back story. Lets get nerdy.


Emscripten

Emscripten is a wonderful tool. I’ve been jokingly referring to it as witchcraft and other colloquialisms for evil, since it does something rather outrageous: Compiles C++ to JavaScript. To some programmers (vocal programmers), that just might be one of the greatest heresies ever committed to page (thou shall not mix chocolate and peanut butter). Me, I actually quite like JavaScript, having developed a taste for it last Ludum Dare.

I’ve been a big supporter of the idea behind similar “C++ to Web” technologies (Adobe Alchemy, Google Native Client), but both seem to have some issues to deal with today (was not part of the early access program, not enabled by default in Chrome). Emscripten on the other hand is out there, it’s free for everyone, and it works on all browsers today. Emscripten’s problem seems to be one of obscurity (success stories forthcoming), how unbelievable the idea of C++ to JS is to most developers, and the technical know-how needed to use it.

Setting Up Emscripten

I wont bother with a step by step description. You should go here for details:

https://github.com/kripken/emscripten/wiki/Tutorial

I will however talk about my specific configuration.

Me, I run Emscripten inside MSys, which is MinGW‘s faux Unix shell. If you’re not familiar with MinGW, it’s a GCC compiler suite and libraries needed for building apps for Windows. Emscripten’s “emcc” works just like GCC, but generates JavaScript code. So a nice bonus with my setup, I have everything needed to make both Windows and Web “binaries”.

To use Emscripten, you need version 3.0 of LLVM and Clang installed. As a Windows user, for whatever reason, 3.0 binaries were unavailable (older binaries were). So I had to manually build them on my PC via the MSys shell.

Read the instructions on installing Clang. Clang and LLVM are built at the exact same time via LLVM. Building is done in a typical “./configure; make; make install”, like you would do on a Linux. Browse to the LLVM root folder, run configure (“./configure”), make it (“make”), and make install it (“make install”).

If configure complains about a missing Linux’y package, you can use the tool “mingw-get” from the command-line to install additional packages (just like on a Linux). mingw-get install package

Grab Python 2.7, and add the path to it to your “path” environment variable. Parts of Emscripten are Python.

Grab Node.js. This is needed to run JavaScript code from the command line. Parts of Emscripten are written JS.

Grab the Java Runtime (OpenJDK if on Linux). This is needed by Google’s closure compiler, which is a tool that will later help you obfuscate and optimize.

That’s everything. Follow the tutorial and build something pretty now.

Using Emscripten

Emscripten uses magic, also known as LLVM and Clang to convert C and C++ code to bytecodes, and reconstructs new JavaScript code based on those generated bytecodes. You should have an understanding of JavaScript coming in to this, as it’s often wise to check the generated code to see what LLVM+Clang+Emscripten are doing, especially when starting out. The project is mature and functional, but you will learn more from seeing and doing than you will from reading documentation. The documentation is good for filling in the gaps of things that aren’t typical with a normal GCC-like C and C++ compiler.

A typical project generated by Emscripten will be an HTML file (eg. index.html) and a JS file (eg. mycode.js). I like having a separate HTML file, as it means you can include other things (external map data files) inside it. If you wanted to though, you could include the JavaScript code right inside the HTML file.

Crunch Time Compile Script

I didn’t have much time this weekend to think, so I did all my compiling via simple shell script that I tweaked and added to whenever I needed. Normally I’d have a proper makefile system for building, but I was still learning the ins and outs of Emscripten, so I opted for more hackibility.

Some notes:

  • I explicitly disabled the closure compiler in the optimizations (–closure 0). Eventually I re-added it, but manually (last line). I tracked down some very very unusual Chrome only performance bugs that were caused by closure Compiler optimizations. Whitespace removal works fine and is totally safe, so I’m doing only that now.
  • –pre-js takes only a single file, so I had to combine all my JS code in to a single file first. Someone should really put in a feature request in to allow multiple –pre-js calls. :D
  • Audio support in browsers is *shrug*, so I have 3 audio libraries I enable/comment out between (SoundManager 2, Buzz, and none).
  • For a brief time, I was including my map file alongside my other JS code. I eventually moved mention of it in to the HTML file, so that file would be read every time (without the need to re-link the binary).

C and C++ Code

A C function named “MyFunction” will become “_MyFunction” inside JavaScript scope. Conversely, a JavaScript function prefixed with an underscore will be accessible from your C code. Just prototype it before hand (“void MyFunction();”) and you’re golden.

I’d recommend using basic types when passing data between JS and C++. Floats and Integers where possible, and char*’s for strings. Strings require a little more work to use, but aren’t bad. See the “Other Methods” section for details:

https://github.com/kripken/emscripten/wiki/Interacting-with-code

C++ functions are a little tricker. If you’ve ever tried mixing and matching C and C++ code, you’re probably familiar with C++ Name Mangling. You need to deal with mangling here if you want to use C++ code.

void GameDraw() becomes __Z8GameDrawv();
void GameInput( float X, float Y, int New, int Old ) becomes __Z9GameInputffii( var, var, var, var );

The encoding isn’t too hard to decipher, but it’s probably easiest to just open the output JavaScript file, and search for the symbol. “GameDraw”, and keep looking until you get one that’s wacky like those mangled names above.

Too keep things simpler on the JS->C++ side, I’d recommend sticking with single underscores and wrapping your prototypes in “extern C” sections.

Graphics

Emscripten ships with a version of the popular SDL game library. In addition, there’s a bunch of work done on a OpenGL port, based on WebGL. I’d expect to see more and more things ported as the GL libraries mature.

Me I’m slightly impatient, and have my own libraries that wrap OpenGL on the PC/Mobile side. I didn’t use any of my existing graphics code, but created a new JavaScript library based on the same naming scheme. Before this weekend, I had only minimal experience using Emscripten, so I didn’t want to pigeonhole myself in to my existing libraries. I don’t use floats in my libraries, but instead I use a type “Real” which is synonymous, and includes some extra features. After today, a change I would make is have my drawing libraries accept floats, and lightly wrap them with versions that take Real’s. That would make building a consistent interface across targets nicer.

For understanding Canvas 2D’s rendering capabilities, I recommend Mozilla’s tutorial.

https://developer.mozilla.org/en/Canvas_tutorial

All my art assets are PNG files that I load from disk.

Music and Audio

I wrote a generic interface to load and play sound files. All JS sound libraries use MP3 or OGG files, which makes them suitable for music playback too. However, unfortunately, no matter what library you use, looping of music has an annoying gap and isn’t seamless.

Options include:

I use both. I started with SoundManager 2, because I occasionally notice weirdness in native HTML5 playback. But for the updated version (the link above), I switched back to Buzz. There are cases when the sound gets broken on Chrome, but SoundManager 2 has a delay. I prefer delayless.

SoundManager 2 also supports multi-shot sound playing (i.e. the sound doesn’t interrupt the last one played), but only with its Flash 9 based noisemaker. This is cool, but for the style of game we were making, interrupting sounds wasn’t a problem.

Optimizations and Performance

If you’ve ever implemented a scripting language in to a game engine, you’ll be familiar with the idea that native code is faster. A “Draw” function that blits a sprite or renders some 3D geometry should always be in native code, and not implemented in script. Working with Emscripten is a bit of that. For the most part, Emscripten does a really good job optimizing, and often does things you would forget if optimizing by hand. However, there are times when going native (in this case JavaScript) will speed up your game.

File IO

Emscripten includes a suite of function for faking data in a filesystem. They work fine and are totally easy to use (can be automatically done via command-line arguments to the linker). However, using a JSON parsing library written in C to read a 400k JSON file that has been artificially encoded in to the virtual file system can be … wasteful.

This is actually what I did at first. The map was originally a 200k JSON file, exported by Tiled. It was a bit horrible, taking about 4 seconds to parse, but tolerable. Once we grew the map to a >400k file, things got out of hand (12 seconds).

Since JSON stands for JavaScript Object Notation (duh, the native object format of JavaScript), my solution was to include the JSON file alongside the rest of the JavaScript code in the HTML page.

A JSON file however lacks an actual usable name in JavaScript. So to avoid weird cross-file crying by a “secure” browser, I wrote a tool to append an appropriate name on to the file.

The tool was placed alongside the JSON file, so we could simply drag+drop the exported JSON file on top of it to regenerate a usable game map. Oh and my apologies, the above code uses a FileIO library of mine, but what it’s doing should still make sense. A better tool would generate a type name based on the filename. Me, I hardcoded it to save time and thinking.

Finally, I wrote a series of functions for looking at the data. With the JSON C loader, I would pinpoint the map data and the dimensions of the map file inside the JSON file. I wrote similar functions, one for each piece of data I wanted, and dropped them in to my existing loader code.

Done. We now had instant file loading, and a way for the artist to change/edit the map and see his changes.

Graphical Rendering and Performance

Firefox and Chrome both have good profiling tools. If you happen to fire them up, and play the game for about 30 seconds, you’ll notice a few functions stand out. The one I want us to take note of is “_gelDrawTiles“. This is a function written in JavaScript that draws a layer of the map. That’s lots and lots of little 8×8 tiles. The function itself is rather complicated, but it’s an improvement before. Originally, the C++ code called a function “_gelDrawTile” (note the lack of S on the end), one for every single tile in the world. At 40 by 30 tiles, by 4 layers, that’s potentially A LOT of overhead wasted by function calls. Since this draw function was written in JS, it couldn’t exactly be inlined (as expected in C++).

So, I changed the tilemap layer rendering in to a single function call written in JavaScript.

The map data was located inside the C++ code though, so I had to get a pointer and call an Emscripten provided function “getValue” to get the actual data. However, this was a BRAND NEW wasteful excessive function call, so like the author of Emscripten suggests, I went in to the code to see how to access the data I wanted (HEAP16[Pointer+Offset]).

The other heavy functions seen by the profiler relate to the collision detection. While writing this though, I just realized I might be able to improve that, so *ahem* disregard them, okay? :)

Standard Template Library – STL

This isn’t particularly a speed optimization, but a size one. If you’ve ever worked with STL as both a C and C++ programmer, you’ll note that STL adds quite a lot of size to program. It performs well, but depending on the compiler, you can see a whopping 500k to 1MB increase in file size over a C program, or a C++ program simply using your own C++ containers.

If you read above, you’ll see that I have my own suite of libraries for doing various things. I use C++, but a number of my libraries use a C style aesthetic (or Pure Functions if you’re in a buzzword mood). So instead of STL, I used my own equivalents and variants.

That said, I’m not saying don’t use STL. If you like it, use it. I like it actually, but I’ve been coding professionally for over a decade now, and have built up my own libraries and ways of doing things. There are some things STL does well, and others STL doesn’t. I actually was planning to use it at first, but after I saw the code size grow by ~500k, I decided not to.

RequestAnimationFrame instead of setInterval

This I was informed of after Ludum Dare.

http://paulirish.com/2011/requestanimationframe-for-smart-animating/

In my tests, it seemed to make Chrome work a little better.

I did like the elegance of this better though:

And manipulating the IntervalHandle to stop (lost focus), and restarting it via setInterval again (focus gained).

But hey, the RequestAnimationFrame stuff is supposed to be better.

Wrapup

That’s all I can think of at the moment. If there’s something you want to know about that I didn’t cover, feel free to ask. I may expand this post accordingly.

6 Responses to “Nook and Emscripten: A technical look at C++ GameDev in the Browser”

  1. lorancou says:

    C++ compiled to Javascript. Peanut butter with chocolate. Maybe I should look into that. I though Haxe could be the solution to JS missing language features to scale up; maybe Emscripten is even a better choice.

    “And manipulating the IntervalHandle to stop (lost focus), and restarting it via setInterval again (focus gained). But hey, the RequestAnimationFrame stuff is supposed to be better.”

    The RequestAnimationFrame is here to do that job for you :) So your draw function doesn’t get called when your browser is minimized or when you’re using another tab, saving resources for other processes.

  2. [...] A blog post on tooNormal.com by Mike Kasprzak on using Emscripten to create a HTML5 game using C++.  [...]

  3. Vinícius says:

    Really nice game. Congratulations!

  4. Benjamin Johns says:

    Extremely insightful article. I’d heard about Emscripten before and seen it used to convert some robust C++ tools into client-side web apps for one reason or another, but this is the first solid writeup I’d seen about using it for a game, and you got some damn impressive results out of it.

    Thanks for doing the writeup!

  5. valdir says:

    any chance of sharing the code for loading the png? im having serious problems to load PNG on my code, i have tried libpng, embedding preloading, but all have problems for me

    i have a texture atlas that is 2048×2048 and has been quite problematic, so im thinking of switching to DXT rendered directly in wegbgl not on the CPP side

    thanks!

  6. valdir says:

    nvm solved :P (im using compressed images now instead of pngs)

    now im looking for any tips on input code, i know there is SDL, but do i have to use such a big library just for that? or should i mix javascript input have no idea :P

Leave a Reply