beat lab / signal
DUAL RECONCILIATION

One signal.
Two
transducers.

Your mouse writes frequency into shared memory. An AudioWorklet reads it and produces sound. A canvas reads it and draws the wave. Same state. Two outputs.

The ideal bridge is an SPSC ring buffer in a SharedArrayBuffer — zero-copy, lock-free. When cross-origin isolation headers aren't available, a postMessage fallback keeps the demo alive. Same architecture, different transport.

The AudioWorklet writes amplitude back to the same shared buffer. The canvas reads that too. Sound informs sight. One signal, flowing through two transducers.

ARCHITECTURE
SPSC RING BUFFER

Single-Producer Single-Consumer. The main thread writes frequency values. The AudioWorklet reads them. Atomic operations on write/read indices. No locks, no contention, no jitter. The buffer is 256 slots deep — enough to absorb timing variance between the 60fps render loop and the audio thread's 128-sample render quantum.

DUAL RECONCILIATION

Two reconcilers read the same state tree. The audio reconciler (AudioWorklet) converts frequency to oscillator output. The visual reconciler (Canvas 2D) converts frequency + amplitude to waveform + ring. They run on different threads at different rates. The SharedArrayBuffer keeps them in sync without coupling.

TWO TRANSPORTS

SharedArrayBuffer gives true zero-copy access — the write is visible on the next atomic read. No queue. No copy. No GC pressure. When SAB isn't available (missing COOP/COEP headers, some browsers), the system falls back to postMessage. Higher latency (~8ms overhead) but the architecture proof still holds. Same instrument. Different wire.

THE SIGNAL PATH

mouse X → freqFromX() → ring buffer write → AudioWorklet read → sine oscillator → amplitude calculation → shared buffer write → Canvas read → waveform visualization. One signal. No branching. No duplication. Each transducer takes what it needs from the same source.

SIGNAL CHAIN
mouse X
freqFromX()
SPSC write
AudioWorklet
sine osc
destination
Transport
SAB / postMessage
Ring capacity
256 slots
Frequency range
80 — 1200 Hz
Audio latency
~2.9ms (128/44100)