Pass Strings Between JavaScript and WebAssembly

WebAssembly (Wasm) only natively supports numeric data types, making the transfer of strings between JavaScript and Wasm a unique challenge. This article explains how to pass string data back and forth by leveraging WebAssembly’s shared linear memory, manually encoding and decoding strings using TextEncoder and TextDecoder, and utilizing modern tooling to simplify the process.

The Core Concept: Shared Linear Memory

Because WebAssembly cannot directly receive or return JavaScript strings, both environments must communicate through Wasm’s linear memory. This memory is represented as a raw ArrayBuffer that JavaScript can read and write to. Passing a string requires converting the characters into raw bytes (usually UTF-8), writing those bytes to memory, and passing the memory address (pointer) and length between JavaScript and WebAssembly.


1. Passing Strings from JavaScript to WebAssembly

To send a JavaScript string to a WebAssembly module, you must manually allocate memory inside the Wasm instance, write the string bytes to that space, and then pass the memory pointer to your Wasm function.

Step A: Encode the String

Use the browser’s built-in TextEncoder to convert the JavaScript UTF-16 string into a UTF-8 byte array (Uint8Array).

const encoder = new TextEncoder();
const encodedString = encoder.encode("Hello from JS!");

Step B: Allocate Memory in WebAssembly

Your WebAssembly module must export an allocation function (often named malloc or alloc) that reserves space in its linear memory and returns a pointer (index).

const length = encodedString.length;
const pointer = wasmModule.instance.exports.alloc(length);

Step C: Copy Bytes into Wasm Memory

Access the raw memory buffer of the Wasm instance, create a Uint8Array view starting at the allocated pointer, and copy your encoded string into it.

const memoryBuffer = new Uint8Array(wasmModule.instance.exports.memory.buffer);
memoryBuffer.set(encodedString, pointer);

Step D: Call the Wasm Function

Now, pass the pointer and the byte length as numeric arguments to your WebAssembly function.

wasmModule.instance.exports.process_string(pointer, length);

2. Passing Strings from WebAssembly to JavaScript

To retrieve a string generated or modified by WebAssembly, the Wasm function must return a pointer to the memory location of the string, along with its length.

Step A: Receive Pointer and Length

Call your WebAssembly function. It should return either a pointer, or write the pointer and length to a known memory address. For this example, let’s assume the Wasm function returns the pointer directly, and we know the length, or the function returns the length.

const pointer = wasmModule.instance.exports.get_string_pointer();
const length = wasmModule.instance.exports.get_string_length();

Step B: Read and Decode the Memory

Create a temporary view of the Wasm linear memory using the returned pointer and length. Then, use TextDecoder to convert those bytes back into a JavaScript string.

const memoryBuffer = new Uint8Array(wasmModule.instance.exports.memory.buffer);
const stringBytes = memoryBuffer.subarray(pointer, pointer + length);

const decoder = new TextDecoder("utf-8");
const jsString = decoder.decode(stringBytes);

console.log(jsString); // Outputs the string from WebAssembly

Simplifying the Process with Tooling

While manually managing pointers and byte buffers is highly performant, it is prone to memory leaks if allocated memory is not freed. Most developers use high-level tooling to automate this glue code: