Anatomy of a WebAssembly file
WebAssembly (Wasm) is a portable binary instruction format for a stack machine; these examples use its equivalent human-readable WebAssembly Text (WAT) representation.
File extensions: .wat, .wasm
Every part of the example below is labelled and explained. This page is one of 55 annotated tours on AnatomyOf, a free, open-source project by LunarWerx Studios.
What is inside a WebAssembly file
Module definition
The top-level container for all of a Wasm binary's components.
Every Wasm file is a single module: the root s-expression in the text format (WAT), or the \0asm magic-number-prefixed binary it compiles to. A module bundles together its types, imports, functions, memories, tables, globals, and exports as ordered sections. Modules are the unit of validation and instantiation: the host (a browser, wasmtime, wasmer, etc.) parses and validates the whole module before a single instruction runs, which is why Wasm has a reputation for failing fast and loud rather than corrupting memory quietly.
Type definition
Declares a function signature: its parameter and result types.
A type entry names a function signature so it can be referenced by multiple functions or call sites without repeating it: (type $add_t (func (param i32 i32) (result i32))) describes "takes two i32s, returns one i32." Wasm has exactly four core value types: i32, i64, f32, f64 (plus newer additions like v128 and reference types). This is also the backbone of call_indirect, which checks the callee's actual signature against the expected type at runtime. The closest thing Wasm has to a vtable check, and the reason a mismatched function-pointer call traps instead of jumping into the weeds.
Comment
Text ignored by the assembler: single-line (;;) or block ((; ... ;)).
WAT supports line comments starting with ;; that run to the end of the line, and block comments delimited by (; and ;) that can nest, handy for temporarily commenting out an s-expression that itself contains comments. Comments exist only in the text format; they carry no meaning to the binary format or the validator. Because WAT is essentially assembly with parentheses, comments are doing a lot of load-bearing work explaining *why* a sequence of stack operations adds up to something meaningful, since the instructions themselves are famously terse.
Import definition
Declares a function, memory, table, or global provided by the host.
(import "env" "log" (func $log (param i32))) asks the host to supply a function named log from a module namespace named env before instantiation can complete. Imports are how a sandboxed Wasm module talks to the outside world at all. There is no ambient syscall table, no filesystem, no network; if it isn't imported, it doesn't exist to the module. This all-imports-explicit design is Wasm's capability-based security model in miniature: a host embeds a module and hands it exactly the functions it chooses to, nothing more.
Function definition
Defines a function's parameters, result, locals, and instruction body.
func defines a callable unit: parameters and result come from its type, and the body is a flat sequence of instructions (plus any local declarations for scratch variables). Functions are referenced by index or, in WAT, by a friendlier $name identifier that the assembler resolves to an index for you. Unlike most assembly, a Wasm function body is structured: block, loop, and if provide control flow instead of arbitrary jumps, which is precisely what lets validators check a module in a single linear pass instead of needing full control-flow-graph analysis like a native disassembler does.
Instruction
A single operation executed by the stack machine.
Instructions like local.get, i32.add, or call are the atoms of a Wasm program. Most are named type.operation (i32.const, f64.mul), which keeps the instruction set small, dense, and mechanically easy to decode, a deliberate tradeoff for fast parsing and JIT compilation over human readability. Instructions are validated for type-correctness against the operand stack ahead of time, so a Wasm engine never discovers a type error mid-execution the way a dynamically typed language might; it either validates cleanly or is rejected before instantiation.
Stack operations
Wasm is a stack machine; instructions implicitly push and pop values.
There are no general-purpose registers in Wasm: local.get $lhs pushes a value onto an implicit operand stack, and i32.add pops the top two values, adds them, and pushes the sum back. This stack-machine design (borrowed from JVM bytecode and predecessors like Forth) makes the bytecode compact and the validator simple, at the cost of needing more instructions to shuffle values around than a register machine would. Engines are free to compile stack code down to registers internally (and every serious one does). The stack model is a portable *interchange* representation, not a mandate about how th
Export definition
Makes a module's function, memory, table, or global visible to the host.
(export "add" (func $add)) gives the host-visible name "add" to an internal function, which is how JavaScript's WebAssembly.instantiate (or any other embedder) gets a handle to call into the module after instantiation. Memories, tables, and globals can be exported the same way. Exports are the mirror image of imports: together they define a module's entire public surface area. A function that is never exported and never called via start is effectively dead code from the host's perspective, even though the validator will happily accept it.
Start function
Specifies a function to run automatically upon instantiation.
The optional (start $main) section names a function (taking no parameters and returning nothing) that the host runs immediately after the module finishes instantiating, before any exported function is callable by user code. It's the closest thing Wasm has to a "main," useful for one-time setup like initializing globals from imported values. Most toolchains (Emscripten, wasm-bindgen) skip start in favor of an explicit exported _initialize or main that the host calls deliberately, since an implicit auto-run function is easy to forget about when debugging module load order.
Identifier
A symbolic $name for a function, type, local, or parameter.
WAT lets you write $add or $lhs instead of raw numeric indices, purely a text-format convenience the assembler resolves at compile time. The binary format has no idea $add ever existed; it only ever sees index 0, 1, 2, and so on. This is why disassembling a .wasm straight to WAT without a name section gives you an anonymous, faintly menacing forest of $func_12-style placeholders. The human-readable names are metadata the compiler chose to keep (or strip for size), not something the format requires.
Official WebAssembly site · All languages on AnatomyOf
The interactive tour needs JavaScript. Enable it to hover a callout and trace it into the code.