Your First Wasm Module
Every Wasm journey starts with the same loop: write a module in the WebAssembly Text format (WAT), compile it to bytes, instantiate it, and call an exported function. This lesson walks through that loop end-to-end with the smallest possible example — a double function that multiplies its argument by two.
By the end you will have written real WAT, compiled it live in the browser, and called it from JavaScript.
Writing the WAT Module
Section titled “Writing the WAT Module”WAT is the human-readable text representation of a .wasm binary. Here is the complete module:
(module (func (export "double") (param $n i32) (result i32) local.get $n i32.const 2 i32.mul))Let’s break down every line:
(module ...)— the top-level wrapper. Every.wasmfile contains exactly one module.(func (export "double") ...)— declares a function and immediately makes it visible to the host as"double". The export name is what JavaScript sees oninstance.exports.(param $n i32)— declares one parameter named$nof typei32(a 32-bit signed integer). The$nlabel is a WAT convenience — at the binary level it is just an index.(result i32)— declares that this function returns onei32.local.get $n— pushes the current value of$nonto the operand stack.i32.const 2— pushes the constant2onto the stack.i32.mul— pops the top two stack values, multiplies them, and pushes the result. Because this is the last instruction and its type matches(result i32), the result becomes the return value.
Wasm’s stack-based design means there is no explicit return — the value left on top of the stack at the end of the function body is the return value.
Compile: WAT Bytes with compileWat
Section titled “Compile: WAT Bytes with compileWat”The sandbox exposes a global compileWat(watString) function powered by the WABT library. It takes your WAT source string and returns a Promise<Uint8Array> — the raw .wasm bytes you would normally get from a file on disk or a network fetch.
const bytes = await compileWat(wat);Instantiate: WebAssembly.instantiate
Section titled “Instantiate: WebAssembly.instantiate”WebAssembly.instantiate(bytes, importObject) is the standard browser API for creating a runnable Wasm instance. It returns a Promise that resolves to a { instance, module } pair:
instance— the live, runnable object. Its.exportsproperty contains all the functions and memories the module exported.module— the compiled but inertWebAssembly.Module. You can share and re-instantiate it without recompiling.
The second argument is the import object — a nested object of functions, memories, and globals you provide to the module from JavaScript. Because our double function imports nothing from the host, we pass {}.
const { instance } = await WebAssembly.instantiate(bytes, {});Call: instance.exports.double
Section titled “Call: instance.exports.double”Once instantiated, every exported function is available as a plain JavaScript function on instance.exports:
instance.exports.double(21); // → 42JavaScript numbers are automatically coerced to/from i32. The call is synchronous — no await needed.
Try It Live
Section titled “Try It Live”The runner below compiles the WAT, instantiates it, and calls double with three different inputs. Run it and watch the results in the console.