Lumen is a generative visual player and VJ engine for macOS and Windows. It renders real-time WebGL2 shader scenes, reacts to live audio, and can be driven by DMX/Art-Net and OSC from a lighting desk or show-control system.
This manual was generated directly from Lumen's source (index.html) rather than from hands-on use of the running app. Every parameter name, style name, OSC address, DMX channel mapping, and keyboard shortcut below is taken straight from the code. What it can't yet capture is the feel of using the app. Sections that couldn't be confirmed from the code are marked "to be documented" rather than guessed at. This needs a pass from someone who has actually run a show with it before it's finished.
Lumen is a generative visual player and VJ engine for macOS and Windows. It renders real-time WebGL2 shader scenes, reacts to live audio, and can be driven by DMX/Art-Net and OSC from a lighting desk or show-control system — making it usable both as a standalone visual instrument and as one more fixture in a larger lighting/AV rig.
The engine pipeline is: a generative scene shader (one of 19 selectable visual styles) renders into an offscreen buffer, which then passes through a feedback/FX post-processing chain (chromatic aberration, feedback trails, kaleidoscope, bloom, posterize), and the result is drawn to the main window and, optionally, a second output window for a projector or screen.
Lumen ships as a single self-contained HTML/JS/WebGL2 file wrapped in a Tauri desktop shell. It runs entirely client-side — no server or internet connection is required for the visuals, audio analysis, or show file storage.
VJs, lighting designers, and production techs who want a generative visual source that can free-run on its own, sync to a loaded song, or be remote-controlled from a lighting console or OSC controller.
The window is a fixed layout: a header bar, the main stage (canvas) on the left, a scrollable settings panel ("aside") on the right, and a footer with quick-reference shortcuts.
| Region | What it does |
|---|---|
| Header | The Lumen wordmark, the timecode readout (shows FREE or EXT mode), and status pills for Audio, Bridge, and Output, plus a live FPS counter. |
| Stage (canvas) | The live WebGL2 render. Overlaid with an audio meter, the current style name, a beat-flash indicator, and a DMX-out colour swatch. Buttons in the corner toggle the waveform view, fullscreen, and the second-screen output window. |
| Waveform strip | Appears over the bottom of the stage once a song is loaded — shows the song's peak waveform, a time ruler, beat ticks, and amber draggable cue markers. Click empty space to seek; drag a cue marker to re-time it. |
| Settings panel (right) | Nine collapsible sections (numbered 01–09 in the UI): Generative Style, Color/Palette, FX Rack/DSP, Modulation Matrix, Audio Input, Song & Beat Sync, Images/Stills, Fixture/DMX Channels, Timeline/Cues/Timecode, Scenes/Presets, and Hardware Bridge. Click any section header to collapse/expand it. |
| Splitter | A drag handle between the stage and the settings panel — resizes the panel between roughly 300px and 760px wide. |
| Footer | A quick shortcut legend (space/1-6/B/F) and a transient status message area that shows feedback after actions (e.g. "Bank A stored."). |
Lumen has 19 generative styles, each its own GLSL fragment shader driven by shared parameters rather than pre-rendered footage — nothing is a video loop. Every style reads the same set of inputs (structure/motion parameters, scale, hue/saturation, and live audio bands) and produces a different procedural pattern from them, which is what makes it "generative" rather than a clip player.
The 19 styles, in engine order (style index 0–18):
| # | Style | What it is |
|---|---|---|
| 0 | Noise Field | Domain-warped fractal noise (fbm) |
| 1 | Plasma | Classic layered sine-wave plasma |
| 2 | Tunnel | Radial tunnel/zoom with ring bands |
| 3 | Spectrum | Radial bars driven by frequency bands |
| 4 | Truchet | Rotating Truchet tile pattern |
| 5 | Particles | Layered twinkling point clusters |
| 6 | Solid Colour | Flat colour field pulsing with bass |
| 7 | Gradient | Vertical gradient with noise-perturbed edge |
| 8 | Rings | Concentric rings expanding from centre |
| 9 | Voronoi | Animated Voronoi cell mosaic |
| 10 | Mosaic | Pulsing grid-cell mosaic |
| 11 | Waves | Layered horizontal sine waveforms |
| 12 | Spiral | Rotating multi-arm spiral |
| 13 | Hex | Glowing hexagonal grid |
| 14 | Starfield | Multi-layer scrolling star field |
| 15 | Metaballs | Organic blending metaball blobs |
| 16 | Interference | Two interfering radial wave sources |
| 17 | Rain | Falling streak/rain effect |
| 18 | Image | A user-imported still image, warped/zoomed by the same controls (see §11) |
Select a style by clicking its button in the Generative Style section, via the 1–9 number keys (styles 0–8 only — see §14 for the shortcut caveat), via a DMX fader on channel 1, or via OSC (/lumen/style). Each style is shaped further by three sliders in that same section:
All styles also read live audio band levels (sub, bass, mid, high) directly in their shader code, so most patterns visibly pulse or shift with the music even before the Modulation Matrix (§5) adds any explicit routing.
After the scene shader renders, a second full-screen post-processing shader applies, in this order: chromatic aberration → feedback (blended with the scene) → bloom → posterize → master brightness. The controls live in the FX Rack / DSP section (03). All are 0–1 sliders bound directly to shader uniforms:
| Control (label) | Internal name | What it does |
|---|---|---|
| Feedback | warp | Blends the previous frame back into the current one (trails). At 0 the feedback loop is effectively muted; higher values increase trail persistence. |
| Fb Zoom | fbzoom | Zooms the fed-back previous frame in or out each frame (0 = zoom out ~4%, 1 = zoom in ~4%, 0.5 = no zoom) — this is what produces the classic feedback "tunnel" effect when combined with Feedback. |
| Fb Rotate | fbrot | Rotates the fed-back frame slightly about centre each frame (0.5 = no rotation). |
| Kaleido | kaleido | Kaleidoscope mirror segments, from 0 (off) up to 16 radial segments at 1.0. |
| Chroma | chroma | Chromatic aberration — offsets the red/blue channels outward from centre. |
| Posterize | posterize | Colour quantisation/banding; 0 = off, 1 = heavy posterize (down to ~3 levels per channel). |
| Bloom | bloom | Cheap 4-tap bloom of bright areas sampled from the previous frame. |
| Speed | speed | Global time-scale multiplier fed into every scene shader's internal clock (roughly 0.3×–2.5× depending on value). |
The post shader samples the scene with chromatic aberration and kaleidoscope applied together, blends in the zoomed/rotated previous frame for feedback, then bloom, then posterize, then multiplies by master brightness. Extreme Feedback + Fb Zoom combinations can wash the image toward white/black quickly — moderate settings are more stable for long-running visuals.
Enable audio input from the Audio Input section (05) — Lumen requests microphone/line input via getUserMedia and analyses it with the Web Audio API's AnalyserNode (FFT size 2048). Four band levels are computed each frame and displayed as live meters:
| Band | Frequency range (FFT bins) |
|---|---|
| Sub | bins 1–6 |
| Bass | bins 6–24 |
| Mid | bins 24–140 |
| High | bins 140–500 |
These four bands (audio.sub, .bass, .mid, .high) feed directly into every scene shader as uniforms, so patterns respond to the music even with no explicit routing configured.
Beat detection runs purely on the bass band: a running average of bass energy is tracked, and a beat fires when instantaneous bass exceeds 1.35× that running average (and is above an absolute floor of 0.12), with a minimum 250ms gap between beats. Each detected beat:
audio.beatEnv) usable as a Modulation Matrix source (see below) — it jumps to 1 and decays exponentially.The Song & Beat Sync section (05B) has a Beat-Sync toggle. When on, every N beats (adjustable 1–32, default 8) the active style advances — either Sequential (next style in order, wrapping) or Random (a random style different from the current one). This switch happens inside the beat detection callback, so it is driven by the same bass-energy beat detector described above, not by a separate rhythm engine.
Lumen has one shared internal clock (transport.t) that every shader's time uniform derives from. Each frame, it is updated by one of two rules:
transport.playing is true, optionally looping back to zero after a configurable number of seconds (Loop (s) field; 0 = no loop).The Timeline / Cues / Timecode section (07) exposes transport controls:
A BPM value (0–180, default 120) is tracked independently of the transport clock and used by the tc modulation source (a beat-phase 0–1 ramp) and by the Modulation Matrix. It can be set two ways, toggled via the Tempo Source button:
When Art-Net timecode arrives via the bridge or native layer, Lumen switches the header readout to EXT mode and sets transport.t directly from the incoming hours/minutes/seconds/frames, overriding the free-run/song clock for as long as timecode keeps arriving.
Lumen can be patched into a lighting rig as if it were one DMX fixture, and can also drive its own output colour back out over DMX (e.g. to sync a real fixture to what's on screen).
Two connection paths exist, both described in the header comment and the Hardware Bridge section (09) of the UI:
window.__TAURI__ and activates native I/O automatically on load: it listens for dmx, tc, and osc events emitted from the Rust side, and sends DMX output back out via a Rust invoke('dmx_out', …) command rather than a WebSocket. An Electron fallback path (window.lumenNative) exists for a legacy Electron build with the same event names (onDmx, onTc, onOsc, sendDmxOut).bridge.js, a Node process) that relays real Art-Net DMX and Art-Net timecode in, and screen-colour DMX out, plus OSC received on UDP port 8000. This bridge script was not found alongside index.html in this repository snapshot — document its setup/location separately if it ships elsewhere.When neither native nor WebSocket bridge is active, Lumen runs in simulation — the on-screen DMX channel faders act as manual stand-ins for a real desk.
The whole engine presents as a single 9-channel fixture. Incoming DMX values 0–255 on channels 1–9 map to:
| Ch | Function | Effect |
|---|---|---|
| 1 | STYLE | Selects one of the 19 generative styles (value divided into 19 bands) |
| 2 | BRIGHT | Master brightness (master param) |
| 3 | HUE | Base hue |
| 4 | SAT | Saturation |
| 5 | SPEED | Animation speed |
| 6 | FEEDBK | Feedback amount (warp) |
| 7 | KALEID | Kaleidoscope segments |
| 8 | CHROMA | Chromatic aberration amount |
| 9 | COLOUR | Snaps hue/saturation to one of the 16 palette swatches (like a colour wheel on a moving-head fixture) — see §9's palette swatch list |
Channels 10–13 are read as four auxiliary values (dmxAux[0..3]) not tied to any fixed parameter — they're only usable as dmx1–dmx4 sources in the Modulation Matrix, so an operator can patch a spare desk fader to modulate any parameter of choice.
Roughly 10 times per second, Lumen downsamples the rendered frame to a 16×16 thumbnail, reads it back, and averages it to a single RGB colour, shown in the "DMX OUT · screen→rig" swatch on the stage overlay. This colour is sent out via the native Rust command, the Electron bridge, or the WebSocket bridge (whichever is active) — intended for driving a real fixture to match the on-screen colour.
OSC messages arrive via the bridge (UDP 8000, relayed over the WebSocket) or the native layer, and are dispatched by address. The full address space, as documented directly in the source:
| Address | Args | Effect |
|---|---|---|
| /lumen/style | int | Select style 0–18 (float 0–1 is also accepted and mapped proportionally) |
| /lumen/style/next | — | Next style (wraps) |
| /lumen/style/prev | — | Previous style (wraps) |
| /lumen/param/<name> | float 0–1 | Set a named parameter. Valid names: hue sat spread master scale speed paramA paramB warp fbzoom fbrot kaleido chroma posterize bloom |
| /lumen/colour (or /lumen/color) | int | Pick palette swatch 0–15 |
| /lumen/morph | float 0–1 | Set A/B palette morph amount |
| /lumen/bpm | float | Set tempo (clamped 0–300) |
| /lumen/beatsync | int (0/1) | Toggle beat→style switching |
| /lumen/play | — | Start transport (and resume song if loaded) |
| /lumen/stop | — | Stop transport (and pause song if playing) |
| /lumen/zero | — | Reset transport to 0 (and seek song to 0 if loaded) |
| /lumen/cue/next | — | Jump to next cue |
| /lumen/cue/prev | — | Jump to previous cue |
| /lumen/scene | int | Recall quick bank slot A–F (0=A … 5=F) |
| /lumen/dmx | ch (1–13), val (0–255) | Set a fixture channel exactly as if it arrived over DMX — channels 1–9 map per the table in §7; 10–13 set the aux modulation sources |
Any address whose first segment isn't lumen, or whose second segment doesn't match a known section, is silently ignored.
Lumen has three layered levels of "save state," from most transient to most durable: cues (song-locked moments), quick banks (six manual slots), and named scenes (a saved library).
A cue captures the entire current look — style, colour, every FX parameter, the modulation matrix, and DMX channel values — tagged to a specific time. Add one with + CUE @ NOW in the Timeline section; CLEAR CUES removes them all. With a song loaded, cues render as amber draggable markers on the waveform, labelled with the style name they recall. As playback crosses a cue's time, its full state is recalled automatically. Cues can also be stepped with /lumen/cue/next / /lumen/cue/prev (see §8), which also seeks the song to the cue's time.
Six one-key slots in the Scenes / Presets section. Tap a lettered button to recall it; shift-tap to store the current full state into it. Banks persist in the browser's localStorage between sessions (key lumen.bank.v1) and are also included in show export/import (§10).
An unlimited, named list of saved states. Type a name and click SAVE to add one; click a saved scene's name to recall it, OVR to overwrite it with the current state, or × to delete it. Scenes persist in localStorage (key lumen.scenes.v1) and travel with show export/import.
The A/B Morph control in the Color/Palette section is a distinct, simpler mechanism: it stores two colour palettes (hue/saturation/spread/scheme) in memory as "A" and "B" and crossfades between them with a single slider — it is not persisted and is not part of a cue/bank/scene's state capture in the same sense (a saved scene does capture whatever hue/sat values were showing at save time, but not the A/B morph position itself).
Everything that can be saved locally (banks, named scenes, cues, and the current tempo) can be exported to a single JSON file and re-imported — for backing up a show or moving it to another machine.
Export Show writes a file named lumen-show-YYYY-MM-DD.json containing:
{
"app": "LUMEN",
"v": 1,
"bank": { "A": {...}, "B": {...}, ... },
"scenes": [ { "name": "...", "state": {...} }, ... ],
"cues": [ { "t": 12.3, "style": 4, "state": {...} }, ... ],
"bpm": 120
}
Import Show reads a file in this shape and merges it in — bank slots and scenes are replaced wholesale, cues are replaced wholesale, and BPM is applied. Malformed files fail with a footer message rather than corrupting the current session.
Because storage is plain localStorage, Lumen works fully standalone as a single HTML file with no server — the only thing that requires a proper http:// origin (rather than opening the file directly) is microphone access for live audio input; loading a song file or working with already-saved banks/scenes does not need a server.
The Images / Stills section lets you import a picture (any browser-supported image format) as a texture. Selecting the Image style (#18, see §3) displays it cover-fit to the stage, and the same Param A / Param B / Scale controls that shape the generative styles instead control image-specific behaviour: Param A drives a warp/displacement amount, Param B drives a palette-based hue-shift overlay, and Scale drives a zoom/pulse. The image responds to bass energy (subtle zoom pulse) exactly like the generative styles do, and passes through the same FX chain (feedback, kaleidoscope, chromatic aberration, posterize, bloom). CLEAR removes the loaded image.
The main stage canvas is always live in the primary window. OPEN OUTPUT ↗ captures the canvas as a media stream (canvas.captureStream(60)) and opens it in a separate borderless popup window showing just the video feed — intended to be dragged onto a second monitor or projector output and set fullscreen. FULLSCREEN instead fullscreens the main canvas in place. Because the output window is a live stream of the same canvas, both windows always show the identical frame — there is no independent "confidence view" vs "programme output" split; to be documented further if the live app behaves differently from what the code implies.
Lumen ships as a Tauri v2 desktop application (package com.lumen.visualplayer, version 1.0.0) targeting macOS and Windows, built from this same single HTML file bundled as the frontend. Key facts from the build configuration:
tauri, tauri-plugin-shell, and tokio with networking features — consistent with the native side handling Art-Net/OSC networking directly rather than requiring the external bridge.js WebSocket process.electron-legacy.zip in the project and as the window.lumenNative code path) — its current support status wasn't determined from this pass; treat the Tauri build as primary.On the web/browser side (running index.html directly, outside Tauri), Lumen degrades gracefully to the WebSocket bridge + getUserMedia microphone path described in §7.
| Key | Action |
|---|---|
| Space | Toggle transport play/stop |
| 1–9 | Select generative style 1–9 (styles at index 0–8 of the 19 available — there is no shortcut for styles 10–19; the footer legend in the app itself only advertises "1–6") |
| B | Tap tempo (same as clicking TAP) |
| F | Fullscreen the stage canvas |
Shortcuts are ignored while focus is inside a text input or dropdown, so typing a scene name or adjusting a select won't accidentally trigger them.
| Term | Meaning |
|---|---|
| Style | One of Lumen's 19 generative shader scenes (Noise Field, Plasma, Tunnel, …, Image). Selected by index 0–18. |
| FX / post chain | The second shader pass applied after the scene renders: chromatic aberration, feedback, kaleidoscope, bloom, posterize, master brightness. |
| Feedback | Blending the previous rendered frame (zoomed/rotated) back into the current one to create motion trails. |
| Modulation Matrix | A patch-bay of source → destination routings that sum onto a base parameter value — e.g. routing the bass band onto the scale parameter. |
| Cue | A full saved look tagged to a specific time; recalled automatically as the playhead passes it, shown as an amber marker on the waveform. |
| Bank | One of six (A–F) quick-recall state slots, stored locally and included in show export. |
| Scene | A named, saved full state, distinct from banks — an open-ended list rather than six fixed slots. |
| Art-Net | A protocol for carrying DMX lighting data (and, here, timecode) over a network, used by Lumen's bridge to receive DMX/timecode from a lighting desk and send DMX colour data back out. |
| DMX | The lighting-industry protocol for addressing up to 512 channels per universe; Lumen exposes itself as a 9-channel fixture (plus 4 auxiliary channels) on channels 1–13. |
| OSC | Open Sound Control — a UDP-based control protocol Lumen listens to (via the bridge, on port 8000) for remote parameter, transport, and cue control; see §8 for the full address space. |
| Timecode | The running HH:MM:SS:FF clock shown in the header. Either free-running/song-following internally (FREE) or driven by incoming Art-Net timecode (EXT). |
| Beat envelope | A value that jumps to 1 on each detected beat and decays exponentially, usable as a Modulation Matrix source (beat). |
LUMEN User Manual · Version 1.0.0 (Draft). This manual was generated by reading the app's source code, not by using the shipping app — expect a human editing pass for workflow and UX detail.