Skip to content

Chapter 32: WebAssembly, Workers, and Hardware Access

The platform isn’t only documents and applications. It’s also a runtime that, in the past decade, has expanded into territory that used to require native code.

WebAssembly. Web Workers. WebRTC. WebGL and WebGPU. WebHID, WebUSB, WebSerial, WebBluetooth, WebMIDI. The File System Access API and OPFS. Each of these gives the browser a capability the native vs. web dichotomy of the early 2010s assumed it would never have. Cumulatively, they make the browser a serious platform for software that goes well beyond the document-with-some-script model of frontend’s traditional scope.

This chapter walks through the major surfaces. The chapter is part inventory — what’s there and what each piece does — and part architectural argument — that the platform’s capabilities now overlap substantially with native development’s, and that the choice between web app and native app increasingly comes down to ergonomics rather than capability.

WebAssembly shipped in browsers in 2017 after several years of collaborative development across the major browser vendors. The shipping team included Lin Clark at Mozilla (whose Rust-Mozilla Code Cartoons series explained the technology to a generation of developers), the V8 team at Google, the JavaScriptCore team at Apple, and Microsoft’s Edge contributors.

The technical idea is that WebAssembly is a binary instruction format designed to be compiled from many languages (C, C++, Rust, Go, AssemblyScript, Zig, and increasingly others) and executed at near-native speed inside the browser’s JavaScript engine. WASM modules can import and export functions, share memory with JavaScript, and integrate with the rest of the web platform through a defined JavaScript API.

The architectural implications are substantial. Applications can now ship code written in any language to run on any browser. Image processing in C++. Cryptography in Rust. Game engines compiled from C. Scientific computing libraries compiled from Fortran. The browser becomes a portable execution environment for code that has nothing to do with web development as the field traditionally understood it.

The real-world adoption is significant. Figma’s design tool runs its core rendering logic in C++ compiled to WASM. Photopea (an in-browser Photoshop alternative) runs much of its image-manipulation code in WASM. The SQLite WASM build lets applications run a full SQLite database in the browser. Many cryptocurrency wallets use WASM for their cryptographic operations. AutoCAD’s web version. Adobe’s web versions of Photoshop and Illustrator. The desktop-software-on-the-web pattern Chapter 18 described is often delivered through WASM for the performance-critical parts.

For typical frontend application code, WASM is rarely the right tool. JavaScript handles UI work fine; the V8 engine’s optimizations make JavaScript fast enough for almost any UI workload. WASM earns its place for non-UI computation — math-heavy algorithms, codec work, cryptographic operations, large-data processing — where the JavaScript engine’s overhead matters.

The forward-looking case for WASM is broader. The WASI (WebAssembly System Interface) work, currently maturing, lets WASM modules run outside the browser too — on servers, in edge functions, as standalone executables. Cloudflare Workers, Fastly Compute@Edge, AWS Lambda’s WASM support, and the Bun runtime’s interest in WASM all point at a world where the same compiled binary runs in the browser, on the edge, and on a backend, with the same security model in each context.

For the platform-first argument, WASM matters because it expands what the platform means. The browser isn’t only a JavaScript runtime. It’s also a portable execution environment for compiled code, with a security model the application can rely on. The choice of language stops being constrained by the browser’s substrate.

JavaScript’s single-threaded execution model has, for most of its history, been one of the harder constraints to design around. CPU-intensive work blocks the UI thread; long-running calculations freeze the page; the only way to get responsiveness was to chop work into small pieces and yield between them.

Web Workers address this. A Worker is a separate JavaScript execution context that runs in parallel with the main thread. The main thread and the Worker communicate by message passing (postMessage / onmessage); they don’t share variables. The Worker can do arbitrary computation without affecting UI responsiveness.

// Main thread:
const worker = new Worker('/heavy-task.js')
worker.postMessage({ data: bigArray })
worker.onmessage = (event) => {
console.log('Result:', event.data)
}
// Worker (heavy-task.js):
self.onmessage = (event) => {
const result = processData(event.data.data)
self.postMessage(result)
}

Web Workers have been in browsers since 2009 (the original spec). They’re widely supported, well-understood, and underused. Most frontend code that does heavy work on the main thread could benefit from moving the work to a Worker — image processing, parsing large JSON, complex search/filter operations on big arrays, on-device machine-learning inference. The architectural lift is real (message-passing is more awkward than shared variables) but the responsiveness benefit is significant.

Service Workers are a specialized kind of Worker designed for network interception, caching, and offline support. Chapter 31 mentioned them in the privacy context; here they’re worth naming as part of the broader Workers story. A Service Worker sits between the application and the network, decides which requests to serve from cache and which to fetch fresh, and enables offline-capable applications. PWAs (Progressive Web Apps) rely on Service Workers for their offline functionality.

Shared Workers let multiple browser tabs share a single Worker — useful for cross-tab coordination of expensive resources (a shared WebSocket connection, a shared cache, a shared database connection).

The Worker family expands what JavaScript applications can do. The work the application has to do to use Workers well (structuring code as message-passing actors instead of shared-memory function calls) is real, and modern application architectures should account for it.

