WASM Basics
WebAssembly fundamentals including module structure, types, memory model, and binary/text formats
You are an expert in WebAssembly fundamentals for building WebAssembly applications. ## Key Points - **Type section** — function signatures (parameter and return types) - **Import section** — functions, memories, tables, and globals imported from the host - **Function section** — declares function indices referencing type signatures - **Memory section** — defines linear memory (pages of 64KB each) - **Export section** — functions, memories, tables, and globals exposed to the host - **Code section** — function bodies containing Wasm instructions - **Data section** — initializes ranges of linear memory - **Use streaming compilation** — `WebAssembly.instantiateStreaming` compiles while downloading, which is significantly faster than fetching the entire buffer first. - **Minimize host calls** — crossing the Wasm/JS boundary has overhead; batch operations in Wasm before returning results. - **Size linear memory carefully** — each page is 64KB. Set initial and maximum bounds to avoid unnecessary memory growth. - **Validate modules** — use `wasm-validate` or `WebAssembly.validate()` before deployment to catch structural errors early. - **Use the text format for debugging** — WAT is human-readable and helps diagnose issues in compiled output. ## Quick Example ```wat (memory (export "memory") 1 10) ;; initial 1 page, max 10 pages ```
skilldb get webassembly-skills/WASM BasicsFull skill: 177 linesWebAssembly Basics — WebAssembly
You are an expert in WebAssembly fundamentals for building WebAssembly applications.
Overview
WebAssembly (Wasm) is a binary instruction format designed as a portable compilation target for high-level languages. It runs in a sandboxed execution environment at near-native speed. Wasm modules are compact, load quickly, and integrate with JavaScript and browser APIs through a well-defined interface.
Core Concepts
Module Structure
A WebAssembly module consists of several sections:
- Type section — function signatures (parameter and return types)
- Import section — functions, memories, tables, and globals imported from the host
- Function section — declares function indices referencing type signatures
- Memory section — defines linear memory (pages of 64KB each)
- Export section — functions, memories, tables, and globals exposed to the host
- Code section — function bodies containing Wasm instructions
- Data section — initializes ranges of linear memory
Value Types
Wasm has four core value types:
| Type | Description |
|---|---|
i32 | 32-bit integer |
i64 | 64-bit integer |
f32 | 32-bit IEEE 754 float |
f64 | 64-bit IEEE 754 float |
Extended proposals add v128 (SIMD), funcref, and externref (reference types).
Linear Memory
Memory in Wasm is a contiguous, byte-addressable array that grows in 64KB pages:
(memory (export "memory") 1 10) ;; initial 1 page, max 10 pages
All data access uses load/store instructions with offsets into this linear memory.
Text Format (WAT)
The WebAssembly Text Format is an S-expression-based representation:
(module
(func $add (param $a i32) (param $b i32) (result i32)
local.get $a
local.get $b
i32.add
)
(export "add" (func $add))
)
Binary Format
The binary format (.wasm) starts with the magic bytes \0asm followed by version 1. Each section is encoded with an ID byte and a byte-length prefix. The binary format is what browsers and runtimes actually execute.
Implementation Patterns
Loading a Wasm Module in JavaScript
// Streaming instantiation (preferred for browsers)
const response = fetch('module.wasm');
const { instance, module } = await WebAssembly.instantiateStreaming(response, {
env: {
log: (value) => console.log('From Wasm:', value),
},
});
const result = instance.exports.add(40, 2);
console.log(result); // 42
Compiling from WAT to Wasm
Using the wabt toolchain:
# Convert WAT text to binary Wasm
wat2wasm add.wat -o add.wasm
# Disassemble binary back to text
wasm2wat add.wasm -o add.wat
# Validate a module
wasm-validate add.wasm
Working with Linear Memory
const memory = new WebAssembly.Memory({ initial: 1, maximum: 10 });
const importObject = { env: { memory } };
const { instance } = await WebAssembly.instantiateStreaming(fetch('app.wasm'), importObject);
// Read a string written by Wasm into memory
const buffer = new Uint8Array(memory.buffer);
const decoder = new TextDecoder();
const str = decoder.decode(buffer.slice(0, 12));
Writing a Complete WAT Module with Memory
(module
(memory (export "memory") 1)
;; Store a 32-bit integer at byte offset 0
(func $store_value (param $val i32)
i32.const 0 ;; address
local.get $val
i32.store
)
;; Load the 32-bit integer from byte offset 0
(func $load_value (result i32)
i32.const 0
i32.load
)
(export "store_value" (func $store_value))
(export "load_value" (func $load_value))
)
Best Practices
- Use streaming compilation —
WebAssembly.instantiateStreamingcompiles while downloading, which is significantly faster than fetching the entire buffer first. - Minimize host calls — crossing the Wasm/JS boundary has overhead; batch operations in Wasm before returning results.
- Size linear memory carefully — each page is 64KB. Set initial and maximum bounds to avoid unnecessary memory growth.
- Validate modules — use
wasm-validateorWebAssembly.validate()before deployment to catch structural errors early. - Use the text format for debugging — WAT is human-readable and helps diagnose issues in compiled output.
- Prefer typed arrays for memory access —
Uint8Array,Int32Array, andFloat64Arrayviews overmemory.bufferare efficient and type-safe.
Common Pitfalls
- Stale ArrayBuffer references — when
memory.grow()is called, the underlyingArrayBufferis detached. Any existing typed array views become invalid and must be recreated frommemory.buffer. - Alignment traps —
i32.loadandi64.loadrequire naturally aligned addresses on some platforms. Unaligned access may trap or silently produce wrong results. - Integer overflow semantics — Wasm integers wrap silently on overflow (no exceptions). This matches C behavior but can surprise developers from languages with arbitrary-precision integers.
- No built-in string type — strings must be manually encoded/decoded through linear memory. Forgetting null terminators or mismatching encoding (UTF-8 vs UTF-16) causes data corruption.
- Import/export name mismatches — the host must provide imports with exactly matching module and field names, or instantiation fails at link time.
- Forgetting to export memory — if the host needs to read or write Wasm memory, the module must explicitly export it.
Core Philosophy
WebAssembly is a compilation target, not a programming language. You do not write Wasm by hand (though you can in WAT for debugging). Instead, you write in Rust, C, C++, AssemblyScript, or another language and compile to the Wasm binary format. Understanding the underlying model — the stack machine, linear memory, function tables, and type system — is valuable for debugging and performance tuning, but your daily workflow should be in a high-level language with proper tooling.
Wasm's security model is containment by default. A module has no access to the host environment unless the host explicitly provides imports. It cannot read files, make network requests, or access the DOM without the host's cooperation. This sandbox is what makes Wasm safe to run untrusted code — and it is the foundation for WASI's capability-based security model.
The four core value types (i32, i64, f32, f64) are not a limitation but a deliberate design choice. They map directly to hardware registers and enable predictable, near-native performance. Complex types — strings, arrays, objects — are represented as patterns in linear memory. Understanding that Wasm operates on numbers and memory regions, not high-level objects, is essential for effective interop design.
Anti-Patterns
-
Writing complex modules in WAT by hand — WAT is useful for learning and debugging, but writing production modules in text format is impractical and error-prone; use a high-level language with a Wasm compilation target.
-
Assuming Wasm replaces JavaScript — Wasm excels at computation-intensive tasks but has no DOM access, no garbage collector (in most runtimes), and significant interop overhead for string-heavy workloads; use it where it has a clear advantage, not everywhere.
-
Setting linear memory to exactly the needed size — starting with
initial: 1page and growing dynamically is expensive and invalidates views; allocate generous initial memory based on expected workload. -
Ignoring the import object at instantiation — if a module declares imports and the host provides an empty or mismatched import object, instantiation fails with a link error; inspect the module's imports before instantiation.
-
Relying on browser-specific Wasm extensions — features like threads, SIMD, and reference types have varying support across browsers and runtimes; check compatibility before using them in production.
Install this skill directly: skilldb add webassembly-skills
Related Skills
Assemblyscript
Writing WebAssembly modules using AssemblyScript, a TypeScript-like language that compiles to Wasm
Js Interop
JavaScript and WebAssembly interop patterns including memory sharing, type marshaling, and binding generation
Performance
Optimizing WebAssembly performance including binary size, execution speed, memory usage, and profiling techniques
Rust WASM
Compiling Rust to WebAssembly using wasm-pack, wasm-bindgen, and the Rust Wasm ecosystem
Wasi
WebAssembly System Interface (WASI) for portable system-level access including filesystem, networking, and clocks
WASM in Browser
Using WebAssembly in the browser for canvas rendering, audio processing, Web Workers, and DOM integration