Definitive guide to multi-threaded rendering on the Web
A typical software engineer deals daily with threads, processes, synchronization, race conditions, context sharing etc. A typical frontend engineer does not, but to build modern scalable interactive apps, one should.
Why do multi threaded rendering ?
The DOM is single threaded (still is, and might be forever). But we want to do more with it. Here are some cases where a single thread starts becoming a bottleneck:
- Heavy data visualizations, dashboards with multiple visualizations
- Apps with complex and sophisticated interaction patterns
- Interactive infographics
- Physics simulations
- Low powered devices
What are my options?
Multi threading on the web can be classified into four broad categories:
- Compute only
- Prioritized scheduling
- Parallelized create DOM
- Parallelized create and mutate
- Canvas
- DOM
Compute Only
This is the traditional Web worker model. Compute on the client can be distributed to multiple Web Workers.
Implementations:
- Web Workers and Friends (Shared worker, Service worker)
- AudioWorklet: Run audio processing in a separate thread
- React Worker DOM (Virtual DOM computes in a separate thread)
Prioritized scheduling
Work is rescheduled as per priority, giving a sense of a responsive application. Still uses a single thread.
Implementations:
Parallelized create DOM
- Single compute thread (main thread)
- The initial render load is shared by multiple workers.
PS: The worker is generally a server side process.
Implementations:
- Facebook: Bigpipe
- Ebay: Async Fragments
Parallelized create and Mutate (Canvas)
With the new Offscreen canvas API (widely available since March 2023), you can create and control a canvas from a Worker. This brings us within striking distance from our goal, true multi-threaded rendering.
Implementations:
- Offscreen Canvas with transferControlToOffscreen.
- ChartJS Parallel rendering
Parallelized create and mutate (DOM)
The DOM is both created and mutated by separate workers. There are two approaches which make this possible, and we will talk about the current implementations for each.
Web Worker w/ DOM
Worker DOM library implemented DOM within web workers, all the mutations are done within the worker and then periodically synced with the Main DOM. Checkout these slides for more details on how this works under the hood.
Parallel DOM via cross origin SubFrames
With the release of performance isolation in Chrome 88, its now possible to have multiple subframes on a webpage which might be running in a separate process. The PDoM library tries to exploit this capability by providing an ergonomic abstraction, for web developers to use.
That’s all folks!
What we didn’t talk about today is you could also use the above techniques in combination with one another. For eg, you could use the “Compute only worker threads” with “Parallelized create only” to achieve performance benefits beyond just the initial render.