ADR-002: Worker-First Execution Model

Status: Accepted Date: 2026-01-15

Context

Browser games that run on the main thread compete with DOM updates, input handling, and browser UI for CPU time. Heavy game logic or rendering work causes frame drops and unresponsive UI. We need an execution model that keeps the main thread light and allows the engine to run at full performance.

Options Considered

1. Worker thread with OffscreenCanvas (chosen)

  • Game logic and rendering run in a Web Worker
  • Canvas rendering is transferred to the worker via OffscreenCanvas
  • Main thread handles only input capture and lightweight UI overlays
  • wgpu can render directly on the OffscreenCanvas from the worker

2. Main thread only

  • Simplest approach
  • All rendering and logic compete with DOM
  • No way to avoid jank from long frames
  • No path to multithreading

3. Worker for logic only, main thread renders

  • Logic offloaded but rendering still blocks main thread
  • Requires serializing render commands across threads
  • Adds latency between state computation and display

Decision

Adopt a worker-first execution model where the WASM game module runs entirely in a Web Worker. The HTML page transfers an OffscreenCanvas to the worker at startup. Input events are forwarded from the main thread to the worker via postMessage.

Consequences

Positive

  • Main thread stays responsive — no jank from game logic
  • Full CPU core dedicated to the game loop
  • Path to SharedArrayBuffer threading when cross-origin isolation is enabled
  • Clean separation between browser shell (HTML/CSS) and engine runtime

Negative

  • OffscreenCanvas is not supported in all browsers (Safari added support in 16.4)
  • postMessage has serialization overhead for input events
  • Debugging is more complex — DevTools worker support varies
  • Initial setup is more complex than direct canvas rendering

Mitigations

  • Fall back to main-thread rendering when OffscreenCanvas is unavailable
  • Input events are small and infrequent — postMessage overhead is negligible
  • Provide dev mode that runs on main thread for easier debugging
  • Document debugging workflow for worker-based execution

Architecture

Main Thread                           Worker Thread
┌──────────────────────┐             ┌──────────────────────┐
│ HTML Shell           │  transfer   │ WASM Module          │
│ ├─ <canvas>   ──────────────────→  │ ├─ sg_app            │
│ ├─ Input capture     │  postMsg    │ ├─ sg_scene           │
│ │   keydown/up  ──────────────────→│ ├─ sg_render          │
│ │   mouse/touch ──────────────────→│ └─ OffscreenCanvas    │
│ └─ Overlay UI        │             │     └─ wgpu surface   │
└──────────────────────┘             └──────────────────────┘

References