skills/runtimes/wasm-emscripten/SKILL.md
WebAssembly with Emscripten skill for C/C++ to WASM compilation. Use when compiling C/C++ to WebAssembly with emcc, configuring EXPORTED_FUNCTIONS, understanding the WASM memory model, using Asyncify for async C code, debugging .wasm with browser devtools or wasm-opt, or targeting WASI vs browser environments. Activates on queries about Emscripten, emcc, WebAssembly from C/C++, WASM memory model, Asyncify, EXPORTED_FUNCTIONS, WASI, or wasm-opt.
npx skillsauth add mohitmishra786/low-level-dev-skills wasm-emscriptenInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Guide agents through compiling C/C++ to WebAssembly using Emscripten: emcc flag selection, function exports, memory model configuration, Asyncify for asynchronous C code, debugging WASM binaries, and targeting WASI vs browser.
# Install Emscripten SDK
git clone https://github.com/emscripten-core/emsdk.git
cd emsdk
./emsdk install latest
./emsdk activate latest
source ./emsdk_env.sh # add emcc to PATH
# Verify
emcc --version
# Compile C to WASM (browser target)
emcc hello.c -o hello.html # generates hello.html + hello.js + hello.wasm
emcc hello.c -o hello.js # just JS + WASM (no HTML shell)
# Serve locally (WASM requires HTTP, not file://)
python3 -m http.server 8080
# Open: http://localhost:8080/hello.html
// math.c
#include <emscripten.h>
// EMSCRIPTEN_KEEPALIVE prevents dead-code elimination
EMSCRIPTEN_KEEPALIVE
int add(int a, int b) {
return a + b;
}
EMSCRIPTEN_KEEPALIVE
double sqrt_approx(double x) {
return x * 0.5 + 1.0;
}
# Export specific functions
emcc math.c -o math.js \
-s EXPORTED_FUNCTIONS='["_add","_sqrt_approx"]' \
-s EXPORTED_RUNTIME_METHODS='["ccall","cwrap"]' \
-s MODULARIZE=1 \
-s EXPORT_NAME=MathModule
# The leading underscore is required for C functions
// Using exported functions in JS
const Module = await MathModule();
// Direct call
const result = Module._add(3, 4);
// Via ccall (type-safe)
const result2 = Module.ccall('add', 'number', ['number', 'number'], [3, 4]);
// Via cwrap (creates a callable JS function)
const add = Module.cwrap('add', 'number', ['number', 'number']);
console.log(add(3, 4)); // 7
Emscripten provides a linear memory heap accessible from both C and JS:
# Configure initial and maximum heap
emcc prog.c -o prog.js \
-s INITIAL_MEMORY=16MB \
-s MAXIMUM_MEMORY=256MB \
-s ALLOW_MEMORY_GROWTH=1 # allow dynamic growth
# Stack size (default 64KB)
emcc prog.c -o prog.js -s STACK_SIZE=1MB
# Shared memory (for SharedArrayBuffer / threads)
emcc prog.c -o prog.js -s SHARED_MEMORY=1 -s USE_PTHREADS=1
// Accessing C memory from JS
const ptr = Module._malloc(1024); // allocate
Module.HEAPU8.set([1, 2, 3], ptr); // write bytes
Module._free(ptr); // free
// Read a C string
const strPtr = Module.ccall('get_message', 'number', [], []);
const str = Module.UTF8ToString(strPtr);
// Write a string to C
const jsStr = "hello";
const cStr = Module.stringToNewUTF8(jsStr); // malloc + copy
Module._process_string(cStr);
Module._free(cStr);
Asyncify lets synchronous C code suspend and resume for async operations (like fetch(), sleep, etc.):
// async.c
#include <emscripten.h>
// Synchronous sleep in C (blocks C, but yields to JS event loop)
EM_JS(void, do_fetch, (const char *url), {
// Emscripten generates wrappers to suspend C while JS runs
Asyncify.handleAsync(async () => {
const resp = await fetch(UTF8ToString(url));
const text = await resp.text();
console.log(text);
});
});
void process_url(const char *url) {
do_fetch(url); // looks synchronous in C
printf("fetch complete\n");
}
# Enable Asyncify
emcc async.c -o async.js \
-s ASYNCIFY \
-s ASYNCIFY_STACK_SIZE=16384 \
-O2 # Asyncify works better with optimization
# Optimization levels
emcc prog.c -O0 -o prog.js # no optimization (fastest build)
emcc prog.c -O2 -o prog.js # balanced
emcc prog.c -O3 -o prog.js # aggressive
emcc prog.c -Os -o prog.js # optimize for size
emcc prog.c -Oz -o prog.js # aggressive size (Emscripten's smallest)
# Post-process with wasm-opt (Binaryen)
wasm-opt -Oz -o prog.opt.wasm prog.wasm # optimize for size
wasm-opt -O4 -o prog.opt.wasm prog.wasm # optimize for speed
# Compare sizes
ls -lh prog.wasm prog.opt.wasm
# Build with debug info
emcc prog.c -g -O0 -o prog.html \
-s ASSERTIONS=1 \
-s SAFE_HEAP=1 # catch misaligned accesses
# In Chrome DevTools:
# Sources → prog.wasm → line-by-line C source debugging
# (requires -g and browser with WASM debugging support)
# LLDB with WASM (wasmtime)
# See skills/runtimes/wasm-wasmtime for CLI WASM debugging
# Emscripten debug helpers
emcc prog.c -o prog.js \
-s ASSERTIONS=2 # extensive runtime checks
-s SAFE_HEAP=1 # sanitize heap accesses
-s STACK_OVERFLOW_CHECK=1
# Print generated JS
emcc prog.c -o prog.js && cat prog.js | head -100
| Feature | Browser (Emscripten) | WASI | |---------|---------------------|------| | Host APIs | Web APIs (fetch, WebGL, etc.) | POSIX subset (files, stdin/stdout) | | Runtime | Browser JS engine | wasmtime, wasmer, WAMR, Node.js | | Threads | SharedArrayBuffer + pthreads | wasi-threads (limited) | | Networking | fetch(), WebSocket | wasi-http (preview2) | | Use case | Web applications | Server-side, CLI tools, edge |
# Build for WASI (no browser JS, pure WASM)
emcc prog.c -o prog.wasm --target=wasi
# Or use wasi-sdk (better WASI support than Emscripten)
/opt/wasi-sdk/bin/clang --sysroot=/opt/wasi-sdk/share/wasi-sysroot \
prog.c -o prog.wasm
wasmtime prog.wasm
For Emscripten linker flags reference, see references/emscripten-linker-flags.md.
skills/runtimes/wasm-wasmtime for server-side WASM with wasmtime CLI and Rust embeddingskills/compilers/clang for Clang-based WASM compilation with WASI SDKskills/binaries/elf-inspection for inspecting WASM binary structuredevelopment
Zig testing skill for writing and running tests. Use when using zig build test, writing comptime tests, using test filters, working with test allocators to detect leaks, or using Zig's built-in fuzz testing (0.14+). Activates on queries about Zig tests, zig test, zig build test, comptime testing, test allocators, Zig fuzz testing, or detecting memory leaks in Zig tests.
development
Zig debugging skill. Use when debugging Zig programs with GDB or LLDB, interpreting Zig runtime panics, using std.debug.print for tracing, configuring debug builds, or debugging Zig programs in VS Code. Activates on queries about debugging Zig, Zig panics, zig gdb, zig lldb, std.debug.print, Zig stack traces, or Zig error return traces.
tools
Zig cross-compilation skill. Use when cross-compiling Zig programs to different targets, using Zig's built-in cross-compilation for embedded, WASM, Windows, ARM, or using zig cc to cross-compile C code without a system cross-toolchain. Activates on queries about Zig cross-compilation, zig target triples, zig cc cross-compile, Zig embedded targets, or Zig WASM.
development
Zig comptime skill for compile-time evaluation and metaprogramming. Use when using comptime parameters, comptime types, generics via anytype, comptime reflection with @typeInfo, or metaprogramming patterns that replace C++ templates. Activates on queries about Zig comptime, compile-time evaluation, Zig generics, anytype, @typeInfo, comptime types, or Zig metaprogramming.