Skip to main content
← All Tools · 🛠️ WebAssembly & Edge Computing

Emscripten

Compile C and C++ code to WebAssembly and JavaScript — powers high-performance web apps including games, image editors, and scientific tools.

Free Fully free and open source (MIT/University of Illinois)
webassemblywasmccppcompilerperformanceweb

✅ Pros

  • + Mature toolchain — powers WebAssembly for Figma, AutoCAD, Unity WebGL
  • + Complete C/C++ standard library support in the browser
  • + Emscripten SDK (emsdk) manages compiler versions easily
  • + Automatic JavaScript glue code generation for browser APIs
  • + Supports pthreads (multi-threading) via SharedArrayBuffer

⚠️ Cons

  • − Large output size — C++ with STL can produce 1MB+ WASM files
  • − Debugging WASM is harder than JavaScript
  • − Build times are significantly longer than native compilation
  • − Browser restrictions (CORS, COOP/COEP headers) needed for shared memory

Overview

Emscripten is the original and most complete C/C++ to WebAssembly compiler. It translates LLVM bitcode (the output of Clang) into WebAssembly, generating JavaScript bindings that bridge native code to browser APIs.

Installation

git clone https://github.com/emscripten-core/emsdk.git
cd emsdk
./emsdk install latest
./emsdk activate latest
source ./emsdk_env.sh

Compiling C to WebAssembly

// hello.c
#include <stdio.h>
#include <emscripten.h>

EMSCRIPTEN_KEEPALIVE
int add(int a, int b) {
    return a + b;
}

EMSCRIPTEN_KEEPALIVE
double calculate_distance(double x1, double y1, double x2, double y2) {
    return sqrt(pow(x2-x1, 2) + pow(y2-y1, 2));
}
# Compile to WASM + JS glue
emcc hello.c -o hello.js \
  -s WASM=1 \
  -s EXPORTED_FUNCTIONS='["_add","_calculate_distance"]' \
  -s EXPORTED_RUNTIME_METHODS='["ccall","cwrap"]' \
  -O2

# Output: hello.wasm + hello.js

Using in JavaScript

<script src="hello.js"></script>
<script>
Module.onRuntimeInitialized = function() {
  // Using cwrap (returns a reusable function)
  const add = Module.cwrap('add', 'number', ['number', 'number']);
  console.log(add(5, 3));  // 8

  const distance = Module.cwrap('calculate_distance', 'number',
    ['number', 'number', 'number', 'number']);
  console.log(distance(0, 0, 3, 4));  // 5
};
</script>

When to Use

Emscripten is ideal for porting existing C/C++ codebases to the web: game engines (Unity, Godot), scientific computing libraries (OpenCV, FFTW), multimedia codecs (FFmpeg), and physics engines (Bullet). For new WASM projects, consider wasm-pack (Rust) for better ergonomics.

Quick Start

Port a C library to the browser in four steps:

# 1. Install the Emscripten SDK
git clone https://github.com/emscripten-core/emsdk.git
cd emsdk && ./emsdk install latest && ./emsdk activate latest
source ./emsdk_env.sh   # adds emcc/em++ to PATH

# 2. Write your C function
cat > image_utils.c << 'EOF'
#include <stdlib.h>
#include <emscripten.h>

EMSCRIPTEN_KEEPALIVE
void grayscale(unsigned char* pixels, int width, int height) {
    for (int i = 0; i < width * height * 4; i += 4) {
        unsigned char avg = (pixels[i] + pixels[i+1] + pixels[i+2]) / 3;
        pixels[i] = pixels[i+1] = pixels[i+2] = avg;
    }
}
EOF

# 3. Compile to WASM
emcc image_utils.c -o image_utils.js \
  -s WASM=1 \
  -s EXPORTED_FUNCTIONS='["_grayscale","_malloc","_free"]' \
  -s EXPORTED_RUNTIME_METHODS='["ccall","cwrap","HEAPU8"]' \
  -O2

# 4. Use in the browser
<script src="image_utils.js"></script>
<script>
Module.onRuntimeInitialized = function() {
  const canvas = document.getElementById('canvas');
  const ctx = canvas.getContext('2d');
  const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);

  // Copy pixel data into WASM memory
  const nBytes = imageData.data.length;
  const ptr = Module._malloc(nBytes);
  Module.HEAPU8.set(imageData.data, ptr);

  // Run C function on the pixel buffer
  Module._grayscale(ptr, canvas.width, canvas.height);

  // Copy result back and render
  imageData.data.set(Module.HEAPU8.subarray(ptr, ptr + nBytes));
  Module._free(ptr);
  ctx.putImageData(imageData, 0, 0);
};
</script>

For production builds, add -s ENVIRONMENT=web to strip Node.js support from the glue code, reducing output size. Use --closure 1 for additional minification if your build pipeline supports it. Output .wasm files should be served with Content-Type: application/wasm for browsers to stream-compile them efficiently.