Skip to main content

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

  • SharedArrayBuffer holds raw bytes and is visible to several threads at once.
  • On postMessage it 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 Atomics is needed.
  • Atomics.wait() and Atomics.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

javascript
// 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)); // 1

Differences from a plain ArrayBuffer

PropertyArrayBufferSharedArrayBuffer
Who uses itA single threadSeveral threads
Passing it to a workerCopied, or moved with transfer and then unavailable in the original threadShared, both threads see the same memory
Can Atomics be usedNoYes, for safe synchronisation
Thread safetyNot requiredRequired, through Atomics

A shared buffer between threads

The main thread (main.js):

javascript
// 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):

javascript
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:

text
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:

MethodWhat 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():

javascript
// 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);
javascript
// 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

ScenarioApplication
Web WorkersPassing large data without copying
Multithreaded computationParallel processing of matrices, graphics, physics
Games and simulationsUpdating world state across several threads
Machine learning and WASMTensorFlow.js, WebAssembly and OpenCV use SharedArrayBuffer
Video and audio processingSharing buffers between threads
A bus between workersInstant 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:

text
Cross-Origin-Opener-Policy: same-origin Cross-Origin-Embedder-Policy: require-corp

And 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:

javascript
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:

PropertyDescription
What it doesShares memory between threads without copying
Where it worksIn Web Workers and in Node.js (worker_threads)
What it requiresAtomics for synchronisation
SecurityOnly with COOP and COEP enabled
Typical scenariosParallel 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. Use Atomics.add().
  • Forgetting the COOP and COEP headers. Without cross-origin isolation the SharedArrayBuffer constructor is simply unavailable and the code fails with a ReferenceError.
  • 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 postMessage to 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 SharedArrayBuffer where postMessage is enough. For occasional small messages, structured cloning is simpler and safer.
  • Ignoring the element type. Atomics only works with integer typed arrays such as Int32Array or BigInt64Array, not with Float64Array.

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.