huff processes video through a sequential chain of effect passes each frame. Understanding the order helps predict how parameter changes interact.
Effect Pipeline
SOURCE (video / webcam)
↓
[1] SCANLINE BANDS — horizontal displacement from ring
[2] FLOW WARP — noise-field UV distortion
[3] GLITCH / DATAMOSH — tile displacement from ring buffer
[4] FEEDBACK — previous frame zoomed/rotated back
[5] SYMMETRY — axis mirror
[6] SOLARISE — luminance-threshold inversion
[7] TRAILS — ghost frame blend
[8] BASE VIDEO MIX — raw source composited on top
↓
RING BUFFER UPDATE — frame written to ring for next pass
↓
OUTPUT → WebSocket relay → Canvas / Syphon / Spout
Each pass is independently toggleable. Disabled passes are completely skipped — no bleed-through. The ring is updated at the end of each frame — passes always read the previous frame's ring state, never the one being assembled.
UI → Effect Data Flow
All parameter control — sliders, MIDI CC, OSC messages — converges on the same DOM element values. The p5.js draw loop reads these values directly on every frame.
No debounce, no interpolation layer, no intermediate state object. Moving a slider and receiving a MIDI CC produce identical results — both write the same DOM element. There is no latency between a MIDI CC and it affecting the output; the next draw() frame picks it up.
Frame Ring Buffer
The ring buffer stores raw ImageData objects — RGBA pixel arrays at canvas resolution. It is the shared memory between passes.
| Parameter | Effect on ring |
| QUALITY | Controls ring depth: 0 → 4 frames, 1 → 60 frames |
| DEPTH | How far back effects sample into the ring (0–50% of ring depth) |
| DEPTH SCATTER | Per-tile randomisation of the back index |
| 192 MB cap | Ring depth also capped by pixel budget: at 1080p (~8 MB/frame) max ≈ 23 frames regardless of quality |
| Resolution | Bytes/frame | Max frames (quality=1) |
| 720p (1280×720) | ~3.7 MB | 51 frames |
| 1080p (1920×1080) | ~8.3 MB | 23 frames |
| 1440p (2560×1440) | ~14.7 MB | 13 frames |
| 4K (3840×2160) | ~33.2 MB | 5 frames |
720p or 1080p is the practical sweet spot. At 4K the ring holds only 5 frames — DEPTH and temporal effects lose most of their range.
Frame Output Pipeline
At the end of each draw() call, huff routes the composited output frame to one or more destinations. Syphon and Spout use a magic-byte prefix on the same WebSocket connection as the canvas mirror — Rust inspects the first bytes and routes accordingly.
canvas.getImageData()
→ raw RGBA bytes
→ magic prefix (HUFFSYPH / HUFFSPOUT) + dimensions + pixels
→ binary WebSocket to port 8787
→ Rust relay inspects prefix
HUFFSYPH → syphon::push_frame() → MTLTexture → Syphon clients
HUFFSPOUT → spout::push_frame() → D3D11 texture → Spout receivers
(none) → forwarded to canvas role clients as JPEG frames