
Wasm Emscripten
- 318 installs
- 155 repo stars
- Updated June 27, 2026
- mohitmishra786/low-level-dev-skills
Port existing C/C++ libraries or compute-heavy modules to WebAssembly via Emscripten for browser clients, plugins, or interactive web experiences.
About
Documents compiling C/C++ to WebAssembly with Emscripten, including emcc configuration, JavaScript interop, runtime embedding, and shipping optimized .wasm plus glue code for browser-based SaaS, extensions, and games.
- Emscripten toolchain and emcc build flags
- JS/WASM binding and embind patterns
- Filesystem and async runtime considerations
- Asset packaging for browser delivery
- Debugging WASM builds and size optimization
Wasm Emscripten by the numbers
- 318 all-time installs (skills.sh)
- +24 installs in the week ending Aug 4, 2026 (Skillselion tracking)
- Ranked #734 of 2,245 Frontend Development skills by installs in the Skillselion catalog
- Data as of Aug 4, 2026 (Skillselion catalog sync)
npx skills add https://github.com/mohitmishra786/low-level-dev-skills --skill wasm-emscriptenAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 318 |
|---|---|
| repo stars | ★ 155 |
| Last updated | June 27, 2026 |
| Repository | mohitmishra786/low-level-dev-skills ↗ |
What it does
Port existing C/C++ libraries or compute-heavy modules to WebAssembly via Emscripten for browser clients, plugins, or interactive web experiences.
Files
WebAssembly with Emscripten
Purpose
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.
Triggers
- "How do I compile C to WebAssembly with Emscripten?"
- "How do I export a C function to JavaScript?"
- "How does WebAssembly memory work with Emscripten?"
- "How do I debug a .wasm file?"
- "How do I use Asyncify to make synchronous C code async?"
- "What's the difference between WASI and Emscripten browser target?"
Workflow
1. Setup and first build
# 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.html2. Exporting functions to JavaScript
// 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)); // 73. Memory model
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);4. Asyncify — synchronous C in async environments
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 optimization5. Optimization and wasm-opt
# 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.wasm6. Debugging 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 -1007. WASI vs browser target
| 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.wasmFor Emscripten linker flags reference, see references/emscripten-linker-flags.md.
Related skills
- Use
skills/runtimes/wasm-wasmtimefor server-side WASM with wasmtime CLI and Rust embedding - Use
skills/compilers/clangfor Clang-based WASM compilation with WASI SDK - Use
skills/binaries/elf-inspectionfor inspecting WASM binary structure
Emscripten Linker Flags Reference
Source: https://emscripten.org/docs/tools_reference/settings_reference.html
Key Settings
| Flag | Type | Default | Description |
|---|---|---|---|
EXPORTED_FUNCTIONS | list | ["_main"] | C functions exported to JS (needs leading _) |
EXPORTED_RUNTIME_METHODS | list | [] | Runtime helpers: ccall, cwrap, allocate, etc. |
MODULARIZE | bool | 0 | Wrap output in a function returning a Promise |
EXPORT_NAME | string | "Module" | Name of the exported JS module |
INITIAL_MEMORY | int | 16MB | Initial WASM heap size |
MAXIMUM_MEMORY | int | 2GB | Max WASM heap with ALLOW_MEMORY_GROWTH |
ALLOW_MEMORY_GROWTH | bool | 0 | Allow heap to grow dynamically |
STACK_SIZE | int | 64KB | C stack size |
ASSERTIONS | 0/1/2 | 1 in debug | Runtime safety assertions |
SAFE_HEAP | bool | 0 | Check all heap accesses for alignment |
ASYNCIFY | bool | 0 | Enable Asyncify for async C |
USE_PTHREADS | bool | 0 | Enable pthreads (needs SharedArrayBuffer) |
SHARED_MEMORY | bool | 0 | Enable shared memory |
SINGLE_FILE | bool | 0 | Embed WASM in JS as base64 |
ENVIRONMENT | string | "web,webview,worker,node" | Target environments |
FILESYSTEM | bool | 1 | Include virtual filesystem |
EXIT_RUNTIME | bool | 0 | Call exit() when main() returns |
INVOKE_RUN | bool | 1 | Run main() automatically |
NO_EXIT_RUNTIME | bool | 1 | Don't tear down runtime on exit |
Common Configurations
Minimal library (no main, no filesystem)
emcc lib.c -o lib.js \
-s EXPORTED_FUNCTIONS='["_my_func"]' \
-s EXPORTED_RUNTIME_METHODS='["cwrap"]' \
-s FILESYSTEM=0 \
-s MODULARIZE=1 \
-s EXPORT_NAME=MyLib \
-s ENVIRONMENT=web \
-OsNode.js WASM module
emcc prog.c -o prog.js \
-s ENVIRONMENT=node \
-s MODULARIZE=1 \
-s EXPORT_NAME=MyModule \
-O2
# Use: const M = await require('./prog.js')()Threaded application
emcc prog.c -o prog.js \
-s USE_PTHREADS=1 \
-s PTHREAD_POOL_SIZE=4 \
-s INITIAL_MEMORY=64MB \
-O2
# Requires: Cross-Origin-Opener-Policy: same-origin
# Cross-Origin-Embedder-Policy: require-corpMinimal WASI build
emcc prog.c -o prog.wasm \
--target=wasi \
-s ENVIRONMENT=node \
-Os