Skip to content

Emscripten browser environment

kripken edited this page Jul 23, 2012 · 13 revisions

The browser environment is different than the environment a normal C/C++ application expects. The main differences are how input and output work, and the fact that the main loop must be asychronous.

For input and output, if you use SDL etc. then everything should basically be taken care of for you. Emscripten implements the familiar SDL API for the browser environment. Typically you should need to refactor nothing or a very small amount of code for this.

The main loop being asynchronous, however, is trickier.

Browser main loop

Graphical C++ apps typically have a main loop that is an infinite loop, in which event handling is done, processing and rendering, then a wait (SDL_Delay, for example) to keep the frame rate right. However, in JS there is no way for something like SDL_Delay to actually return control to the browser event loop. The reason is that the browser event model is asynchronous - your JavaScript must finish its "turn", by completing execution and returning control to the browser itself. The browser will then asynchronously call your JavaScript later according to how you asked it to do that.

This is inherent in how browsers work. If you do not finish your turn, the page will 'hang' and the browser will eventually tell the user the page is stuck and offer to halt it or close it. Also, that things like WebGL will only actually render when your JS "turn" is over - so while a normal C++ GL app would swap buffers manually, in JS you just finish your turn and the browser renders and swaps.

Implementing an asynchronous main loop in C/C++

The standard way to do this is make a C function that runs one iteration of your main loop. Then call it from JS at the proper frequency. This is very simple to do manually (just call it from JS, all you need is an underscore at the beginning of the name), but you can also use emscripten_set_main_loop (see emscripten.h) for something a little more convenient. For a regular native build, you can just call that function in an infinite loop (likely with SDL_Delay etc.), so typically you will have a small section with #ifdef EMSCRIPTEN for the two cases.