What Is WebAssembly?
WebAssembly (Wasm) is a binary instruction format for a stack-based virtual machine, designed as a portable compilation target for high-level languages. Standardized by the W3C in 2019 (the first new language natively supported in browsers since JavaScript in 1995), WebAssembly runs at near-native speed in all major web browsers and increasingly in server-side and embedded environments.
WebAssembly is not a replacement for JavaScript — it is a complement. JavaScript remains the language of DOM manipulation, web APIs, and event-driven interaction. WebAssembly handles computationally intensive tasks: image processing, video encoding, 3D graphics, cryptography, scientific simulation, and running existing native codebases (C, C++, Rust) on the web without rewriting them.
The Core Idea: Portable Native Speed
The challenge WebAssembly solves: how do you run C++ code in a browser safely and fast?
Before WebAssembly, the only option was asm.js — a subset of JavaScript that JavaScript engines could optimize aggressively. asm.js proved the concept but had a fundamental limitation: it was still text-based JavaScript that the engine had to parse and compile from source on every load.
WebAssembly is binary-encoded and designed for fast streaming compilation. A browser can begin executing WebAssembly before the full download completes (streaming compilation). The compact binary format is typically 3× smaller than equivalent asm.js and 10-20× smaller than equivalent JavaScript for compute-heavy code.
WebAssembly File Formats
WebAssembly exists in two representations:
1. Binary Format (.wasm)
The binary format is what browsers execute. It is compact, fast to parse, and not human-readable. A .wasm file begins with a magic number (\0asm, hex 00 61 73 6D) followed by a version number (01 00 00 00).
# Check a .wasm file
file app.wasm # WebAssembly (wasm) binary module
xxd app.wasm | head -2 # 0000: 0061 736d 0100 0000 ...
wc -c app.wasm # size in bytes (typically much smaller than equivalent JS)
2. Text Format (.wat)
The WebAssembly Text format is the human-readable equivalent, using S-expressions (parenthesized syntax similar to Lisp). It is primarily used for debugging, learning, and hand-authoring small modules.
;; factorial.wat — computes n!
(module
(func $factorial (export "factorial") (param $n i32) (result i64)
(if (result i64)
(i32.le_s (local.get $n) (i32.const 1))
(then (i64.const 1))
(else
(i64.mul
(i64.extend_i32_s (local.get $n))
(call $factorial
(i32.sub (local.get $n) (i32.const 1))
)
)
)
)
)
)
Converting between binary and text:
# Binary (.wasm) → Text (.wat)
wasm2wat app.wasm -o app.wat
# Text (.wat) → Binary (.wasm)
wat2wasm factorial.wat -o factorial.wasm
WebAssembly Type System
WebAssembly has four core value types in its initial MVP:
| Type | Description | Example |
|---|---|---|
i32 |
32-bit integer (signed or unsigned, interpreted by instruction) | Loop counter, boolean |
i64 |
64-bit integer | Large integers, pointers |
f32 |
32-bit IEEE 754 float | Vertex coordinates |
f64 |
64-bit IEEE 754 double | Scientific calculations |
The WebAssembly SIMD extension adds 128-bit vector types (v128) for parallel data processing — essential for image processing and ML inference.
Reference types (Wasm 2.0) allow WebAssembly to hold references to JavaScript objects natively, dramatically improving JS↔Wasm interoperability.
Compiling to WebAssembly
From C/C++ with Emscripten
Emscripten is the primary toolchain for compiling C/C++ to WebAssembly. It handles the entire compilation pipeline: C/C++ → LLVM IR → WebAssembly, plus generates JavaScript glue code for browser integration.
# Install Emscripten
git clone https://github.com/emscripten-core/emsdk.git
cd emsdk && ./emsdk install latest && ./emsdk activate latest
source ./emsdk_env.sh
# Compile a simple C program to WebAssembly
cat > hello.c << 'EOF'
#include <stdio.h>
int add(int a, int b) { return a + b; }
int main() { printf("Hello from WebAssembly!\n"); return 0; }
EOF
# Basic compilation (generates hello.js + hello.wasm)
emcc hello.c -o hello.js
# Optimized build with exported functions
emcc hello.c -O3 \
-s EXPORTED_FUNCTIONS='["_add","_main"]' \
-s EXPORTED_RUNTIME_METHODS='["ccall","cwrap"]' \
-o hello.js
From Rust with wasm-pack
Rust + wasm-pack is the most ergonomic path to production-grade WebAssembly. The wasm-bindgen crate handles JS↔Wasm bindings automatically.
# Install wasm-pack
curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
# Create a new Rust + Wasm project
cargo new --lib my_wasm_lib
cd my_wasm_lib
# Cargo.toml
[lib]
crate-type = ["cdylib"]
[dependencies]
wasm-bindgen = "0.2"
// src/lib.rs
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn greet(name: &str) -> String {
format!("Hello, {}! From Rust/WebAssembly.", name)
}
#[wasm_bindgen]
pub fn fibonacci(n: u32) -> u64 {
match n {
0 => 0,
1 => 1,
_ => {
let mut a = 0u64;
let mut b = 1u64;
for _ in 2..=n {
let c = a + b;
a = b;
b = c;
}
b
}
}
}
# Build (generates pkg/ directory with .wasm + JS bindings)
wasm-pack build --target web
Using WebAssembly in the Browser
Vanilla JavaScript API
// Load and instantiate a .wasm module
async function loadWasm() {
// Streaming instantiation (fastest — starts compiling during download)
const { instance } = await WebAssembly.instantiateStreaming(
fetch('factorial.wasm'),
{} // imports object (empty for this module)
);
// Call exported functions
const result = instance.exports.factorial(10);
console.log(`10! = ${result}`); // 3628800
}
loadWasm();
Using a Rust/wasm-pack Module
<!DOCTYPE html>
<html>
<head>
<script type="module">
import init, { greet, fibonacci } from './pkg/my_wasm_lib.js';
async function run() {
await init(); // loads and instantiates the .wasm file
console.log(greet("World")); // "Hello, World! From Rust/WebAssembly."
console.log(fibonacci(40)); // 102334155 (fast!)
}
run();
</script>
</head>
<body><h1>WebAssembly Demo</h1></body>
</html>
Memory Model
WebAssembly uses a flat linear memory (a WebAssembly.Memory object — essentially a resizable ArrayBuffer). Data passing between JavaScript and WebAssembly happens through this shared memory:
const memory = new WebAssembly.Memory({ initial: 1 }); // 64 KB page
const { instance } = await WebAssembly.instantiateStreaming(fetch('app.wasm'), {
env: { memory }
});
// Write a string to Wasm memory
const bytes = new TextEncoder().encode("hello\0");
new Uint8Array(memory.buffer).set(bytes, 0); // write at offset 0
const resultPtr = instance.exports.process_string(0, bytes.length);
// Read result back from memory
WebAssembly Beyond the Browser
WebAssembly is increasingly used outside browsers:
WASI (WebAssembly System Interface) — a standardized API for accessing operating system resources (files, network, clocks) from WebAssembly. Allows Wasm modules to run on the server like portable, sandboxed native binaries.
# Run a .wasm file with Wasmtime (WASI runtime)
wasmtime hello.wasm
# Run with file system access granted
wasmtime --dir=./data process.wasm
# Docker + WebAssembly (experimental)
docker run --runtime=io.containerd.wasmedge.v1 myapp.wasm
Edge computing: Cloudflare Workers, Fastly Compute@Edge, and Vercel Edge Functions run WebAssembly at the network edge — ultra-low latency with a security sandbox stronger than containers.
Plugin systems: Applications like Envoy Proxy, eBPF alternatives, and game engines use WebAssembly as a safe plugin format where third-party code runs in an isolated sandbox.
Real-World WebAssembly Applications
| Application | What runs in Wasm | Technology |
|---|---|---|
| Figma | Vector rendering engine | C++ / Emscripten |
| Google Earth | 3D globe rendering | C++ / Emscripten |
| AutoCAD Web | CAD engine | C++ / Emscripten |
| Photoshop Web | Image processing codecs | C++ / Emscripten |
| Squoosh | Image compression (WebP, AVIF, etc.) | C++ / Rust |
| FFmpeg.wasm | Video/audio conversion in the browser | C / Emscripten |
| PyScript | Python interpreter | CPython / Emscripten |
| SQLite in the browser | Full SQLite database | C / Emscripten |
| Bitwarden | Cryptographic primitives | Rust / wasm-pack |
Performance Characteristics
WebAssembly typically achieves 70-90% of native C++ performance — a dramatic improvement over JavaScript for compute-intensive tasks. Key factors:
- Deterministic performance: no garbage collection pauses (Wasm uses linear memory, not a GC heap)
- Predictable optimization: the binary format is designed for single-pass compilation
- SIMD: the v128 vector type enables data-parallel execution
- Threads: the
SharedArrayBuffer+AtomicsAPI enables multi-threaded WebAssembly
Related conversions
Frequent conversions across the catalogue: