Enzyme working title
Prototype, September 2026

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
EDIT·PC Demo|00:00:34:21 PLAY Demo|cue 2 next -|LTC 01:00:06:23 Locked 25 fps|DMX U1 U2|OSC 0|Programme Monitor 120 fps decode 9|⚠ 0
The status bar, always on screen: mode, show, transport, cues, timecode lock, DMX per universe, OSC subscribers, outputs, decode load, warnings.

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.

Sources into a layer, the layer's fixed stages, the mapping onto the canvas and outputs SOURCE Media bank · clip Generator bank · tile Live input LAYER transitioncolourgeometrymask keyblureffectblend generator with media = media as inputImage effect = a generator tile run on the layer's own picture CANVAS · OUTPUTS Programme 1920×1080 @ 0,0 Monitor @ 1920,0 mapping rectanglecrop · place · ¼-turns · flip · opacity rectangles live on the canvas, not on an output; one can cross two outputs
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.
Outputs: the two outputs on the canvas with their live pictures, each layer’s rectangle in its owner’s colour; output settings on the left, the mapping bank on the right.
Outputs: the two outputs on the canvas with their live pictures, each layer’s rectangle in its owner’s colour; output settings on the left, the mapping bank on the right.
Media: banks down the left, numbered tiles with their pictures. The same view serves generators; the Inputs bank is 250.
Media: banks down the left, numbered tiles with their pictures. The same view serves generators; the Inputs bank is 250.

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.

TimelineThe clip under the playhead. Taken and given back only by cue actions.
DMXThe layer's channel block, decoded through the personality. The default.
ManualThe strip, or OSC. Editable while another owner drives.
Layers: one strip per layer, its border and chip in the owner’s colour. Layers 1 and 2 are the timeline’s, 3 and 4 the desk’s; the owner row at the foot takes a layer.
Layers: one strip per layer, its border and chip in the owner’s colour. Layers 1 and 2 are the timeline’s, 3 and 4 the desk’s; the owner row at the foot takes a layer.

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.

CueNameTimeThenActions
1Take layers00:00:00:00continuelayers 1, 2 → timeline
1.5Announce00:00:05:00continuesend /enzyme/cue/1.5
2Hold00:00:30:00hold 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-9 to rehearse a loss.
Timeline: the section bar, cue markers, a track per layer, the cue list on the right, and the selected clip’s record in the inspector below.
Timeline: the section bar, cue markers, a track per layer, the cue list on the right, and the selected clip’s record in the inspector below.

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.

FixtureUnivAddrChCarries
Show1116master, blackout, mode, override
Transport13316play, pause, go, hold, locate, cue
Layer 1165144owner · 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 21209144
Layer 31353144
Art-Net port address N is shown as universe N+1, matching sACN and a grandMA3 default. Snap values are option numbers: Play mode 2 = Loop, Owner 1 = DMX. Loss of DMX holds last values.
Control: the patch with what each fixture is receiving, timecode, the bound interface and ports, OSC subscribers and cue send destinations.
Control: the patch with what each fixture is receiving, timecode, the bound interface and ports, OSC subscribers and cue send destinations.

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; F fills 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.
Show mode: timecode and state, the layers and their owners, output pictures; Go, the cues and the master in the operator’s column; engine health on the right.
Show mode: timecode and state, the layers and their owners, output pictures; Go, the cues and the master in the operator’s column; engine health on the right.
Setup: show file and content folders, licence and machine id, log and diagnostics bundle, this machine’s settings, and the self-test list.
Setup: show file and content folders, licence and machine id, log and diagnostics bundle, this machine’s settings, and the self-test list.

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. /subscribe gets 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 9200 shows 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.