Managing WebAssembly Linear Memory from JavaScript: Practical Guide
Linear memory is WebAssembly’s isolated byte array. This guide shows how to create, grow, and access it from JavaScript, pass pointers for strings, test limits, and avoid common pitfalls.
01 Nov 2025, 13:36 UTC

Why Linear Memory Matters
When a WebAssembly module runs in a browser or Node.js, it doesn’t share the host’s heap. Instead it owns a linear memory – a contiguous, growable byte array. The module can read and write raw bytes, but it can’t directly use JavaScript objects or strings. Understanding how to create, grow, and access this memory from JavaScript is essential for efficient data exchange and preventing runtime errors.
Creating Memory with Limits
Linear memory is defined by two page counts: initial and maximum. A page is 64 KiB. The host can set these limits to guard against runaway allocation.
const memory = new WebAssembly.Memory({
initial: 2, // 2 pages = 128 KiB
maximum: 10 // 10 pages = 640 KiB
});
Pass the memory object in the import object when instantiating the module. If the module tries to grow beyond maximum, the memory.grow instruction will trap.
Reading and Writing from JavaScript
JavaScript can view the underlying ArrayBuffer of the memory using a typed array. The Uint8Array view lets you inspect or modify raw bytes.
const view = new Uint8Array(memory.buffer);
// Write a byte at offset 0
view[0] = 0x42;
// Read it back
console.log(view[0]); // 66
Any write performed by the Wasm module is immediately visible to JavaScript because both share the same buffer.
Passing Data: Pointers and Offsets
Wasm has no native string or struct type. To send a string from JS to Wasm, you must encode it as UTF‑8 bytes and pass the memory offset (an integer) to the module.
// Encode string to UTF-8
const encoder = new TextEncoder();
const bytes = encoder.encode('hello');
// Allocate space in memory (simple example: append at end)
const ptr = memory.buffer.byteLength;
new Uint8Array(memory.buffer, ptr, bytes.length).set(bytes);
// Call Wasm function that expects a pointer and length
wasmExports.processString(ptr, bytes.length);
The Wasm function would read bytes starting at ptr for len bytes and interpret them as a string.
Growing Memory at Runtime
The memory.grow instruction is how a module requests more pages. JavaScript can also grow memory by calling memory.grow directly.
// Grow by 3 pages (192 KiB)
const result = memory.grow(3);
console.log('previous page count:', result);
console.log('new byteLength:', memory.buffer.byteLength);
If the grow request exceeds maximum, the call returns -1 and the module traps. Verify this behavior by attempting an oversized grow.
Checking Boundaries Safely
Because Wasm code isn’t bounds‑checked, a bug that writes past the end of memory can corrupt data or crash the sandbox. A common mitigation is to keep a memoryLimit value in JavaScript and expose it to Wasm via an imported function or global. The module can then guard its accesses.
Limitations and Common Pitfalls
- No automatic bounds checking – always validate offsets in your Wasm code.
- Frequent memory.grow calls can cause fragmentation or performance hits because the underlying ArrayBuffer may need to be reallocated.
- Manual string encoding adds overhead; consider using a helper library to abstract pointer handling.
- Large maximum sizes can still exhaust host resources if the environment doesn’t enforce limits strictly.
Practical Verification Steps
- Instantiate the module with a memory object that has
initialandmaximumset. - Use a
Uint8Arrayto read a value written by the module and confirm it matches the expected byte. - Attempt to grow memory beyond
maximumand check that the result is-1or that the module traps. - After growing, verify
memory.buffer.byteLengthhas increased by the expected amount.
These checks ensure that memory is correctly configured, that data exchange works, and that limits protect the host.
Conclusion
Linear memory is the bridge between WebAssembly and JavaScript. By defining sensible limits, using typed arrays for inspection, and handling pointers carefully, you can build robust, high‑performance modules. Always validate offsets in Wasm, avoid unnecessary grow calls, and remember that strings must be manually encoded.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.