Web Workers: Messaging, Cloning, and the Spectre Incident That Broke Everything
Two-Way Communication with postMessage
So far messages only flowed one way — worker to page. Now let's go both directions.
Inside worker.js, set up a message listener using the onmessage shorthand:
self.onmessage = function onMessage(evt) {
console.log('received in web worker', evt.data);
};Both self.onmessage = and self.addEventListener('message', ...) work — just two styles for the same thing.
Then in home.js, after the worker sends its first message, respond to it:
function onMessage(evt) {
console.log(evt.data);
worker.postMessage('hello from the client');
}The Ping-Pong Test ✅
Click Start and the console shows:
hello from the web worker ← worker → page
received in web worker, hello from the client ← page → workerTwo-way communication confirmed. The pattern is symmetrical:
| Direction | Method |
|---|---|
| Worker → Page | self.postMessage(data) inside worker |
| Page → Worker | worker.postMessage(data) on the worker instance |
| Receiving (either side) | Listen for message event, read evt.data |
How Data Actually Travels: The Structured Clone Algorithm 🧬
Here's the part that trips people up. Data passed between a worker and the page is not sent by reference it's copied.
This is intentional. If it were passed by reference, two threads could read and write the same memory simultaneously classic race condition territory.
The copying mechanism is called the Structured Clone Algorithm, part of the web spec. Key things to know:
- Primitives (strings, numbers, Booleans) — copied cleanly
- Objects and arrays — deep copied, including nested structures
- Functions — ❌ cannot be cloned
Workaround for functions: serialize with
.toString(), send the string, theneval()it inside the worker. Some libraries actually do this to run arbitrary code in background threads.
The Hidden Cost: Copy Overhead ⚠️
For large data payloads this matters. Say you send several megabytes of bitmap data to a worker for processing. The structured clone algorithm will:
- Make a full copy when sending to the worker
- Make another full copy when the result comes back
That's potentially double the memory overhead. For small no issue. For heavy data something to design around. This is exactly why transferables and shared array buffers were introduced.
Data Transfer Solutions: A Brief History 📦
The Problem
Web workers are supposed to make heavy processing faster. But if you have 100MB of data and you copy it to send to the worker now you have 200MB in memory. That defeats the purpose.
Attempt 1: Transferables
The first solution was transferables instead of copying, the browser moves the data:
- Post to worker → disappears from the main thread
- Worker posts back → disappears from the worker
Only one copy ever exists at any point. No duplication.
Transferables appear to have since been deprecated and don't have much of a future.
Attempt 2: Shared Array Buffers
About two years before this recording, the web platform shipped Shared Array Buffers a literal shared memory segment that both threads could access simultaneously. No copying, no moving just shared RAM.
To handle the obvious race condition problem, they also shipped Atomics an API providing mutex-like primitives so only one thread accesses the shared memory at a time.
The workflow: stuff data into a shared array buffer → hand it to the worker → worker operates on it → signals done → main thread reads results from the same memory. Clean, fast, zero copies.
The Spectre Incident 💥
A major CPU-level vulnerability called Spectre was disclosed. The attack required a high-resolution timer (nanosecond precision) to exploit. The web platform had always been careful about not exposing such timers — but it turned out Shared Array Buffers were a perfect mechanism for constructing one, and attackers exploited exactly that on web pages.
Less than six months after shipping, Shared Array Buffers were disabled across browsers.
"Every time they give us something cool, some jerk goes and abuses it — and that's why we can't have nice things."
What You Can Actually Rely On Today ✅
- Copy via
postMessage— works reliably cross-browser for strings, numbers, and objects - Be mindful of payload size — large data is expensive to copy
- Shared Array Buffers and Transferables are not safe assumptions for production