This FAQ contains answers to many questions asked on IRC and the mailing list.
See the Emscripten Tutorial and emcc.
All the tests in the Emscripten test suite are known to build and pass on the master branch, so if these are failing it is likely that there is some problem with your environment.
First call ./emcc -v, which runs basic sanity checks and prints out useful environment information. If that doesn’t help, follow the instructions in Verifying the Emscripten Development Environment.
You might also want to go through the Emscripten Tutorial again, as this is updated as Emscripten changes.
If something doesn’t work (for example a compiler flag, a libc function, etc.) then first search the comprehensive documentation on this site.
Next check if there is a test for the failing functionality in the Emscripten test suite (run grep -r in tests/). All the tests are known to pass on the master branch, so they provide concrete “known-good” examples of how various options and code are used.
In most cases you will be able to use your project’s current build system with Emscripten. See Building Projects.
Emscripten makes some trade-offs that make the generated code faster and smaller, at the cost of longer compilation times. For example, we build parts of the standard library along with your code, which enables some additional optimizations, but takes a little longer to compile.
Note
You can determine what compilation steps take longest by compiling with EMCC_DEBUG=1 in the environment and then reviewing the debug logs (by default in /tmp/emscripten_temp). Note that compiling in debug mode takes longer than normal, because we print out a lot of intermediate steps to disk.
The main tips for improving build time are:
Create fully optimized builds less frequently — use -O0 during frequent development iterations (or don’t specify an optimization level).
- Compiling with higher levels of optimization can in some cases be noticeably slower: -O2 is slower than -O1, which is in turn slower than -O0.
- Compiling with -O3 is especially slow — this can be mitigated by also enabling -s AGGRESSIVE_VARIABLE_ELIMINATION=1 (removing variables makes the -O3 regalloc easier).
Compile without line number debug information (use -O1 or -g0 instead of -g):
- Currently builds with line-number debug information are slow (see issue #216). Stripping the debug information significantly improves compile times.
Compile on a machine with more cores:
- Emscripten can run some passes in parallel (specifically, the JavaScript optimisations). Increasing the number of cores results in an almost linear improvement.
- Emscripten will automatically use more cores if they are available. You can control how many cores are used with EMCC_CORES=N (this is useful if you have many cores but relatively less memory).
Make sure that the native optimizer is being used, which greatly speeds up optimized builds as of 1.28.2. EMCC_DEBUG=1 output should not report errors about the native optimizer failing to build or not being used because of a previous failed build (if it previously failed, do emcc --clear-cache then compile your file again, and the optimizer will be automatically rebuilt).
Make sure you optimize code by building with -O2 (even more aggressive optimization is available, at the cost of significantly increased compilation time).
Make sure you build with -O2 so code is optimized and minified. You should also set up gzip compression on your webserver, which all browsers now support.
Note
You can use the closure compiler to reduce code size even further (--closure 1). However that will require that your code be prepared for closure compiler advanced optimizations, including proper exports and so forth. It is usually not worth the effort over an optimized build and supporting gzip on your webserver.
Make sure you are using the Emscripten bundled system headers. Using emcc will do so by default, but problems may occur if you use your local system headers with emcc or compile into LLVM bitcode yourself.
Make sure that you are running an optimized build (smaller builds are faster to start up). If the sheer code size is causing the slow startup, you can try Outlining: a workaround for JITs and big functions.
Network latency is also a possible factor in startup time. Consider putting the file loading code in a separate script element from the generated code so that the browser can start the network download in parallel to starting up the codebase (run the file packager and put file loading code in one script element, and the generated codebase in a later script element).
Emscripten cannot compile inline assembly code (because it is CPU specific, and Emscripten is not a CPU emulator).
You will need to find where inline assembly is used, and disable it or replace it with platform-independent code.
Note
Emscripten automatically unsets the following #define values, as these are commonly set in projects to enable platform dependent code (inline assembly):
#undef __i386__
#undef __x86_64__
The browser event model uses co-operative multitasking — each event has a “turn” to run, and must then return control to the browser event loop so that other events can be processed. A common cause of HTML pages hanging is JavaScript that does not complete and return control to the browser.
Graphical C++ apps typically have an infinite main loop in which event handling, processing and rendering is done, followed by a delay to keep the frame-rate right (SDL_DELAY in SDL apps). As the main loop does not complete (is infinite) it cannot return control to the browser, and the app will hang.
Apps that use an infinite main loop should be re-coded to put the actions for a single iteration of the loop into a single “finite” function. In the native build this function can be run in an infinite loop as before. In the Emscripten build it is set as the main loop function and will be called by the browser at a specified frequency.
There is more information on this topic in Emscripten Runtime Environment.
To run a C function repeatedly, use emscripten_set_main_loop() (this is discussed in Emscripten Runtime Environment). The related functions in emscripten.h are also useful, allowing you to add events that block the main loop, etc.
To respond to browser events use the SDL API in the normal way. There are examples in the SDL tests (search for SDL in tests/runner.py).
See also: Why does my HTML app hang?
See the SDL automatic tests for working examples: python tests/runner.py browser.
System libraries that are included with Emscripten are automatically linked when you compile (just the necessary parts). This includes libc, libc++ (C++ standard library) and SDL.
Libraries not included with Emscripten (like Boost) must be compiled and linked with the program just as if they were a module in the project.
There is a set of libraries ported to Emscripten for convenient use, Emscripten Ports. See Building Projects
Another option is to implement needed C APIs as JavaScript librarys (see --js-library in emcc and Implement a C API in JavaScript). Emscripten itself does this for libc (not including malloc) and SDL (but not libc++ or malloc).
Note
Emscripten has partial support for SDL (1, not 2) audio, and OpenAL.
To use SDL audio, include it as #include <SDL/SDL_mixer.h>. You can use it that way alongside SDL1, SDL2, or another library for platform integration.
Emscripten uses a virtual file system that may be preloaded with data or linked to URLs for lazy loading. See the File System Overview for more details.
Emscripten-generated code running in the browser cannot access files in the local file system. Instead you can use preloading and embedding to work around the lack of synchronous file IO. See File System Overview for more information.
It is possible to allow access to local file system for code running in node.js.
(You may need this answer if you see an error saying something like you need to wait for the runtime to be ready (e.g. wait for main() to be called).)
Calling a compiled function before a page has fully loaded can result in an error, if the function relies on files that may not be present (for example the .mem file and preloaded files are loaded asynchronously).
The easiest way to find out when loading is complete is to add a main() function, and within it call a JavaScript function to notify your code that loading is complete.
Note
The main() function is called after startup is complete as a signal that it is safe to call any compiled method.
For example, if allReady() is a JavaScript function you want called when everything is ready, you can do:
#include <emscripten.h>
int main() {
EM_ASM( allReady() );
}
You can also define a main() function in JavaScript:
Module['_main'] = function() { ... };
or
Module['_main'] = allReady;
What happens in practice is that when code is ready to be run, we check for Module._main. If present, we call it. If a main() function was compiled from C, it will be there (and it will be a JavaScript function). But, you can also just define a JavaScript function there, either will work.
Another option is to define an onRuntimeInitialized function,
Module['onRuntimeInitialized'] = function() { ... };
That method will be called when the runtime is ready and it is ok for you to call compiled code. In practice, that is exactly the same time at which main() would be called, so onRuntimeInitialized doesn’t let you do anything new, but it can be convenient in some cases - for example, if you use onRuntimeInitialized and don’t define a main() function, then the runtime will not be shut down after main() exits, and you can keep calling compiled methods (you can also have a main() and build with -s NO_EXIT_RUNTIME=1 to keep the runtime from being shut down). Thus, for libraries, onRuntimeInitialized can be convenient.
Here is an example of how to use it:
<script type="text/javascript">
var Module = {
onRuntimeInitialized: function() {
Module._foobar(); // foobar was exported
}
};
</script>
<script type="text/javascript" src="my_project.js"></script>
The crucial thing is that Module exists, and has the property onRuntimeInitialized, before the script containing emscripten output (my_project.js in this example) is loaded.
Emscripten does dead code elimination of functions that are not called from the compiled code. While this does minimize code size, it can remove functions that you plan to call yourself (outside of the compiled code).
To make sure a C function remains available to be called from normal JavaScript, it must be added to the EXPORTED_FUNCTIONS using the emcc command line. For example, to prevent functions my_func() and main() from being removed/renamed, run emcc with:
./emcc -s EXPORTED_FUNCTIONS="['_main', '_my_func']" ...
If your function is used in other functions, LLVM may inline it and it will not appear as a unique function in the JavaScript. Prevent inlining by defining the function with EMSCRIPTEN_KEEPALIVE:
void EMSCRIPTEN_KEEPALIVE yourCfunc() {..}
Note
Another possible cause of missing code is improper linking of .a files. The .a files link only the internal object files needed by previous files on the command line, so the order of files matters, and this can be surprising. If you are linking .a files, make sure they are at the end of the list of files, and in the right order amongst themselves. Alternatively, just use .so files instead in your project.
Tip
It can be useful to compile with EMCC_DEBUG=1 set for the environment (EMCC_DEBUG=1 emcc ... on Linux, set EMMCC_DEBUG=1 on Windows). This splits up the compilation steps and saves them in /tmp/emscripten_temp. You can then see at what stage the code vanishes (you will need to do llvm-dis on the bitcode stages to read them, or llvm-nm, etc.).
The Closure Compiler will minify the File Server API code. Code that uses the file system must be optimized with the File System API, using emcc’s --pre-js option.
The Closure Compiler minifies variable names, which results in very short variable names like i, j, xa, etc. If other code declares variables with the same names in global scope, this can cause serious problems.
This is likely to be the cause if you can successfully run code compiled with -O2 set and --closure unset.
One solution is to stop using small variable names in the global scope (often this is a mistake — forgetting to use var when assigning to a variable).
Another alternative is to wrap the generated code (or your other code) in a closure, as shown:
var CompiledModule = (function() {
.. GENERATED CODE ..
return Module;
})();
The likely cause is an undefined function — a function that was referred to, but not implemented or linked in. If you get undefined, look at the line number to see the function name.
Emscripten by default does not give fatal errors on undefined symbols, so you can get runtime errors like these (because in practice, for many codebases it is easiest to get them working without refactoring them to remove all undefined symbol calls). If you prefer compile-time notifications, run emcc with -s WARN_ON_UNDEFINED_SYMBOLS=1 or -s ERROR_ON_UNDEFINED_SYMBOLS=1.
Aside from just forgetting to link in a necessary object file, one possible cause for this error is inline functions in headers. If you have a header with inline int my_func() { .. } then Clang may not actually inline the function (since inline is just a hint), and also not generate code for it (since it’s in a header). The result is that the generated bitcode and JavaScript will not have that function implemented. One solution is to add static to the function declaration, which forces code to be generated in the object file: static inline int my_func() { .. }.
A possible cause is that building libcxx or libcxxabi failed. Go to system/lib/libcxx (or libcxxabi) and do emmake make to see the actual error. Or, clean the Emscripten cache (./emcc --clear-cache) and then compile your file with EMCC_DEBUG=1 in the environment. libcxx will then be built in /tmp/emscripten_temp/libcxx, and you can see configure*, make* files that are the output of configure and make, etc.
Another possible cause of this error is the lack of make, which is necessary to build these libraries. If you are on Windows, you need cmake.
Note
lli is not maintained, and has odd errors and crashes. We do include tools/nativize_llvm.py (which compiles bitcode to a native executable) but it will also hit the impure_ptr error.
The issue is that newlib uses impure_ptr code, while glibc uses something else. The result is that bitcode built with the Emscripten will not run locally unless your machine uses newlib (basically, only embedded systems).
The impure_ptr error only occurs during explicit use of stdout etc., so printf(..) will work, but fprintf(stdout, ..) will not. Usually it is simple to modify your code to avoid this problem.
You may need to increase the stack size for node.js.
On Linux and Mac OS X, you can just do NODE_JS = ['node', '--stack_size=8192'] in the Emscripten Compiler Configuration File (.emscripten). On Windows, you will also need --max-stack-size=8192, and also run editbin /stack:33554432 node.exe.
This is a limitation of the asm.js target in Clang. This code is not currently supported.
Building Fastcomp from source (and hence the SDK) can fail at 100% progress. This is due to out of memory in the linking stage, and is reported as an error: collect2: error: ld terminated with signal 9 [Killed].
The solution is to ensure the system has sufficient memory. On Ubuntu 14.04.1 LTS 64bit, you should use use at least 6Gb.
By default Emscripten uses doubles for all floating-point variables, that is, 64-bit floats even when C/C++ code contains 32-bit floats. This is simplest and most efficient to implement in JS as doubles are the only native numeric type. As a result, you may see rounding errors compared to native code using 32-bit floats, just because of the difference in precision between 32-bit and 64-bit floating-point values.
To check if this is the issue you are seeing, build with -s PRECISE_F32=1. This uses proper 32-bit floating-point values, at the cost of some extra code size overhead. This may be faster in some browsers, if they optimize Math.fround, but can be slower in others. See src/settings.js for more details on this option.