Webassembly: calling C functions from Javascript with emscripten

Elia Maino
Jul 27, 2017 · 3 min read

I started to look into webassembly with the goal of implementing a version of Conway’s Game of Life which was able to use an engine written in C to calculate the next state of the game.

In this short article I want to illustrate the steps I made to compile a simple C function into a webassembly module callable by Javascript.

To compile the C code I used emscripten, an open source LLVM (Low Level Virtual Machine) to Javascript compiler.

Installing emscripten

Installing the emscripten toolchain is pretty straightforward, the official documentation is available at https://kripken.github.io/emscripten-site/docs/getting_started/downloads.html. The installation process could take quite a lot of time anyway (depending on your network and machine speed), since the project is quite big.

Note: I use macOS therefore the syntax used in the article may be slightly different on Windows or Linux :)

Once emscripten is installed you have to set the system path to the active version of it (from the emsdk directory):

source ./emsdk_env.sh

To test if emscripten is correctly configured just run:

emcc -v

Writing and compiling a C function

The C function that I am going to compile implements the logic to define if a cell of the grid will be dead or alive in the next iteration of the game.

Let’s create a file called getCellStatus.c and paste this code in it.

The logic of getCellStatus is pretty simple. It accepts two integer parameters: the status and the number of neighbours, and returns the new status of the cell as an integer.

Now run:

emcc getCellStatus.c -o getCellStatus.js -s EXPORTED_FUNCTIONS=”[‘_getCellStatus’]”

This command will generate a javascript file (getCellStatus.js) wrapping our C code in a module.

For the moment we need to manually export the function that we want to call from Javascript by adding the EXPORTED_FUNCTIONS options to the command.

Now there are two ways to call a C compiled function from Javascript:

  • Wrapping the function using cwrap() (this returns Javascript function callable multiple times)
  • Directly called the function using ccall() (this returns directly the return value of the C function)

We can now call getCellStatus by adding some code to the end of our Javascript module.

cwrap accepts three parameters and returns a Javascript function:

  • The name of the function to be wrapped
  • The return type of the function
  • An array with the types of the parameters of the function (which can be omitted if the function has no parameter)

In our case a call to cwrap would be as follows:

const getCellStatus = Module.cwrap('getCellStatus ', 'number', ['number', 'number']);console.log(getCellStatus(1,3));
console.log(getCellStatus(0,3));
console.log(getCellStatus(0,2));
console.log(getCellStatus(1,1));
// Prints 1 1 0 0

ccall is similar to cwrap. It returns the value returned by the C function and requires an additional parameter defining the actual arguments to pass to the function call.

const getCellStatus = Module.ccall('getCellStatus ', 'number', ['number', 'number'], [1, 3]);// Prints 1

When we compiled our C file to Javascript we had to manually declare in the command which functions we wanted to export.

Emscripten provide us with an useful annotation to avoid this: EMSCRIPTEN_KEEPALIVE. So let’s rewrite our C file:

Compile the function (no need to declare exported functions now)

emcc getCellStatus.c -o getCellStatus.js

Then we can just create an index.js file to test our function

And run it with

node index_getCellStatus.js// Prints 0 1

Conclusion

As you can see using emscripten to generate a Javascript module from a C file and to call compiled C function with it is pretty easy.

To get further details about how you can use emscripten to interact with C functions read the official documentation at https://kripken.github.io/emscripten-site/docs/porting/connecting_cpp_and_javascript/Interacting-with-code.html.

Anyway this is a really basic usage of this tool, more advanced access to the memory or to wasm files requires a little more effort. I will try to explain how to do that in the second part of this article.

Thanks for reading :)

Elia Maino

Written by

Software engineer at dailymotion.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade