wasm-emscripten

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

WebAssembly with Emscripten

使用Emscripten开发WebAssembly

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.
指导开发者通过Emscripten将C/C++编译为WebAssembly:包括emcc参数选择、函数导出、内存模型配置、使用Asyncify处理异步C代码、调试WASM二进制文件,以及针对WASI与浏览器环境进行适配。

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?"
  • "如何使用Emscripten将C编译为WebAssembly?"
  • "如何将C函数导出到JavaScript?"
  • "WebAssembly内存在Emscripten中如何工作?"
  • "如何调试.wasm文件?"
  • "如何使用Asyncify将同步C代码转为异步?"
  • "WASI与Emscripten浏览器目标有什么区别?"

Workflow

工作流程

1. Setup and first build

1. 环境搭建与首次构建

bash
undefined
bash
undefined

Install Emscripten SDK

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
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

Verify

emcc --version
emcc --version

Compile C to WASM (browser target)

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)
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://)

Serve locally (WASM requires HTTP, not file://)

python3 -m http.server 8080
python3 -m http.server 8080
undefined
undefined

2. Exporting functions to JavaScript

2. 向JavaScript导出函数

c
// 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;
}
bash
undefined
c
// 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;
}
bash
undefined

Export specific functions

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
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

The leading underscore is required for C functions


```javascript
// 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

```javascript
// 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

3. Memory model

3. 内存模型

Emscripten provides a linear memory heap accessible from both C and JS:
bash
undefined
Emscripten提供了一块可同时被C和JS访问的线性内存堆:
bash
undefined

Configure initial and maximum heap

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
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)

Stack size (default 64KB)

emcc prog.c -o prog.js -s STACK_SIZE=1MB
emcc prog.c -o prog.js -s STACK_SIZE=1MB

Shared memory (for SharedArrayBuffer / threads)

Shared memory (for SharedArrayBuffer / threads)

emcc prog.c -o prog.js -s SHARED_MEMORY=1 -s USE_PTHREADS=1

```javascript
// 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);
emcc prog.c -o prog.js -s SHARED_MEMORY=1 -s USE_PTHREADS=1

```javascript
// 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

4. Asyncify — 异步环境中的同步C代码

Asyncify lets synchronous C code suspend and resume for async operations (like
fetch()
, sleep, etc.):
c
// 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");
}
bash
undefined
Asyncify允许同步C代码暂停和恢复,以处理异步操作(如
fetch()
、sleep等):
c
// 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");
}
bash
undefined

Enable Asyncify

Enable Asyncify

emcc async.c -o async.js
-s ASYNCIFY
-s ASYNCIFY_STACK_SIZE=16384
-O2 # Asyncify works better with optimization
undefined
emcc async.c -o async.js
-s ASYNCIFY
-s ASYNCIFY_STACK_SIZE=16384
-O2 # Asyncify works better with optimization
undefined

5. Optimization and wasm-opt

5. 优化与wasm-opt

bash
undefined
bash
undefined

Optimization levels

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)
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)

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
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

Compare sizes

ls -lh prog.wasm prog.opt.wasm
undefined
ls -lh prog.wasm prog.opt.wasm
undefined

6. Debugging WASM

6. 调试WASM

bash
undefined
bash
undefined

Build with debug info

Build with debug info

emcc prog.c -g -O0 -o prog.html
-s ASSERTIONS=1
-s SAFE_HEAP=1 # catch misaligned accesses
emcc prog.c -g -O0 -o prog.html
-s ASSERTIONS=1
-s SAFE_HEAP=1 # catch misaligned accesses

In Chrome DevTools:

In Chrome DevTools:

Sources → prog.wasm → line-by-line C source debugging

Sources → prog.wasm → line-by-line C source debugging

(requires -g and browser with WASM debugging support)

(requires -g and browser with WASM debugging support)

LLDB with WASM (wasmtime)

LLDB with WASM (wasmtime)

See skills/runtimes/wasm-wasmtime for CLI WASM debugging

See skills/runtimes/wasm-wasmtime for CLI WASM debugging


```bash

```bash

Emscripten debug helpers

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
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

Print generated JS

emcc prog.c -o prog.js && cat prog.js | head -100
undefined
emcc prog.c -o prog.js && cat prog.js | head -100
undefined

7. WASI vs browser target

7. WASI vs 浏览器目标

FeatureBrowser (Emscripten)WASI
Host APIsWeb APIs (fetch, WebGL, etc.)POSIX subset (files, stdin/stdout)
RuntimeBrowser JS enginewasmtime, wasmer, WAMR, Node.js
ThreadsSharedArrayBuffer + pthreadswasi-threads (limited)
Networkingfetch(), WebSocketwasi-http (preview2)
Use caseWeb applicationsServer-side, CLI tools, edge
bash
undefined
特性浏览器(Emscripten)WASI
宿主APIWeb APIs(fetch、WebGL等)POSIX子集(文件、标准输入/输出)
运行时浏览器JS引擎wasmtime、wasmer、WAMR、Node.js
线程SharedArrayBuffer + pthreadswasi-threads(有限支持)
网络fetch()、WebSocketwasi-http(预览版2)
适用场景Web应用服务端、CLI工具、边缘计算
bash
undefined

Build for WASI (no browser JS, pure WASM)

Build for WASI (no browser JS, pure WASM)

emcc prog.c -o prog.wasm --target=wasi
emcc prog.c -o prog.wasm --target=wasi

Or use wasi-sdk (better WASI support than Emscripten)

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](references/emscripten-linker-flags.md).
/opt/wasi-sdk/bin/clang --sysroot=/opt/wasi-sdk/share/wasi-sysroot
prog.c -o prog.wasm wasmtime prog.wasm

Emscripten链接器参数参考见 [references/emscripten-linker-flags.md](references/emscripten-linker-flags.md)。

Related skills

相关技能

  • Use
    skills/runtimes/wasm-wasmtime
    for server-side WASM with wasmtime CLI and Rust embedding
  • Use
    skills/compilers/clang
    for Clang-based WASM compilation with WASI SDK
  • Use
    skills/binaries/elf-inspection
    for inspecting WASM binary structure
  • 使用
    skills/runtimes/wasm-wasmtime
    实现基于wasmtime CLI和Rust嵌入的服务端WASM开发
  • 使用
    skills/compilers/clang
    通过WASI SDK实现基于Clang的WASM编译
  • 使用
    skills/binaries/elf-inspection
    检查WASM二进制结构