C and Emscripten
Emscripten is the classic toolchain for bringing C and C++ code to the web. It compiles to WebAssembly (or the older asm.js fallback), emulates POSIX APIs, and can generate a JavaScript loader that handles instantiation for you.
Install Emscripten
Section titled “Install Emscripten”# Clone the Emscripten SDKgit clone https://github.com/emscripten-core/emsdk.gitcd emsdk
# Install and activate the latest release./emsdk install latest./emsdk activate latest
# Add emcc to PATH (run this in every new shell, or add to your profile)source ./emsdk_env.sh
# Verifyemcc --versionWrite a C function
Section titled “Write a C function”Create math.c:
#include <math.h>
// Compute the hypotenuse: sqrt(a*a + b*b)double hypotenuse(double a, double b) { return sqrt(a * a + b * b);}
// Fibonacci (iterative)int fibonacci(int n) { if (n <= 1) return n; int a = 0, b = 1; for (int i = 2; i <= n; i++) { int tmp = a + b; a = b; b = tmp; } return b;}Compile with emcc
Section titled “Compile with emcc”emcc math.c \ -o math.js \ -sEXPORTED_FUNCTIONS='["_hypotenuse","_fibonacci"]' \ -sEXPORTED_RUNTIME_METHODS='["ccall","cwrap"]' \ -sASSERTIONS=1 \ -O2This produces two files:
math.wasm— the compiled binarymath.js— the JS loader and runtime glue
The EXPORTED_FUNCTIONS list uses a leading underscore because C symbol names are prefixed with _ by convention in Emscripten.
Call from JavaScript with ccall / cwrap
Section titled “Call from JavaScript with ccall / cwrap”// In a browser: load the Emscripten-generated JS loader first// <script src="math.js"></script>
Module.onRuntimeInitialized = function () { // ccall — one-shot call: (name, returnType, argTypes, args) const h = Module.ccall( 'hypotenuse', // C function name (without leading _) 'number', // return type ['number', 'number'], // argument types [3.0, 4.0] // argument values ); console.log('hypotenuse(3, 4) =', h); // 5
// cwrap — create a reusable JS function const fib = Module.cwrap('fibonacci', 'number', ['number']); console.log('fibonacci(10) =', fib(10)); // 55 console.log('fibonacci(20) =', fib(20)); // 6765};Output a standalone HTML page
Section titled “Output a standalone HTML page”Emscripten can generate a self-contained HTML shell that embeds the loader and a terminal-style output canvas:
emcc math.c \ -o math.html \ -sEXPORTED_FUNCTIONS='["_hypotenuse","_fibonacci"]' \ -sEXPORTED_RUNTIME_METHODS='["ccall","cwrap"]' \ -O2Open math.html in a browser and the Emscripten terminal canvas appears. Any C printf calls output there automatically.
Using malloc and strings
Section titled “Using malloc and strings”When passing strings across the Wasm boundary you must manually allocate and free memory:
#include <string.h>#include <stdlib.h>
// Returns a heap-allocated copy of the input in upper-casechar* to_upper(const char* s) { int len = strlen(s); char* out = malloc(len + 1); for (int i = 0; i <= len; i++) out[i] = (s[i] >= 'a' && s[i] <= 'z') ? s[i] - 32 : s[i]; return out;}emcc strings.c \ -o strings.js \ -sEXPORTED_FUNCTIONS='["_to_upper","_malloc","_free"]' \ -sEXPORTED_RUNTIME_METHODS='["ccall","cwrap","allocateUTF8","UTF8ToString"]' \ -O2Module.onRuntimeInitialized = function () { const ptr = Module.allocateUTF8('hello wasm'); const resultPtr = Module.ccall('to_upper', 'number', ['number'], [ptr]); console.log(Module.UTF8ToString(resultPtr)); // HELLO WASM Module._free(ptr); Module._free(resultPtr);};