What SharedArrayBuffer does
SharedArrayBuffer is a special kind of buffer that holds raw binary data and can be shared between different threads, for example between a Web Worker and the main thread. It lets them read and write the same memory at the same time and does not copy the data when it is passed around: a reference to the very same region of memory is handed over.
Theory
TL;DR
SharedArrayBufferholds raw bytes and is visible to several threads at once.- On
postMessageit is neither copied nor detached; both threads see the same memory. - You read and write through a typed array, for example
Int32Array. - Simultaneous access means a risk of data races, which is why
Atomicsis needed. Atomics.wait()andAtomics.notify()provide real thread synchronisation.- It works in Web Workers and in Node.js (
worker_threads). - In the browser it is only available in a cross-origin isolated context (COOP + COEP).
Quick example
// A shared buffer of 4 bytes, which is exactly one Int32
const sharedBuffer = new SharedArrayBuffer(4);
const sharedArray = new Int32Array(sharedBuffer);
sharedArray[0] = 0;
Atomics.add(sharedArray, 0, 1); // atomic increment, safe across threads
console.log(Atomics.load(sharedArray, 0)); // 1Differences from a plain ArrayBuffer
| Property | ArrayBuffer | SharedArrayBuffer |
|---|---|---|
| Who uses it | A single thread | Several threads |
| Passing it to a worker | Copied, or moved with transfer and then unavailable in the original thread | Shared, both threads see the same memory |
Can Atomics be used | No | Yes, for safe synchronisation |
| Thread safety | Not required | Required, through Atomics |
A shared buffer between threads
The main thread (main.js):
// Create a shared buffer of 4 bytes (Int32 = 4 bytes)
const sharedBuffer = new SharedArrayBuffer(4);
// Wrap it in a typed array
const sharedArray = new Int32Array(sharedBuffer);
sharedArray[0] = 0; // initial value
// Create a worker and hand it the shared buffer
const worker = new Worker('worker.js');
worker.postMessage(sharedBuffer);
// Watch for changes
setInterval(() => {
console.log('Main thread value:', sharedArray[0]);
}, 1000);The worker (worker.js):
onmessage = (e) => {
const sharedArray = new Int32Array(e.data);
// Increase the value every 500 ms
setInterval(() => {
Atomics.add(sharedArray, 0, 1); // atomic increment
}, 500);
};What the console prints:
Main thread value: 0
Main thread value: 2
Main thread value: 4
Main thread value: 6
...SharedArrayBuffer provides the shared memory, and Atomics guarantees thread safety, meaning no data races.
Why Atomics is needed
Because several threads can read and write the same memory cell at once, without synchronisation you get race conditions. That is what the Atomics object is for, with its atomic operations:
| Method | What it does |
|---|---|
Atomics.add(typedArray, index, value) | Adds a value atomically |
Atomics.sub() | Subtracts atomically |
Atomics.and() / or() / xor() | Atomic bitwise operations |
Atomics.load() / Atomics.store() | Safe reading and writing |
Atomics.exchange() | Replaces the value and returns the old one |
Atomics.compareExchange() | Replaces it if the current value matches the expected one |
Atomics.wait() / Atomics.notify() | Puts a thread to sleep until the value changes (workers only) |
Synchronisation with Atomics.wait() and Atomics.notify():
// main.js
const sharedBuffer = new SharedArrayBuffer(4);
const sharedArray = new Int32Array(sharedBuffer);
const worker = new Worker('worker.js');
worker.postMessage(sharedBuffer);
// Wake the worker up after 2 seconds
setTimeout(() => {
console.log('Main: notify worker');
Atomics.store(sharedArray, 0, 1);
Atomics.notify(sharedArray, 0, 1);
}, 2000);// worker.js
onmessage = (e) => {
const sharedArray = new Int32Array(e.data);
console.log('Worker: waiting...');
Atomics.wait(sharedArray, 0, 0); // blocks until the value changes
console.log('Worker: woken up, value:', sharedArray[0]);
};Here the worker literally sleeps until the main thread wakes it up. This gives genuine coordination between JavaScript threads.
Where it is used
| Scenario | Application |
|---|---|
| Web Workers | Passing large data without copying |
| Multithreaded computation | Parallel processing of matrices, graphics, physics |
| Games and simulations | Updating world state across several threads |
| Machine learning and WASM | TensorFlow.js, WebAssembly and OpenCV use SharedArrayBuffer |
| Video and audio processing | Sharing buffers between threads |
| A bus between workers | Instant data transfer without JSON serialisation |
Security and cross-origin isolation
Because of Spectre-class vulnerabilities, browsers enable SharedArrayBuffer only in isolated contexts (cross-origin isolation). For it to work, the server must send these headers:
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corpAnd every resource (scripts, workers, images) has to be compatible with that mode.
For a dev server, Vite or Express for instance, it looks like this:
app.use((req, res, next) => {
res.setHeader('Cross-Origin-Opener-Policy', 'same-origin');
res.setHeader('Cross-Origin-Embedder-Policy', 'require-corp');
next();
});Now the browser will allow SharedArrayBuffer.
Summary:
| Property | Description |
|---|---|
| What it does | Shares memory between threads without copying |
| Where it works | In Web Workers and in Node.js (worker_threads) |
| What it requires | Atomics for synchronisation |
| Security | Only with COOP and COEP enabled |
| Typical scenarios | Parallel computation, caching, rendering, WebAssembly |
| Equivalent in C and C++ | Shared memory between threads |
Common mistakes
- Writing to the shared array with plain assignment from several threads.
sharedArray[0]++is not atomic, and two threads will lose an increment. UseAtomics.add(). - Forgetting the COOP and COEP headers. Without cross-origin isolation the
SharedArrayBufferconstructor is simply unavailable and the code fails with aReferenceError. - Calling
Atomics.wait()on the main thread. It blocks the thread, so it is forbidden on the browser's main thread; it is for workers only. - Expecting
postMessageto copy the buffer. What is passed here is the shared memory itself, so a change in one thread is instantly visible in the other. - Reaching for
SharedArrayBufferwherepostMessageis enough. For occasional small messages, structured cloning is simpler and safer. - Ignoring the element type.
Atomicsonly works with integer typed arrays such asInt32ArrayorBigInt64Array, not withFloat64Array.
Short Answer
Interview readyA concise answer to help you respond confidently on this topic during an interview.