postMessage and Transferables
How postMessage copies data — structured clone
Section titled “How postMessage copies data — structured clone”By default, postMessage deep-clones the value using the structured clone algorithm. This is more capable than JSON.stringify: it handles circular references, TypedArray, Blob, Map, Set, Date, and more. The original object on the sending side is completely untouched — both sides end up with independent copies.
The cost of structured clone is proportional to the size of the data. Cloning a few small objects is negligible. Cloning a 10 MB ArrayBuffer means copying 10 MB across thread boundaries — measurably slow for real-time workloads.
const arr = [1, 2, 3];worker.postMessage(arr); // arr is cloned — original is unchangedconsole.log(arr); // [1, 2, 3] — still hereTransferables — zero-copy ownership transfer
Section titled “Transferables — zero-copy ownership transfer”To avoid copying large buffers, pass them as transferables in the second argument to postMessage. Instead of cloning, the browser moves ownership of the underlying memory to the receiver. The source side is detached immediately — its byteLength drops to 0 and any attempt to read or write it throws.
const buffer = new ArrayBuffer(1024 * 1024); // 1 MBworker.postMessage(buffer, [buffer]); // transfer ownershipconsole.log(buffer.byteLength); // 0 — detached!The second argument is an array of transferable objects. Every item in that array must also appear as (or inside) the first argument — otherwise the browser won’t know where to route it.
When to use transferables
Section titled “When to use transferables”- Large
ArrayBuffers (images, audio, binary blobs) — transferables are orders of magnitude faster than clone for multi-MB data MessagePort— transfer a port to route messages directly between two workers without going through the main threadOffscreenCanvas— transfer canvas rendering to a worker for off-thread 2D or WebGL drawing- For small plain objects, structured clone is fine and simpler — don’t over-engineer
Runnable: transfer an ArrayBuffer, observe detachment
Section titled “Runnable: transfer an ArrayBuffer, observe detachment”This demo creates a 1 MB ArrayBuffer, fills byte 0 with the value 42, then transfers it to a worker. Notice the byteLength printed before and after the transfer call.