C → WebAssembly
in the browser
Write functions in C, compile them to WASM, and race them against JavaScript. Same algorithms, same browser — see where compiled C pulls ahead and where V8's JIT keeps up.
From C source to browser execution
C code compiled to a .wasm binary — a compact, pre-compiled format the browser executes at near-native speed.
# Minimal — produces just math.wasm
docker run --rm -v $(pwd):/src emscripten/emsdk \
emcc math.c -o math.wasm --no-entry \
-s EXPORTED_FUNCTIONS='["_fibonacci","_hash_djb2",\
"_count_primes"]' -O2# Full — produces math_full.js + math_full.wasm
docker run --rm -v $(pwd):/src emscripten/emsdk \
emcc math.c -o math_full.js \
-s EXPORTED_FUNCTIONS='["_fibonacci","_hash_djb2",\
"_count_primes"]' \
-s EXPORTED_RUNTIME_METHODS='["ccall"]' \
-s MODULARIZE=1 -O2Two ways to load WASM
The raw browser API is 3 lines. Emscripten adds a JS loader for convenience. Both run the same compiled binary — try them both.
// Raw browser API — no libraries, no glue code
const response = await fetch('/wasm/math.wasm');
const bytes = await response.arrayBuffer();
const { instance } = await WebAssembly.instantiate(bytes);
// Call C functions directly — just numbers in, numbers out
instance.exports.fibonacci(38);
instance.exports.hash_djb2(123456);
instance.exports.count_primes(100000);// Emscripten glue — convenience for complex programs
const Module = await loadEmscriptenModule('/wasm/math_full.js');
// ccall: function name, return type, arg types, args
Module.ccall('fibonacci', 'number', ['number'], [38]);
Module.ccall('hash_djb2', 'number', ['number'], [123456]);
Module.ccall('count_primes', 'number', ['number'], [100000]);Minimal for pure number crunching — smaller, no dependencies. Emscripten when your C code needs strings, arrays, file I/O, or malloc — the glue emulates all the system interfaces C expects.
WASM vs JavaScript — same algorithm, same browser
Three algorithms implemented identically in C and JavaScript. Fibonacci tests simple recursion. Hashing tests bitwise ops in tight loops. Prime counting tests sustained nested integer computation — all inside a single WASM call.
The C source
Pure computation. No #include, no main(), no I/O. Just functions that take numbers and return numbers.
// Pure computation — no I/O, no malloc, just numbers.
int fibonacci(int n) {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
}
unsigned int hash_djb2(unsigned int input) {
unsigned int hash = 5381;
while (input > 0) {
unsigned int digit = input % 10;
hash = ((hash << 5) + hash) + digit;
input /= 10;
}
return hash;
}
// Nested loops, all computation inside WASM
int count_primes(int max_n) {
int count = 0;
for (int n = 2; n <= max_n; n++) {
int is_prime = 1;
for (int i = 2; i * i <= n; i++) {
if (n % i == 0) { is_prime = 0; break; }
}
count += is_prime;
}
return count;
}