fugu.estate
SIGNAL OUT / FIELD SYSTEMS
/////////////
>>>> SIGNAL OUT / FIELD SYSTEMS
DIV. 01
<< BACK
video / embedded MANDLEROT
UNIT >> mandleROT / 2026
mandleROT
video embedded
Rust · GLSL · Raspberry Pi · OpenGL ES
>> REPO

mandleROT is a VJ tool for Raspberry Pi composite video output. A single Rust binary drives a library of 58 GLSL fragment shaders — fractals, audio scopes, demoscene effects, CRT simulations — with live hot-reload, a two-layer A/B mixer with crossfade, and audio reactivity from a USB microphone. A secondary SPI display shows the operator's state while the Pi's composite output feeds the venue projection.

The Problem

VJ software generally runs on laptops with high-performance GPUs. mandleROT targets the opposite: a $45 Raspberry Pi with a composite video output, specifically to feed vintage monitors and CRT projectors for a low-resolution analog aesthetic. The constraint of composite video (720×480 at 50/60 Hz) becomes the design rather than a limitation to work around.

Key Decisions

GLSL hot-reload from disk. Fragment shaders are watched via inotify. When a scene file changes, mandleROT recompiles it and swaps it in within ~250ms while the other layer continues running. If compilation fails, the layer falls back to a safe SMPTE test pattern. This makes shader authoring interactive: edit a .glsl file in one terminal, watch it update on screen.

Audio pipeline: USB mic to four frequency bands. Capture runs at 1024-sample windows (~100 Hz update rate). FFT bins are log-grouped into four bands (bass, lo-mid, hi-mid, treble) with per-band attack/release envelopes and P95 auto-gain to adapt to room acoustics. Beat detection runs on the spectral magnitude. Scenes consume audio via standard uniforms: u_audio.x through u_audio.w, plus u_beat and u_bpm.

SPI TFT as operator panel. A 480×320 amber-phosphor SPI display shows mode, active layer, scene names, eight parameter readouts per layer, audio band levels, crossfade position, and Look slots 1–8. The panel renders into a CPU framebuffer and pushes to SPI while the GPU handles composite output independently. In desktop dev mode it dumps a PNG snapshot each frame instead.

Looks: saving full A/B state. A Look captures both layers' scenes, crossfade position, blend mode, audio bypass, and full parameter maps for both layers — keyed by parameter name rather than slot index, so they survive scene refactors. Up to eight Looks stored as JSON. A VJ can pre-program a set of positions and recall them live with a single keypress.

Modal keyboard controls. Tab cycles SCENE → PARAM → LOOK modes. All controls are reachable from a standard keyboard or a USB numpad (with NumLock as a sticky modifier). A double-tap Esc or a specific numpad chord triggers a PANIC: both layers reset to safe-scene, audio bypass on. Designed for reliable one-hand operation in the dark.

Architecture

mandleROT runs as a single binary with two rendering threads sharing a framebuffer via a channel. The main loop handles input events and mode transitions. A separate audio thread owns the USB capture device and writes to a shared ring buffer of band values that the render thread reads each frame. The SPI panel thread runs independently — an operator display failure does not affect the composite output.

The scene library is organized by aesthetic: fractals, gritty/digital, demoscene classics, audio scopes, geometric/vector, organic, and experimental. Each scene is a .glsl file paired with a .toml metadata file declaring its display name, eight parameter slots, and audio routing.

Status / What's Next

58 scenes ship. Active development is on composite video input (feeding a v4l2 USB capture as a live layer texture), additional blend modes (Overlay, Dodge/Burn, HSL family), and a post-FX pipeline (bloom, vignette, CRT overlay, LUT grading, dither).