From Binary to Running Code
A WebAssembly module starts life as a .wasm binary — a compact, portable bytecode file produced by a compiler like Emscripten or wasm-pack. Before any of its functions can run, the host environment must move that binary through three stages: fetching the raw bytes, compiling them into a WebAssembly.Module (a validated, ready-to-instantiate blueprint), and instantiating that module into a WebAssembly.Instance with live memory, tables, and linked imports. Skipping or reordering these stages is not possible — each API call depends on the output of the previous one.
Cricket analogy: Think of the raw .wasm bytes like a sealed kit bag arriving before a Test match: the ball, bat, and pads (bytes) must be unpacked and inspected before the umpire (the compiler) certifies them fit for play, and only then can Virat Kohli actually walk out and bat (the instance runs).
Compiling a Module: compile() vs compileStreaming()
WebAssembly.compile(bufferSource) takes an already-downloaded ArrayBuffer or TypedArray and asynchronously compiles it into a WebAssembly.Module, returning a Promise. This requires the entire binary to be in memory before compilation starts, which is fine for small modules loaded via fs.readFile in Node.js or a completed fetch().arrayBuffer() call in the browser, but it wastes time waiting for the full download before doing any compiler work.
Cricket analogy: It's like waiting for the entire day's play to be recorded on a scorecard before a commentator analyses it, rather than commentating ball-by-ball — MS Dhoni's whole innings has to be over before you review it as one block.
WebAssembly.compileStreaming(source) accepts a Response object or a Promise for one — typically the direct result of fetch() — and compiles the module while bytes are still arriving over the network. Browsers implement this by validating and compiling function bodies incrementally as network chunks land, which is significantly faster for large modules and is the recommended approach whenever the source is a network response. The server must send the Content-Type: application/wasm header, or compileStreaming will reject.
Cricket analogy: It's like a scorer updating the scoreboard ball-by-ball during a live IPL match rather than waiting till stumps — analysis happens in real time as Jasprit Bumrah bowls each delivery, not after the innings ends.
// Streaming compile + instantiate directly from a network response
const importObject = {
env: {
memory: new WebAssembly.Memory({ initial: 10 }),
log_i32: (value) => console.log('wasm says:', value),
},
};
WebAssembly.instantiateStreaming(fetch('math.wasm'), importObject)
.then(({ module, instance }) => {
const result = instance.exports.add(3, 4);
console.log('3 + 4 =', result);
})
.catch((err) => console.error('Failed to load wasm module:', err));Instantiating: instantiate() vs instantiateStreaming()
WebAssembly.instantiate() has two overloads: pass it a WebAssembly.Module plus an import object to get back a WebAssembly.Instance directly, or pass it a raw BufferSource plus an import object to get back { module, instance } in one step (compiling and instantiating together). WebAssembly.instantiateStreaming(source, importObject) is the streaming equivalent of the buffer-source overload — it is the fastest and most common way to bring a module to life in a browser, combining download, compile, and instantiate into a single pipelined call.
Cricket analogy: It's like the difference between a net bowler who's already been assessed and cleared (a compiled Module you just field a team with) versus a trial bowler who is assessed and selected in one combined session (instantiate from raw bytes) — both end with someone bowling, but the paths differ.
Browsers cache compiled WebAssembly.Module objects keyed by the response's URL and headers via the HTTP cache, so repeat visits can skip recompilation entirely when instantiateStreaming is used with a cacheable fetch response. For very large modules (game engines, codecs), this can save hundreds of milliseconds on subsequent loads.
Import Objects and Reading Exports
Every instantiation call takes an importObject, a plain JavaScript object whose top-level keys are module namespaces (matching the (import "env" "log") declarations inside the .wasm binary) and whose values are objects providing the actual functions, memories, tables, or globals the module expects. Once instantiation succeeds, instance.exports exposes everything the module marked as (export ...) — typically functions, but potentially memory, tables, and globals too — as ordinary JavaScript-callable values.
Cricket analogy: It's like a franchise squad list for the IPL auction: the import object is the roster of players (env.log, env.memory) a team must have signed and ready before the tournament (instantiation) starts, matched by exact role and name.
If the import object is missing a required import, has the wrong type (e.g., providing a number where a function is expected), or a memory/table with incompatible size limits, instantiation throws a WebAssembly.LinkError — always wrap instantiate/instantiateStreaming calls in a try/catch or .catch() and log the specific missing import name for debugging.
- Loading a wasm module has three stages: fetch bytes, compile into a Module, instantiate into a live Instance.
- WebAssembly.compile() works on an in-memory buffer; WebAssembly.compileStreaming() compiles while the network response is still downloading.
- instantiateStreaming(fetch(url), importObject) is the fastest, most common path in browsers — it fuses fetch, compile, and instantiate.
- The server must send Content-Type: application/wasm or streaming APIs will reject the response.
- The importObject's namespaces and keys must exactly match the module's declared imports, or instantiation throws a LinkError.
- instance.exports exposes the module's exported functions, memory, tables, and globals as usable JavaScript values.
- Browsers can cache compiled Module bytecode via the HTTP cache, speeding up repeat loads of the same wasm URL.
Practice what you learned
1. Which API compiles a WebAssembly module incrementally while it is still downloading over the network?
2. What HTTP header must a server send for WebAssembly.instantiateStreaming(fetch(url)) to succeed?
3. What error is thrown if the importObject passed to instantiate() is missing a function the module declares as an import?
4. Where do a module's exported functions become accessible after successful instantiation?
5. Which function signature lets you compile and instantiate in a single call from a raw ArrayBuffer, returning both module and instance?
Was this page helpful?
You May Also Like
The WebAssembly JavaScript API
A tour of the WebAssembly global object's core classes — Module, Instance, Memory, Table, and Global — and how JavaScript orchestrates Wasm.
Passing Data Between JS and Wasm
Practical techniques for moving numbers, strings, and structured data across the JavaScript/WebAssembly boundary via linear memory.
Memory Management in Wasm
How WebAssembly's linear memory model works, how it grows, and how it differs from garbage-collected host environments like JavaScript.
Related Reading
Related Study Notes in Web Development
Browse all study notesWebSockets Study Notes
Web Development · 30 topics
Web DevelopmentgRPC Study Notes
Protocol Buffers · 30 topics
Web DevelopmentSpring Boot Study Notes
Java · 30 topics
Web DevelopmentFlask Study Notes
Python · 30 topics
Web DevelopmentDjango Study Notes
Python · 30 topics
Web DevelopmentNext.js Study Notes
JavaScript · 30 topics