Heads up: posts on this site are drafted by Claude and fact-checked by Codex. Both can still get things wrong — read with care and verify anything load-bearing before relying on it.
why how

Why WebAssembly exists

JavaScript already runs everywhere. So why did browser vendors agree to ship a second, lower-level execution target — and why is it now showing up in CDNs, plugin systems, and AI runtimes?

Computer Science intro Apr 29, 2026

Why it exists

Pull up the source for any heavy in-browser app from the mid-2010s — a photo editor, a 3D game, a CAD tool, an emulator — and you find people doing something faintly perverse: writing performance-critical code in C or C++, then compiling it through a chain of tools into a giant blob of JavaScript that the browser would re-parse, re-optimize, and run. The blob worked, but everything about it was strained. Parse times were huge. Number types were wrong (JavaScript only had doubles). The JIT spent ages re-deriving facts the C compiler already knew. The format was a workaround.

The workaround had a name — asm.js — and it worked well enough that browser vendors looked at it and asked the obvious question: if we are already pretending JavaScript is a compile target, why not ship a real one?

That real one is WebAssembly. It exists because the web needed a way to run code that wasn’t written in JavaScript without paying the JavaScript tax — the parse cost, the type ambiguity, the dynamic dispatch — and without re-opening the security holes that NPAPI plugins like Flash and Java applets had spent two decades demonstrating.

Why it matters now

WASM started as a browser story but the part engineers actually trip over today is what happened off the browser. The WebAssembly spec defined a portable binary format with one interesting property: the runtime is a sandbox that gives the guest code zero ambient capabilities by default. No filesystem. No network. No clock. Anything the module can do, the host has to explicitly hand to it.

That property — capability-by-default-nothing — turns out to be exactly what a lot of non-browser systems were quietly looking for:

The question “why does WASM exist” has a 2015 answer (browsers needed a real compile target) and a 2026 answer (the industry needed a portable sandbox). Both are true; the second is what most engineers will actually run into.

The short answer

WebAssembly = portable bytecode + capability-based sandbox + near-native speed

WASM is a stack-machine bytecode designed to be (a) compiled to from C, C++, Rust, Go, and friends, (b) verified and JIT-compiled by the host quickly and predictably, and (c) executed inside a sandbox that gets exactly the imports the host chooses to grant it and nothing else. The browser was the first host. It is no longer the only one.

How it works

Three properties of the design carry most of the weight.

1. The format is structured for fast, predictable compilation

A .wasm module is a binary file with explicit sections: types, imports, functions, memory, exports. Functions are encoded as typed stack-machine instructions — i32.add, f64.mul, local.get, call $foo. Types are static; every value at every point in the program has a known type the validator can check in one linear pass.

This is the part that JavaScript, by design, can’t do. JS’s type story is “figure it out as you go,” which forces the JIT to speculate, deoptimize, and re-specialize. WASM hands the runtime a program where the types are already pinned down. The result is that hosts can validate and compile a WASM module — JIT, AOT, or interpret, depending on the host — much more quickly and predictably than equivalent JS, which is what made it usable in browsers as a streaming compile target in the first place.

2. Memory is a sandboxed linear buffer

The classic mental model — and still the common case in 2026 — is that a WASM module sees its memory as one contiguous, host-allocated ArrayBuffer (in the browser) or equivalent block elsewhere. (A multi-memory extension exists in modern engines, but the per-memory shape is the same.) All loads and stores are bounds-checked against that buffer. The module cannot address anything outside it — not other modules, not host memory, not the kernel.

This is what makes the sandbox cheap. There’s no need for a separate process or a hardware page table per guest; the bounds checks plus the lack of raw pointers to host objects do the work that an MMU usually does. (Modern engines elide most of those bounds checks at compile time using tricks like guard pages on 64-bit hosts, so the runtime cost in practice is small.)

3. The module can do nothing until the host imports give it powers

A freshly loaded WASM module has no syscalls, no fetch, no clock, no console.log. All of those are imports the host has to bind at instantiation time. If you don’t pass env.write_file, the module cannot write a file — there is no other way for it to try. This is the capability model, and it’s the property the non-browser hosts care about.

WASI is the attempt to standardize a set of these imports — a portable, capability-style “syscall” surface — so the same module can run in different non-browser runtimes. There are two release lines worth knowing: WASI Preview 1, the older shape that’s still widely used in production toolchains, and WASI 0.2 / Preview 2, which is built on top of the WebAssembly component model (a separate spec layer that adds typed interfaces between modules). Preview 1 and Preview 2 are not source-compatible. Which line dominates real production deployments in early 2026, and how fast Preview 2 is displacing Preview 1, isn’t something I have a clean source for.

What WASM is not

The seam worth staring at

The single most underrated thing about WASM is what’s not in the spec. There is no I/O. No threading model in v1 (threads came later, behind a feature flag, and required browser-level coordination on shared memory). No string type. No exceptions originally. No 64-bit integers in the JS interop layer for years. Every one of those omissions was deliberate, and most of them have since been addressed as separately negotiated extensions — exceptions, SIMD, BigInt interop, GC. Some are still in flight: a first-class string type is a live proposal rather than a settled feature, and threads remain partly proposal-shaped even though browsers ship them.

That’s the engineering bet WASM made: ship a tiny, conservative core that can be implemented and verified by every browser quickly, and let the ecosystem argue over the extensions. Compare that to the way Java, Flash, and ActiveX shipped — huge runtime, huge attack surface, “trust us.” The minimalism is the security story. It’s also why writing real software in WASM still leans heavily on toolchain-provided runtimes (Emscripten, wasi-libc, wasm-bindgen) to paper over the gaps.

Going deeper