Workers communicate by message-passing, but for some workloads — high-performance gaming, audio processing, certain kinds of numerical computation — the message-passing overhead is itself the bottleneck. SharedArrayBuffer addresses this by letting multiple Workers share a single block of memory directly.

// Main thread:
const sab = new SharedArrayBuffer(1024)
const sharedArray = new Int32Array(sab)
worker.postMessage(sab) // Worker now has access to the same memory
// Both threads can read and write to sharedArray
Atomics.store(sharedArray, 0, 42)
Atomics.add(sharedArray, 1, 100)

SharedArrayBuffer gives JavaScript real shared-memory multithreading, with Atomics providing the synchronization primitives (atomic reads, writes, compare-and-swap, wait/notify) that thread-safe code needs.

The security implications are substantial. Spectre and similar speculative-execution vulnerabilities became more exploitable when JavaScript code had access to high-precision timing and shared memory. The platform’s response was to gate SharedArrayBuffer behind cross-origin isolation (Chapter 31’s COOP/COEP requirement). A page has to opt into the strictest isolation mode before it gets access to shared memory.

For applications that need this — game engines like Unity’s WebGL builds, scientific computing tools, video editors, real-time audio processing tools — the isolation overhead is worth paying. For everything else, message-passing through normal Workers is the right model.

WebRTC (Web Real-Time Communication) gives browsers the ability to communicate directly with each other, without an intermediate server, for audio, video, and data.

The technology landed in 2011, championed by Justin Uberti at Google with Mozilla and Apple eventually joining. The deployment story is, even fifteen years later, complex — WebRTC’s architecture includes signaling servers (to coordinate the initial connection), STUN servers (to discover network topology), TURN servers (to relay traffic when peer-to-peer fails), and a substantial protocol stack underneath (SDP, ICE, DTLS, SRTP). The application has to coordinate all of this to get two browsers talking to each other.

The use cases are significant. Video conferencing (Google Meet, Zoom’s web client, Microsoft Teams web), real-time collaboration (Figma’s multiplayer cursor, Notion’s real-time editing — though these often use server-relayed protocols rather than peer-to-peer), in-browser games, peer-to-peer file transfer (WebTorrent, file.pizza), and many others.

WebRTC’s data channel — an arbitrary-message peer-to-peer channel separate from the audio/video media tracks — is particularly underused. The data channel lets two browsers exchange JSON, binary blobs, or any other application data without a server in the middle. For real-time multiplayer applications, peer-to-peer file sharing, and any use case where server bandwidth costs matter, the data channel is a serious capability.

The complexity is real. Most applications that need real-time communication don’t build directly on WebRTC; they use a hosted service (Daily, LiveKit, Twilio, Agora) that wraps WebRTC and handles the signaling, NAT traversal, and relay infrastructure. For the platform-first argument, WebRTC matters because real-time peer-to-peer is a platform capability — it just requires substantial wrapping to be usable.

The platform’s GPU access story is significant.

WebGL (2011) gave browsers an OpenGL ES 2.0-compatible API for GPU rendering. WebGL 2 (2017) added support for OpenGL ES 3.0 features. The API has been the foundation of in-browser 3D rendering for over a decade — games, data visualization (D3’s WebGL variants, Three.js, Babylon.js), CAD tools, scientific visualization, generative art.

WebGPU (shipping across browsers through 2023–2024) is the next-generation replacement. The API is modeled on modern graphics APIs (Vulkan, Metal, Direct3D 12) rather than OpenGL, and gives applications much closer control over the GPU’s capabilities — compute shaders, parallel data processing, modern rendering pipelines. WebGPU enables a class of GPU-accelerated work (machine learning inference, large-scale data processing, advanced 3D rendering) that WebGL couldn’t.

The architectural implication is that the browser is now a serious platform for graphics-intensive work. Adobe’s web versions of Photoshop and Illustrator use GPU acceleration heavily. Figma’s renderer is GPU-accelerated. Web-based ML inference tools (Transformers.js, ONNX Runtime Web) increasingly use WebGPU for performance. The categories of application that had to be native because GPU access was required are increasingly viable on the web.

For most frontend applications, neither WebGL nor WebGPU is relevant — UI work doesn’t typically need GPU compute. For the applications that do, the platform’s capabilities are competitive with native equivalents.

Google’s Project Fugu — formally the Web Capabilities Project — has been adding hardware-access APIs to Chromium since 2018. Several have landed in the standards process and reached cross-browser support (though Safari’s adoption of some is intentionally limited):

WebHID lets the browser communicate with USB HID devices (game controllers, custom input devices, scanners, specialty hardware).

WebUSB lets the browser talk to USB devices generally, with explicit user permission per device.

WebSerial lets the browser communicate with serial-port devices (industrial equipment, Arduino-style microcontrollers, scientific instruments).

WebBluetooth lets the browser talk to Bluetooth Low Energy devices (heart rate monitors, fitness trackers, smart-home devices).

WebMIDI lets the browser communicate with MIDI devices (musical keyboards, controllers, synthesizers).

