Web Platformadvanced
Transferable objects with postMessage for zero-copy worker handoff
Published June 15, 2027
const { port1, port2 } = new MessageChannel();
port2.onmessage = (event: MessageEvent<ArrayBuffer>) => {
console.log("received", event.data.byteLength, "bytes, zero-copy");
};
const buffer = new ArrayBuffer(1024 * 1024 * 16);
port1.postMessage(buffer, [buffer]);
console.log(buffer.byteLength); // 0 — ownership transferred, not copiedWhat
postMessage(data, transferList)'s second argument moves ownership of the listed objects to the receiver instead of structured-cloning them — after the call, the sender can no longer use them.
Why it matters
By default, postMessage — to a Worker, through a MessagePort, or to another window — structured-clones its payload. For a large ArrayBuffer (image data, audio samples, a big typed array), that means copying every byte, which is slow and briefly doubles memory use. Passing the buffer in the transfer list instead makes the handoff O(1): the receiver gets the exact same underlying memory, and the sender's reference is detached, its byteLength dropping to 0. It's the difference between sending a copy and handing over the original.
How it works
MessageChannelcreates a connected pair of ports (port1/port2). The transfer mechanism is identical acrossWorker.postMessage,window.postMessage, and ports — a channel just demonstrates it in one file, with no separate worker script needed.- The second argument to
postMessage,[buffer], is the transfer list. Every transferable object in it (ArrayBuffer,MessagePort,ImageBitmap, and a handful of others) is detached from the sender and re-attached on the receiver — no bytes are copied. buffer.byteLengthreads0immediately after the call, on that samebuffervariable — direct proof the sender no longer owns the memory, not just that a copy happened somewhere else.
Gotchas
- Only certain types are transferable. A plain object, array, or a
TypedArrayview itself isn't — though the.bufferan array view wraps is. Using an already-transferred buffer afterward throws. - If the sender still needs the data after sending it, transfer is the wrong tool — omit the transfer list and let it clone, or send a copy on purpose.
- Structured cloning still applies to the rest of the message. Only the objects named in the transfer list are moved; everything else in the payload is cloned as usual.
Related
- Web Worker — the most common place this shows up: handing large binary data to or from a background thread without copying it.
- Node's
worker_threads—worker.postMessagehas the same transfer-list mechanic on the server side.