A SharedArrayBuffer Polyfill

Andrea Giammarchi
2 min readJul 2, 2024

--

Photo by Steve Johnson on Unsplash

That’s it, that’s the post, really … if you look for SharedArrayBuffer in search engines the only thing that comes out is related to issue with headers, COI, CORP, COEP, and all the acronymis nobody needing this primitive really cares about, so here I am telling you that I have managed to polyfill this primitive for all Desktop to Mobile browsers, and it’s published on both npm and GitHub under the name sabayon (SharedArrayBuffer always on).

… previously …

I have previously talked about the beauty and power of this primitive, but it’s only over last weekend that I’ve decided to nail down an ochestration that “just works(™️), one that wouldn’t compromise security at all, or that requires special headers that browsers’ vendors can’t even agree about, or need iframe credentialless attribute still in an implementation limbo when embedded foreign content is desired in websites that actually need Atomics and “SAB”.

Sabayon — the nitty gritty

Hopefully explained reasonably well in its GitHub’s README under the “How does it work?” detail, this module provides all related primitives to be used in either the main thread or the worker’s one.

A crypto secure unique identifier is used per each page or tab to ensure a safe communication across workers and main threads, and of course the Shared bit of the equation is a facade of an ArrayBuffer orchestration, but that’s fully transparent for this module’s users.

// Worker example
import {
Atomics,
Int32Array,
SharedArrayBuffer,
addEventListener,
postMessage,
ignore,
} from 'sabayon/worker';

addEventListener('message', event => {
const { handle, complex } = event.data;
handle[0] = 1;
Atomics.notify(handle, 0);
});

const sab = new SharedArrayBuffer(4);
const view = new Int32Array(sab);

postMessage({
handle: view,
passThrough: ignore({ complex: "data" })
});

Atomics.waitAsync(view, 0).value.then(_ => {
console.log('view changed', [...view]);
});

Atomics.wait(view, 0);
console.log('view changed', [...view]);

// Main example
import {
Atomics,
Int32Array,
SharedArrayBuffer,
Worker,
ignore,
} from 'sabayon/main';

const w = new Worker('./worker.js', {
type: 'module',
serviceWorker: './sw.js', // optional
});

w.addEventListener('message', event => {
const { handle, complex } = event.data;
handle[0] = 1;
Atomics.notify(handle, 0);
});

const sab = new SharedArrayBuffer(4);
const view = new Int32Array(sab);

postMessage({
handle: view,
passThrough: ignore({ complex: "data" })
});

Atomics.waitAsync(view, 0).value.then(_ => {
console.log('view changed', [...view]);
});

Because the module is actually not obtrusive at all, what you’d get from it, if the right headers are around, is the native, untouched deal, except for waitAsync that is automatically patched in Firefox, as it apparently didn’t get the memo, but if headers are not there, and solutions to enable those headers are not desired, you’ll get the whole thing just as if everyhing was fine to start with: a win-win situation 🥳

Enjoy the beauty of buffers based exchanges between workers and main thread without the hassle and/or implications these primitives have behind the Web scene 👋

--

--

Andrea Giammarchi

Web, Mobile, IoT, and all JS things since 00's. Formerly JS engineer at @nokia, @facebook, @twitter.