WebNFC (limited to Android Chrome currently) lets the browser read NFC tags.

The list keeps growing. Each API requires explicit user permission for each device, which provides a security boundary against random web pages accessing arbitrary hardware. The combined surface lets browser-based applications interact with the physical world in ways that used to require native applications and platform-specific code.

The adoption is uneven across browsers. Chrome (and Chromium-based browsers) ship most of the Fugu APIs. Firefox ships some. Safari has been more conservative, often declining to ship APIs Apple considers privacy-concerning. The cross-browser support story for any given hardware-access API has to be checked individually.

For most applications, hardware access isn’t relevant. For specific application categories — music production tools, IoT dashboards, industrial control interfaces, scientific instrumentation, game development — the Fugu APIs make web-based development viable in domains that were previously native-only.

A specific platform capability worth naming.

The File System Access API (Chrome, behind a permission prompt) lets web applications read and write files in the user’s actual file system, with the user’s explicit consent. The user opens a file picker; the application gets a FileSystemFileHandle representing the chosen file; the application can read and write that file repeatedly across sessions (the handle persists if the application stores it in IndexedDB).

This is significant. Before File System Access, file uploads were one-way — the application could receive files but couldn’t modify them in place. With File System Access, applications can edit files directly. VSCode’s web version uses this. The various web-based design tools use it. Document editors use it.

Safari and Firefox haven’t shipped the full File System Access API yet, citing privacy concerns. The cross-browser story is patchy.

OPFS (Origin Private File System, mentioned in Chapter 25) is the cross-browser cousin. OPFS provides a hidden file system scoped to the origin — the application can write files to a private virtual file system that only it can access, with much higher performance than IndexedDB for file-like workloads. SQLite-in-the-browser (with WASM) uses OPFS heavily.

The platform now has actual file-system-style storage available, in addition to the structured-data storage of IndexedDB and the simpler key-value storage of localStorage. The full hierarchy from Chapter 25 — memory, sessionStorage, localStorage, IndexedDB, OPFS, the Cache API — covers most application storage needs.

What This Means for the Platform-First Argument

Section titled “What This Means for the Platform-First Argument”

The accumulation of capabilities described in this chapter has a specific implication for the book’s broader argument.

The platform is competitive with native development for a substantially expanded range of use cases. Game engines run in the browser via WebGL/WebGPU and WASM. Music production tools run via WebMIDI, Web Audio, and WASM. Photo editors run via WebGL and WASM. Scientific instruments are accessed via WebUSB and WebSerial. Real-time video conferencing runs via WebRTC. Document editors edit files directly via File System Access. The line between web app and native app has shifted dramatically over the past decade.

Chapter 18 made the case that the desktop software industry has voted for web technologies by shipping their products on Electron. This chapter makes the more aggressive case that the platform’s capabilities, accessed directly inside a normal browser, are increasingly sufficient for the same workloads — without the Electron wrapper, without the 200 MB binary, without the parallel reality.

For most frontend application code, this isn’t directly relevant. The application doesn’t need WebGPU or WebMIDI or WebSerial. The architectural point is that the platform can do far more than the field’s typical assumption. When a use case appears that previously would have demanded a native application, the question worth asking first is can the platform do this now? The answer in 2025 is yes substantially more often than the answer in 2015 was.

Kitsune’s architecture stays focused on the common case — applications that are mostly UI, mostly data, mostly interaction. The capabilities in this chapter are available when they’re needed, but they aren’t the architecture’s center. The point of the chapter is that they’re there. The platform is no longer the limited substrate it was once treated as.

The next chapter takes the most argumentative position the book has built toward — the ergonomics question. The honest case that platform-first development is more verbose than framework development, and the four-part response the book’s argument has been preparing. The decoration-versus-replacement principle gets its formal naming. The altitude-versus-all-or-nothing framing gets its full defense. After Chapter 33, Part II is complete and Part III begins drawing architectural principles from the platform substrate.

Pick one capability in this chapter you haven’t used before. WebGL, WebRTC’s data channel, a Web Worker, a Fugu hardware API, OPFS, WebAssembly, WebGPU — whatever’s furthest from your current experience.

Build a small thing that uses it. Not a production feature; a demonstration. A Web Worker that sorts a large array off the main thread. A WebRTC data channel between two browser tabs on your machine. A WebGL canvas that renders a rotating cube. A WebMIDI listener that prints messages from a MIDI controller. A WASM module compiled from Rust or AssemblyScript that does some math.

After it works, reflect:

  1. How long did this take, end to end?
  2. How much of the code was understanding the API surface, versus actually using it?
  3. What surprised you about the capability?
  4. Would this have been easier in a native development environment? More portable? Faster to ship?
  5. If your application had a use case for this, would you reach for it now? Or assume the platform couldn’t handle it?

The point is to feel how far the platform extends. Most developers have an outdated map of what the browser can do, formed when WebGL was the bleeding edge and WebAssembly didn’t exist. The current map is much bigger than the inherited one. Knowing what’s there is the first step in using it.