A show-control media server for LED, IMAG and monitors.
Timecode-locked timeline or a lighting console drives it. Layers land on outputs as rectangles on one canvas. No performance controls, no projection stack.
- Scale
- Mid-scale; sixteen 1080p HAP layers headless above 100 fps on an M-series Mac
- Platforms
- macOS on Apple Silicon (Metal); Windows 11 with a discrete GPU (DirectX 12)
- Codecs
- HAP, HAP Alpha, HAP Q in QuickTime; ProRes, H.264 and DXV3 to come
- Control
- Timeline and cues, LTC, Art-Net, sACN, OSC, the shell
- Outputs
- Displays, windowed or full screen; NDI to come; SDI is input only
- Built in
- Rust on wgpu, egui, winit
Pipeline
Fixed per-layer stage order, no user-assembled chains. RGBA16 float, premultiplied, end to end. HAP frames go up as compressed textures and decode on the GPU. Each clip has a decode ring that reads ahead in the direction of play, so the first frame of every cue target is resident before the cue.
- Mappings
- A numbered bank of rectangle lists, any one applicable to any layer, by number from a console. Mapping 1 is Default: one rectangle over every output, rewritten as outputs change.
- Content canvas
- A mapping may carry its own canvas, 16 to 16384 px a side. The layer renders at that size; rectangles cut pieces of it to outputs. Position and scale then move the picture as one across a sliced wall, at one raster per layer (16 MB at 1080p, 64 MB at 4K).
- Regions
- Named areas in an output's pixels, LED panels or blend zones, for snapping and fitting rectangles. Layout aids only; the engine ignores them.
- Banks
- Media and generators as numbered folders of numbered tiles. A content folder is a bank; a three-digit prefix numbers a file, the rest are placed from 256. The Inputs bank is 250.
- Generators
- Built-in families (test patterns, colour, gradient) and ISF files in a folder: each file a tile, its header inputs the eight slots (float/long/bool 1, point2D 2, color 3). Compiled by naga; a bad shader is a line number, not a crash. Refused: persistent buffers, multi-pass, audio, transitions, extra image inputs.
- Live inputs
- Anything the OS sees as a camera, USB SDI/HDMI capture boxes included. Opened while a layer shows it, closed thirty seconds after.
- Clock
- One show rate; every time entered is in show frames. Clip native rates are never exposed.
Ownership
Each layer has one owner. Once a frame the arbiter reads it, takes the values from that owner's source and records the source beside the values, so the shell draws every value in its owner's colour. Changing owner is a command: shell, OSC, cue action, or the layer's Owner channel. No crossfade between owners. A global override forces every layer to one.
Timeline
Sections, tracks, cues. A section free-runs or chases timecode. A clip carries the whole layer record: source, play group (loop or hold at end, speed, in point), geometry, colour, mask, key, generator values, mapping preset, sweeps, fades. It is edited with the strip's controls and captured from them. Clips can follow a master clip, one level, without copying. Trimming a start keeps the in point on the same picture.
| Cue | Name | Time | Then | Actions |
|---|---|---|---|---|
| 1 | Take layers | 00:00:00:00 | continue | layers 1, 2 → timeline |
| 1.5 | Announce | 00:00:05:00 | continue | send /enzyme/cue/1.5 |
| 2 | Hold | 00:00:30:00 | hold until Go | — |
- Cue actions
- Take or release layers, send OSC, release timecode until the section ends.
- Timecode in
- LTC on any audio input, capture timestamps through to a PLL on the render thread: offset plus rate against host time, slewed on small error, relocated beyond about two frames. Lock states: no signal, acquiring, locked, freewheeling, held.
- Loss
- Timecode absent while chasing holds. Timecode present but outside any chased section hands the playhead to the timeline clock, so a chased song runs into a free-run section. Go frees a hold.
- Without hardware
--ltc test@01:00:00:00, with--ltc-dropout 6-9to rehearse a loss.
Console
Patched as fixtures: one block per layer, plus Show and Transport. Every strip parameter has a channel in physical units. The personality is data in the show file, exports as GDTF, and is versioned: a patch laid under an older version is laid again on load.
| Fixture | Univ | Addr | Ch | Carries |
|---|---|---|---|---|
| Show | 1 | 1 | 16 | master, blackout, mode, override |
| Transport | 1 | 33 | 16 | play, pause, go, hold, locate, cue |
| Layer 1 | 1 | 65 | 144 | owner · folder/file · play mode · speed · position · in/out · transition · geometry · mapping preset · opacity · colour · mask · key · generator 1–8 · effect select · effect 1–8 |
| Layer 2 | 1 | 209 | 144 | |
| Layer 3 | 1 | 353 | 144 |
Engine and shell
Two processes. The engine owns outputs, clock, decoders, receivers and the show; the shell is a client over a TCP socket, local or remote, one JSON document per line each way. Closing a shell never stops the outputs. Several shells can hold one engine; a laptop can hold an engine at the wall.
Engine
- Loop paced by presentation at the display rate; 240 Hz cap when nothing presents.
- One window per output;
Ffills the display it is on, Escape returns. - Full status on slow changes; per-tick part (transport, resolved layers, counters) 30×/s locally, 10×/s remote.
- Output and tile pictures as JPEG, about 8 KB each, at each client's rate.
- Show file written beside the target and renamed over it.
- Edit and Show modes enforced in the core, not the UI.
Shell
- Six workspaces on 1–6: Outputs, Timeline, Media, Layers, Control, Setup. Show mode is one screen: Go, transport, cues, ownership, output pictures, warnings.
- Every value in its owner's colour; the manual value editable beneath.
- A drag is one undo step; a change log names every edit and its client.
- Copy/paste of layer settings as plain text, by group.
- Remote: file and folder pickers browse the engine's disk.
- Shortcuts read the physical key, so any keyboard layout.
I/O
- Art-Net, sACN
- In, any interface or one bound. ArtPoll answered. Test sender ships.
- OSC
- In on UDP 9000. Every command has an address:
/transport/go,/cue/go 12.5,/locate 01:00:00:00,/layer/1/owner dmx,/layer/1/opacity 0.5,/show/blackout 1./subscribegets status on change and once a second. Cue sends out to any host:port. - LTC
- In, any audio input. MTC, LTC out, MSC not yet.
- Video in
- Cameras and capture devices now; DeckLink SDI next. SDI is never an output.
- Video out
- Displays. NDI later. The processor or broadcast chain takes the display feed.
- Wire
nc host 9200shows the status stream; any command the shell sends, a script can.
Built and not built
Built: everything above.
Designed, not built: MTC in/out, LTC out, MIDI Show Control, DeckLink input, NDI in/out, ProRes and H.264, DXV3, blur, LUTs, output frame delay, next-cue preview, keyframe editing in the shell (the core evaluates automation already), multi-viewer, Syphon/Spout in and out.
Not in scope: warp, blend, mesh, corner pin, 3D stage, VJ controls, effect chains, clustering, recording, switching.