To use Emscripten and complete this tutorial, first get Emscripten!
Before continuing, it's a good idea to make sure the requirements work.
First, change directory to where Emscripten is located, the rest of this tutorial will assume that that is where you are running commands (or, add a path to another location if you prefer). Then try
clang tests/hello_world.cpp ./a.out
(Add the path to clang if it isn't installed systemwide.) That uses Clang and LLVM to compile a "hello world" app and run it. The second command there should print "hello, world!". Then, test Node.js with
which should also print out "hello, world!". (As before, add the path to node if it isn't installed systemwide.)
If you haven't run Emscripten before, run it now with
If you are on Windows, you will call
instead. If you are having trouble with python versions, you can also explicitly invoke
python2 is not defined in your system (
python2 allows python 2 and 3 to be installed together on one system, which is increasingly common; as an alternative to
python emcc, you can also add a symlink to
python2). In that case you should also update PYTHON in
~/.emscripten, see below about that file.
"emcc" is the "Emscripten compiler frontend", an easy way to use Emscripten basically as a drop-in replacement for a standard compiler like gcc.
The first time you run emcc (or any other of the Emscripten tools), it will create a settings file at
~ is your user's home directory) and exit. You should edit that file now, changing the directory locations of LLVM and Node to the right places in your setup (specifically, edit
NODE_JS). If those paths are not right, Emscripten will not find LLVM, Clang or Node.js and it will fail. Look at the comments in that file that explain what the settings are and which you need to change.
After setting those paths, run
./emcc again. It should do some sanity checks which test the specified paths in
~/.emscripten. If they don't all pass, you might have a typo somewhere. When everything is set up properly, running
./emcc should tell you
emcc: no input files (since we didn't specify any input files), and you can proceed to the next section in this tutorial.
You can now compile your first file! First, let's build the same "hello world" C++ file from before, but this time using Emscripten:
There should now be an
a.out.js file in the current directory. Run it with
and it should print "hello, world!" as expected.
-v, which will print out information that can help pinpoint the problem.
./emcc tests/hello_world_sdl.cpp -o hello.html
By specifying that the output is an HTML file, you have told Emscripten to generate a complete HTML page. In this case, the source code uses the SDL API to show a colored cube. Open the web page in your web browser to see it (it should work in any browser that supports the Canvas element.)
Your C/C++ code can access files using the normal libc API (stdio: fopen, etc.). Try this command:
./emcc tests/hello_world_file.cpp -o hello.html --preload-file tests/hello_world_file.txt
hello.html in a web browser and you will see the data from a file being written out. (Note: Chrome is unable to do
file:// XHRs, so for
hello.html to work in that browser you need a webserver, for example
python -m SimpleHTTPServer 8888 and then open
tests/hello_world_file.cpp to see the C++ source code, and
tests/hello_world_file.txt to see the data. The
--preload-file option will automatically preload the file before running the compiled code. This is useful because loading data from the network cannot be done synchronously in browsers outside Web Workers. Almost all C/C++ code is synchronous, so preloading is the simplest solution.
For an example of files with SDL, change to the
tests/ directory and run
../emcc hello_image_sdl.c --preload-file screenshot.jpg -o a.html
Then browse to a.html. Note that we must run the command from tests/, since file preloading will generate files in the virtual filesystem that correspond to the current filesystem as it is seen at compile time. When we run the command in tests/, then the file is accessible as
screenshot.jpg and not
tests/screenshot.jpg, and the former is what the code expects.
Emscripten will by default generate unoptimized code, just like gcc does. You can generate slightly-optimized code with
-O1, for example
./emcc -O1 tests/hello_world.cpp
The "hello world" code here doesn't really need to be optimized, so you won't see a difference in speed when running it. But, you can look at the generated code to see the differences:
-O1 applies several minor optimizations to the code (simple ones that don't increase compilation time), and removes some runtime assertions. For example,
printf will have been replaced by
puts in the generated code.
Further optimizations are done in
./emcc -O2 tests/hello_world.cpp
If you inspect the generated code now, you will see it looks very different.
Emscripten has an extensive test suite. You can run it with
This will take a long time, perhaps several hours - there are many many tests! You can run an individual test as follows:
python tests/runner.py test_hello_world
If you want to view the generated code from that individual test, do
EMCC_DEBUG=1 python tests/runner.py test_hello_world, and then you can look inside the temp directory (
TEMP_DIR is defined in
~/.emscripten - by default it is
/tmp). Note that you can use
EMCC_DEBUG with emcc in general, not just with the test runner - it tells emcc to save the internal code generation stages (much like
A test suite specific feature is
EM_SAVE_DIR=1 in the environment, which will save the temp dir the test runner users, in the same place as mentioned in the previous paragraph. This is useful if the test you are running creates some temp files.
Note that Node.js cannot run 100% of the tests in the suite; if you care about running them all, you should get the SpiderMonkey shell (a recent trunk version).
You can run the Emscripten benchmarks using
python tests/runner.py benchmark
This will compile a sequence of benchmarks and run them several times, reporting averaged statistics including a comparison to how fast the same code runs when compiled to a native executable.
-I/usr/include/something: Using system headers that way is dangerous when you are cross-compiling, since the headers are meant for your local system, not for the platform you are actually building for.
src/settings.js, they appear there with descriptions of what they do in comments. To modify a setting, use the
-s option to emcc, for example
emcc source.cpp -s TOTAL_STACK=10000000
After finishing this tutorial, here are some general tips for using Emscripten:
--pre-jsoption to emcc works, search for
--pre-jsin the test suite (
tests/, and usually the result will be in
src/settings.jswhich describes the compiler options, and
system/include/emscripten/emscripten.hwhich describes JS-specific C APIs that your C/C++ programs can use when compiled with emscripten.
Last edited by Jared Hirsch,