Skip to content

SAB Communication

DownDraft uses multiple SharedArrayBuffer (SAB) channels — typed RecordReader/RecordWriter, SlotBuffer, and SimBufferReader/SimBufferWriter pairs (core/src/sab/) — for zero-copy, high-frequency communication between the sim worker and renderer.

BoundaryMechanismUse case
Sim → RenderMultiple SharedArrayBuffers + AtomicsTransforms, water, terrain, physics, audio positions (60fps, zero-copy)
Game ↔ HostDirect function calls (HostAPI)Saves, screenshots, window state, dialogs, restart
Host ↔ SimpostMessage + multiple SABsCommands/events (postMessage), state (SAB channels via sequence counters)
Host ↔ Service workerspostMessage / MessageChannelSave worker, task pool, UI raster workers, plugin workers
Cross-thread syncAtomics on SAB headersFrame synchronization, seqlock read/write coordination

Each SAB channel carries a sequence counter in its header, bumped with Atomics when the writer publishes new data:

  • Change detection — Readers poll hasChanged(lastSeen) and skip the channel entirely when the counter is unchanged
  • Lock-free — No mutexes, no blocking, no allocation
  • Seqlock reads — Where a reader copies live data (e.g. the html-ui zero-copy pixel path), the counter doubles as a seqlock: an odd value means a write is in flight, and the reader rechecks the counter after copying to detect a torn read

Each channel has its own independent sequence counter, enabling fine-grained frame synchronization:

ChannelData
TransformEntity positions, rotations, scales
InputKeyboard, mouse, gamepad state (host → sim)
PhysicsPhysics body transforms
Audio PositionSpatial audio source positions
WaterWater height field, dynamics
TerrainTerrain height field, deformation

If only transforms changed, water and terrain reads are skipped entirely.

For SAB-backed data, Atomics.store / Atomics.load on the SAB header’s sequence counter serves as the cross-thread change signal. The render thread polls the counter; if unchanged, it skips reading that channel.

Planned: Atomics.wait / Atomics.notify for blocking sync when low-latency is needed.

The SAB sequence counter integrates with the ECS change detection system. A system querying Changed(Transform) checks lastChanged >= lastReadTick — a single integer comparison. For SAB-backed data, the sequence counter serves the same role across threads.