Step 1: Installing Emscripten

I found this to be quite easy and not very long. Go here: https://kripken.github.io/emscripten-site/docs/getting_started/downloads.html.

Use the web installer.

Wait a while and you’re done.

Step 2: Write some C code

I used the TutorialZine code as a basis for my testing and added a bit:

As you can see, there’s a regular C main() function on line 8.

There are three additional functions:

roll_dice: This is from the TutorialZine article. It generates a random number from 1 to 6. The JS UI calls into this when the user clicks on a CSS-rendered die and re-draws itself based on the result.

Note how there’s a token, EMSCRIPTEN_KEEPALIVE. I’m not entirely sure how this works but … it basically keeps the function around post-load. If you don’t decorate your function with that token, you can’t call it from JavaScript.

I have a function, sayHi(). This one does a couple of things:

At line 19: emscripten_run_script(“console.log(‘hi3!’)”); That emscript_run_script executes that associated JavaScript. At line 20, it invokes the function, _sayXyzzy(). _sayXyzzy is actually another function in the C program that just logs out to the console, “xyzzy” (again, via emscripten_run_script — see line 24 in the code).

It’s not doing much, but it’s good enough for this baby-steps POC.

Compile the C code

I started off by doing this via the Emscripten command line (part of the web installer) and it’s pretty simple:

You can always tell those developers who come from the ee cummings branch of CompSci.

The first time I ran the command, it did a bunch of initialization and there were some scary warnings. However, it all worked out. I think it’s actually compiling a bunch of standard C libraries, possibly downloading them first. Subsequent runs look like the screenshot above.

You should read the full Emscripten docs for a detailed run-down on the options. That command is compiling a single C file, dice-roll.c. It generates a WASM file (WASM=1). It does very little optimization (-O1 — note, it’s O for Optimize, no 0 for Zero). It also generates that glue JS file and names it “index.js”.

I switched over to using VS Code soon after that. VS Code helpfully told me there was a plugin for viewing C/C++ files and I installed that. I then created a task for the above. I ended up creating a .bat file with the actual command. My tasks.json looks like this:

Making It Work in the Browser

It’s pretty simple to get this to work in Chrome. Just create a barebones .html file (like index.html) and include that index.js file the emcc command generated in the prior step.

That index.js file is … something :). In my env, it’s 6,111 lines long :). It’s not *super* well documented, but was enough to help me figure out. This is a good place to start: https://kripken.github.io/emscripten-site/docs/api_reference/module.html#module.

Among 1,000 or so other things, it loads the Wasm file and creates a globally available object, Module. In fact, it supplements Module if you already created it. I think you’d always want to do that so that you can influence the initialization life cycle.

This index.js plus Module object allowed me to do several important things:

Load the Wasm in the first place

Add a function to the Module’s “postRun” array. The index.js initialization code runs these postRun functions once the Wasm is loaded and initialized.

Provides a useful wrapper that lets me invoke the C code functions.

Here’s my HTML:

A lot of this is straight from that TutorialZine article. I’ve modified by adding a reference to “prerun.js”.

Emscripten created that index.js file (line 18) and that’s the 6100 line glue monster.

The rest of this is from TutorialZine. It references some CSS that does a good job showing a die. It adds an event listener on click, dispatching an anonymous function that call out to the C code at line 31:

var result = Module._roll_dice();

If you recall, the actual C function’s name is roll_dice (no leading underscore). The Emscripten index.js file populates the Module object with all of your C functions (remembering they have to be decorated with that EMSCRIPTEN_KEEPALIVE tag). These are, in essence, proxy JavaScript functions. Emscripten inserts an underscore in front of the C function’s name. If you have function, “someAwesomeFunction” in your C code, the JS Module object will have a function, _someAwesomeFunction() for you to invoke when you want.

Line 21 actually throws a runtime error. That _sayHi() JS function, which is a proxy to the sayHi() function in C doesn’t exist yet. The index.js file does its work asynchronously. It is eventually available and by the time I get around to clicking on the die, it does. Line 33 never fails but line 21 always fails.

I obviously want to know when the C code is ready to use. This is where the “prerun.js” comes into play.

Here’s the prerun.js that gets loaded from line 17:

A line 1, I define an empty Module object.

I then define a couple of functions (sayAbc(), sayXyz(), sayInit()).

When the index.js glue code executes, it will supplement the Module object if it already exists or create a new one. In this case, I’ve already created it, so it supplements it.

In fact, it does more than supplement it. I can influence the Module life cycle by providing some functions for it to execute at certain times, like preRun[], onInit[], postRun[] and maybe more. The online docs led the way here and postRun[] was where I was able to reliable start running my C code.

You can see that I have a function, notifyReady() at line 18. It just creates an Event and publishes it onto the window object.

At line 25, I have an event listener. When it picks up the event, it logs out a happy message and then invokes Module._sayHi().

I added two functions to the postRun[] array on line 23.

Summarizing

When you spell it all out in detail like this, it might feel a bit overwhelming, but it’s actually not that complicated.

Install Emscripten. Write some C code. Have some kind of build process to emcc your C code. There are a million ways to do this, of course. Use the generated JS “glue” code to load and process your Wasm. Take advantage of the lifecycle array hooks to safely execute your C functions.

Once I got the basic tooling done, it was a piece of cake. Edit my C file, press control-shift-B. Wait for that and then press F5 on the browser.

(Quick note on the browser — you need to use a real web server since the Wasm files are fetch()’d / XHR’d. Chrome won’t let you can’t fetch a file from the file system via the file:// protocol, so you need a real web server to do this. I use Fenix for these kinds of things — it’s ridiculously easy to work with.).

And that’s it! It feels like a pretty viable thing to try at this point. I am not entirely sure where to go next. I think I might try implementing a fancy sorting algorithm and do some compare/contrast with that and plain JS.

<end/>

<postscript>

I recently published a book on TypeScript! It’s free and you can access it here: https://www.gitbook.com/book/pagalvin/yet-another-typescript-book/details

</endForReal>