C and C++ with Emscripten
15.1 What Emscripten Provides
A complete toolchain (Clang/LLVM + Binaryen) plus runtime support: libc (musl-based), libc++, a virtual filesystem, SDL2/GLFW/OpenGL ES → WebGL, pthreads → Web Workers, networking emulation, and a JS glue file that loads and connects everything.
15.2 Hello World and Calling C from JS
// math.c
#include <emscripten/emscripten.h>
#include <stdio.h>
EMSCRIPTEN_KEEPALIVE
int fib(int n) { return n < 2 ? n : fib(n - 1) + fib(n - 2); }
EMSCRIPTEN_KEEPALIVE
double average(const double* values, int len) {
double sum = 0;
for (int i = 0; i < len; i++) sum += values[i];
return len ? sum / len : 0;
}
int main(void) {
printf("Hello from C!\n"); // goes to console.log
return 0;
}emcc math.c -O3 -o math.js \
-sEXPORTED_FUNCTIONS=_fib,_average,_malloc,_free \
-sEXPORTED_RUNTIME_METHODS=ccall,cwrap,HEAPF64 \
-sMODULARIZE -sEXPORT_ES6 -sENVIRONMENT=web
emcc math.c -o index.html # generates an HTML test pageimport createModule from "./math.js";
const Module = await createModule();
console.log(Module._fib(30));
const fib = Module.cwrap("fib", "number", ["number"]);
console.log(fib(20));
// Passing an array
const values = new Float64Array([1, 2, 3, 4]);
const ptr = Module._malloc(values.byteLength);
Module.HEAPF64.set(values, ptr / 8);
console.log(Module._average(ptr, values.length));
Module._free(ptr);
// Strings: Module.ccall("greet", "string", ["string"], ["Ana"])15.3 Embind (C++ Classes to JS)
Alternative: WebIDL Binder. TypeScript definitions: --emit-tsd.
// shapes.cpp
#include <emscripten/bind.h>
#include <string>
#include <vector>
using namespace emscripten;
class Counter {
public:
explicit Counter(int start) : value(start) {}
void increment() { ++value; }
int get() const { return value; }
std::string label() const { return "Counter=" + std::to_string(value); }
private:
int value;
};
double sum(const std::vector<double>& v) { double s = 0; for (double x : v) s += x; return s; }
EMSCRIPTEN_BINDINGS(my_module) {
class_<Counter>("Counter")
.constructor<int>()
.function("increment", &Counter::increment)
.function("get", &Counter::get)
.property("label", &Counter::label);
register_vector<double>("VectorDouble");
function("sum", &sum);
}em++ shapes.cpp -O2 -lembind -o shapes.js -sMODULARIZE -sEXPORT_ES6const M = await createModule();
const c = new M.Counter(5);
c.increment();
console.log(c.get(), c.label);
c.delete(); // ⚠️ free C++ objects manually (or use FinalizationRegistry carefully)15.4 Key Emscripten Features and Flags
Flag / feature -O0 … -O3, -Os, -Oz -sMODULARIZE -sEXPORT_ES6 Export a factory function / ES module
-sINITIAL_MEMORY=64MB, -sMAXIMUM_MEMORY Minimal JS dependency (WASI-like imports) -pthread -sPTHREAD_POOL_SIZE=4
Threads via Web Workers (needs cross-origin isolation) Native Wasm exception handling for C++ -sASYNCIFY, -sJSPI
Call async JS from synchronous C code (Module 28) --preload-file assets@/ / --embed-file Virtual filesystem contents
-sFORCE_FILESYSTEM, -lidbfs.js, WasmFS Filesystems (MEMFS, IDBFS persistence, NODEFS, OPFS via WasmFS)
-sUSE_SDL=2, -sUSE_WEBGL2=1, -sFULL_ES3 -sUSE_ZLIB=1, -sUSE_LIBPNG=1 (emports) -gsource-map, -g -sASSERTIONS, -fsanitize=address Runtime checks, ASan Minify the JS glue
emmake make, emcmake cmake, emconfigure Build existing projects
EM_JS, EM_ASM, emscripten_run_script Inline JS from C Browser-friendly main loop (requestAnimationFrame)
#include <emscripten.h>
EM_JS(void, show_alert, (const char* msg), {
alert(UTF8ToString(msg));
});15.5 Porting an Existing Library
Common issues: blocking main loops (use emscripten_set_main_loop or Asyncify), synchronous file/network I/O, threads without COOP/COEP headers, inline assembly/CPU intrinsics (replace with SIMD intrinsics via -msimd128 and wasm_simd128.h), and 64-bit assumptions.
emcmake cmake -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build
emcc build/libfoo.a glue.c -O3 -o foo.js -sMODULARIZE [email protected]