open atlas
↑ Back to track
Browser & Frontend Runtime WEB · 04 · 02

Web worker mechanics: dedicated, shared, and OffscreenCanvas

Dedicated vs shared vs module workers, the OffscreenCanvas escape hatch, and why the worker script URL must be same-origin.

WEB Middle ◷ 14 min
Level
FoundationsJuniorMiddleSenior

You know workers exist. But new Worker('script.js') spawns a dedicated thread that dies with the tab — so what do you use when five tabs need the same WebSocket connection, or when WebGL rendering should survive main-thread saturation?

Dedicated workers

new Worker('worker.js') spins up a fresh JS realm on its own OS thread, with its own event loop, its own global (DedicatedWorkerGlobalScope), and no access to document, window, or the DOM. It can use fetch, IndexedDB, WebSocket, OffscreenCanvas, timers, and importScripts/ES modules. Communication is exclusively postMessage in both directions.

A worker is tied to the page that created it: close the tab and the worker dies. Use it for anything CPU-bound that would otherwise blow the 50 ms task budget — JSON parsing of large payloads, image filters, cryptographic hashing, diffing, compression, syntax highlighting, physics.

The same-origin constraint. The worker script URL must be same-origin (or a blob URL). You cannot load a worker straight from a third-party CDN without proxying it through your own origin first. This is the same origin boundary that protects the rest of the browser’s security model.

Shared workers

new SharedWorker('s.js') creates a worker that is shared across all tabs of the same origin. Each tab connects through a MessagePort, and the worker lives as long as any tab uses it — useful for one shared WebSocket connection or a coherent cross-tab cache. Support is weaker than dedicated workers (absent in some browser configurations) and debugging is harder, because the worker’s lifetime is decoupled from any single tab.

Module workers

If you are starting a new project today, the choice of how a worker loads its dependencies matters for bundle size and future maintainability. The { type: 'module' } option changes that loading strategy:

  • Classic worker — uses importScripts() to load dependencies synchronously.
  • Module worker — uses ordinary static import. This gives tree-shaking, top-level await, and the same module graph as the rest of the app.

Module workers are the modern default. Classic importScripts remains only for legacy code and cases that build the script name dynamically at runtime.

Worker variant comparison
Dedicated worker lifetime
Tied to creating page
Shared worker lifetime
Last subscribing tab
Module worker
{ type: 'module' } option
Worker startup overhead
5–20 ms + script parse
Max pool size (rule of thumb)
hardwareConcurrency − 1

OffscreenCanvas: the one DOM carve-out

A worker cannot touch the DOM, but canvas is a special case — a canvas bitmap is not part of the DOM tree, it is just a pixel buffer. OffscreenCanvas is the canvas decoupled from a <canvas> element.

The pattern:

  1. Main thread calls canvas.transferControlToOffscreen(), gets an OffscreenCanvas.
  2. Transfers it to a worker through postMessage’s transfer list.
  3. Worker gets a 2D or WebGL context and renders entirely on its own thread.
  4. The browser reflects the result into the visible <canvas> — with no main-thread involvement.

This makes smooth 60 fps WebGL visualisations, map rendering, and games possible even while the main thread is saturated.

A worker can also create a standalone new OffscreenCanvas(w, h) purely for off-screen rendering — texture generation, image processing — and return the result as a transferable ImageBitmap.

The exclusivity constraint. An OffscreenCanvas whose control was transferred cannot also be drawn from the main thread — ownership is exclusive, exactly like a transferred ArrayBuffer. If both threads need to draw, you need two canvases or a different architecture; the platform deliberately forbids two threads racing on one bitmap.

Quiz

Five browser tabs from the same origin each need a single shared WebSocket connection managed in one place. Which worker type fits?

Quiz

After `canvas.transferControlToOffscreen()`, the main thread tries to call `ctx.fillRect(0,0,100,100)`. What happens?

Complete the analogy

A worker that is shared across all tabs of the same origin — each tab connects through a separate port — is called what?

Why this works

Why does OffscreenCanvas exist if workers cannot touch the DOM? Canvas is special because its output is a flat bitmap — it has no DOM events, no CSS, no accessibility tree. The browser’s rendering pipeline reads the bitmap and composites it; nothing else in the DOM tree cares who wrote the pixels. This makes it possible to safely hand pixel-writing to any thread. Generalising this to all DOM would require locking the entire tree — which is exactly what the worker model avoids.

Recall before you leave
  1. 01
    What are the three worker variants and when do you use each?
  2. 02
    Why must a worker script be same-origin (or a blob URL)?
  3. 03
    After calling canvas.transferControlToOffscreen(), can the main thread still draw to that canvas?
Recap

A dedicated worker is a fresh JS realm, event loop, and OS thread tied to the page that created it — it dies when the page closes. A shared worker spans all tabs of an origin via MessagePort connections, surviving as long as any tab uses it. Module workers use ES imports instead of the legacy importScripts. OffscreenCanvas is the one way workers can render: transfer a canvas’s control to a worker and it draws 2D or WebGL on its own thread, with the browser compositing the result — but ownership is exclusive and transfer is permanent. Now when you reach for a worker, you know which variant fits: dedicated for per-tab compute, shared for cross-tab state, module style for the modern import graph, and OffscreenCanvas when the bottleneck is GPU rendering itself.

Practice

Start at the top. Tasks go easiest → hardest: recall a fact, apply it to a case, then a senior-level stretch. Open one, attempt it, then reveal.

recallapplystretch0 of 5 done
Connected lessons
appears again in41

Something unclear?

Ask a question about this lesson. Questions are anonymous and go straight to the author to make the lesson better.

shortcuts expand
search
K
prev piece
k
next piece
j
cycle tier
t
this menu
?
sources3
expand
  1. 01
  2. 02
  3. 03

Trademarks belong to their respective owners. Editorial reference only.