The TL;DR: I’ve been interested in Web Assembly for a while and finally sat down to do a little proof of concept to see how far I could get. I’m happy and encouraged with the result.
You might find this useful for your own research.
If you just want to get to the technical bits, skip this section.
I’m increasingly excited about the potential for writing web apps in languages other than JavaScript. This is already happening, of course. I haven’t written anything of consequence in plain JS since … I’m not sure. I distinctly remember writing an app using plain JS back in 2013(!) and that’s it.
Since then, I’ve moved on to working with TypeScript. I enjoy TS a lot (I recently published a free book on it), but at the end of the day, it’s mostly just JavaScript and it doesn’t do quite enough to ameliorate my SmallTalk envy :). I’ve played around a bit with Closure but I got sidetracked from that with this *hilarious* article about systems programmers. You should really just got read it now.
Seriously. Just go read it.
I’m a self-taught C programmer who learned from the first edition of Kernighan and Ritchie’s seminal book (first edition, no less). I bought and used Borland’s C++ compiler waaay back, like 20 years ago. I’m very far away from those roots these days :(. Don’t get me wrong — I love what I do, but there’s something really fun and thrilling about working so directly with memory and being close to the hardware like that.
So, that funny article got me thinking about those old fun days and I decided to see how viable it would be to write C/C++ code and deploy it as part of a web application.
I got started by looking up “web assembly tutorial” and came across this article: https://tutorialzine.com/2017/06/getting-started-with-web-assembly
This article largely worked but I think that some of the underlying infrastructure bits have changed. I had to make a few changes on the JavaScript side to get that article’s code to work. I then extended it a bit.
I did all of this work on a windows 10 laptop.
This is what happens at a high level:
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.
I used the TutorialZine code as a basis for my testing and added a bit:
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:
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:
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.
When you spell it all out in detail like this, it might feel a bit overwhelming, but it’s actually not that complicated.
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>