vizz documentation
A realtime generative visual instrument. This covers building a look, playing a set, and wiring it into a rig — from first launch to a projector at a venue.
Install and first launch
macOS — download from
the latest release
and drag vizz.app to Applications, or run:
curl -fsSL https://raw.githubusercontent.com/legofsalmon/vizz/main/scripts/install.sh | bash
Releases are signed with a Developer ID and notarized, so they open with a normal double-click. Syphon is embedded — there is nothing else to install.
Windows and Linux build from source and are tested in CI, but have no installer yet. You will need a Rust toolchain:
git clone https://github.com/legofsalmon/vizz
cd vizz
cargo run --release
What happens on first launch. A window opens at 1280×720
showing a slowly turning sphere of particles, with the control panel on the
left. OSC is listening on UDP 7000 and — on macOS — a Syphon source named
vizz is already published, so Resolume or VDMX can see the output
before you have touched anything.
The three screens
vizz has one window and three overlays on it. The visuals keep running underneath all of them.
| Screen | Key | What it is for |
|---|---|---|
| Control panel | Tab | Every parameter, grouped. Where you build a look. Open on first launch. |
| Performance | P | Big faders, scene pads, punch buttons. Where you play. Nothing here can destroy work. |
| Modulation canvas | G | A node graph: what moves what. Editing, not performing. |
Press ? at any time for the shortcut list on screen.
Shows
The name at the start of the panel, and at the start of the performance
strip, is the show you have open. Click it for the list of
shows on this machine, and for new show…,
save as…, rename… and delete.
A show is everything you built for one band or one night: both grids, the pages of pads, your looks, your patches, your fader layout and your slider ranges. Switching shows swaps all of it at once.
A show carries its own preset library, which is what makes one a
complete thing you can copy to another machine and have work — a pad names a
look, so the pool it names travels with it. So new show… starts
with an empty library: the built-in looks are always there,
save as… is how you carry your own looks into a new show, and
the built-in set is one right-click away on the + beside the deck
chips.
What does not travel is the rig. Your output size, audio device and controller map stay where they are, because they describe the machine in front of you rather than the set you are playing — plug into a different rig and your show is unchanged; open a different show and your rig is unchanged.
There is no save button. Everything above is written the
moment it changes, so save as… means copy this show and
carry on in the copy rather than "write it down before you lose it".
Nothing is ever lost for not having pressed anything.
A name already taken counts up rather than overwriting: type
Warehouse twice and the second one is Warehouse 2.
The last show cannot be deleted — there would be nowhere for the next pad to
live.
Build your first look
The fastest route in is to start from something that already looks like something, then pull it apart.
- Click a preset. In the panel's Presets list, click Butterfly (the Lorenz attractor) or Tunnel (feedback, the high-energy one). The look glides in over a few hundred milliseconds rather than cutting.
- Open a parameter group. The list below is grouped by
address —
particles,shape,fx,color,camera. Each header shows how many controls it holds. - Move something obvious.
/fx/trailis the one that turns a particle field into VJ material: it is how much of the last frame survives into this one. Take it to 0.9 and add/fx/zoomfor a tunnel. - Find a control by name. Press / and type. The groups flatten while you filter, because once you have typed a name you already know what you want.
- Keep it. Type a name in the box under the preset list and press Enter. Your presets live beside the built-ins and can be recalled by number, MIDI or OSC.
Sliders that will not stay put. A parameter with a
~ beside it is being modulated — the value is still yours, and
modulation rides on top as an offset. Hover the mark to see the current
offset. The >< button narrows a slider's working range so a
fader can sweep the part you care about.
Presets
A preset is where every knob is sitting. It is not the modulation graph — that is a patch, stored separately, so recalling a look never rewires your LFOs.
Six ship with the app and cannot be overwritten: Slow bloom (a neutral opener), Butterfly, Tunnel, Stage (cloud inside the room, forced perspective), Confetti and Ribbon. Your own go beside them.
- Recall — click it in the panel, press 1–9
or 0 for slots 1–10, send
/preset/recall, or learn a MIDI button to it. All four are the same path. - Recall does not snap. Values arrive as targets and the per-parameter smoothing carries them from wherever they are — a glide, not a cut. That is what makes presets usable mid-track.
- Each preset sets only what matters to its look, so recalling one leaves everything else where you left it.
- Two things are never captured.
/master/dimis the panic fader — a preset that restored it could black out the show, or silently undo a blackout somebody reached for./preset/recallis excluded because a preset containing it would fire another preset on load.
Slot 0 means nothing selected and presets start at 1, so a controller resting at zero cannot fire anything on startup. Recall is edge-triggered: it fires when the slot changes, so a button parked on a slot does not re-apply its preset every frame and pin the controls you are trying to adjust.
Scenes and transitions
A preset recalled by number is a cut. A scene is the other thing you want during a set: moving from one look to the next over musical time.
Sixteen pads, laid out like a sequencer — four by four in the panel, sixteen across on the performance layout.
- Build a look.
- Click store to arm it, then press a pad. The pad now holds that look.
- Repeat for as many pads as you want to travel between.
- Press pads to move. Set blend to the transition length in seconds and pick a curve — linear, smooth, ease in, ease out, or cut.
The blend is in the data, not in the picture. Crossfading two rendered images of a particle field gives a double image that reads as a mistake. Interpolating the parameters gives one field whose settings are somewhere between the two — still one of everything, and it looks like the material moving rather than like a mixer.
Two things do not interpolate. Switches jump at the half-way
point: /fx/mirror has off, x and quad and nothing sensible
between them. Cloud slots never blend, because half way between
slot 0 and slot 2 is slot 1 — a different cloud entirely, which would flash on
screen part-way through every move.
Firing during a transition re-aims from wherever the blend has reached, so you are never locked out until the last move finishes.
Autopilot walks the filled pads in time with the beat clock,
every /scene/bars bars. It fires on the bar boundary and never on
the frame you switch it on. The performance layout shows it as a wide green
bar naming the pad it is heading for.
Decks and Resolume
Sixteen scenes and sixteen gravity slots is a generous evening if the set is one continuous thing, and nowhere near enough if it is twelve songs that each want their own looks. A deck is a page of both grids together, one per song.
The chips above the pads on the performance layout are the set list.
- Click a chip to turn the page. The pads underneath — scenes and gravity both — become that song's.
- + makes a fresh empty page; right-click a chip and choose duplicate to start the next song from this one, which is usually what you want.
- Right-click for rename… and delete page. The last page cannot be deleted.
A deck holds references, not copies. Looks live in one pool, so the same preset can sit on a pad in every song and refining it once improves all twelve. Deleting a page throws away an arrangement, not an evening's work.
Turning a page changes nothing on screen. Switching decks fires nothing and writes no parameter: whatever is showing stays until you press a pad. Every page turn happens in front of an audience. A blend already running finishes, but stops claiming a pad — after a page turn no pad on screen produced what you are looking at.
/deck/select is an ordinary parameter, so a chip, a controller
button and an OSC message are one gesture. Bindings name the deck
number, exactly as the pads and preset slots do, so sixteen buttons address
sixteen pages. Right-click a chip to learn one.
Following Resolume's columns
Arena's column launches can drive vizz. In Arena ▸ Preferences ▸
OSC turn on OSC Output and point it at this machine on
vizz's OSC port (7000 unless --osc-port says otherwise), then turn
on resolume beside the deck chips.
Launching Arena column N then fires column N here — the scene pad and the gravity pad of that number, together, which is what a column means in Arena too. Keep the two grids arranged in parallel and one button runs the video and the field.
Off unless you ask for it. The OSC listener binds every interface by default, so following hands anyone on the venue's wifi the scene transport. The switch is remembered between runs.
Each page declares which columns it follows. A deck's origin is the Arena column its column 1 follows, on the chip's right-click menu. With one grid in Arena, leave every page at 1. With one long composition and a song every sixteen columns, set 1, 17, 33 … and each page follows its own stretch; a column outside the live page's stretch does nothing rather than landing on the nearest pad.
Relaunching the column already showing fires it again, unlike pressing the
same pad twice — a relaunch in Arena is a deliberate re-trigger and has to land.
/column/fire is an ordinary address, so anything else that speaks
OSC can play the show by column too.
Arena's deck changes come too.
/composition/decks/N/select picks page N here. A deck is the song
and a column is the section, so with the same messages reaching the lighting rig
one launch moves the video, the lights and the field to the same place.
The built-in set
A machine that has never had a show set up on it opens with one: twenty songs, eight sections each, in the same idiom the app was built alongside — hard-edged flat geometry, black paper, bold limited palettes, layers at close frequencies interfering into moiré.
The pads are the parts of a song, in the order you play them: Intro, Build, Break, Drop, Bridge, Peak, Outro, Blackout — the same eight the lighting rig uses.
Only on a machine with no show on it. No deck file, one page, nothing on it. Anything else and the set stays out of the way. To ask for it later, right-click the + beside the deck chips; it replaces every page, so it asks once.
Each song carries one designed look, and its eight pads are that look read through the shape a song has — a bed, a rise, something stripped back, a hit, a hold, the biggest thing in the song, a return to the bed, and an ember. The Drop pad is the designed look exactly; everything else is measured from it. A quiet song's peak stays under a loud song's, so the set's arc survives into the pads.
Every pad is an ordinary preset, so any of them is one store away from being yours.
Punch FX
The things you do on the drop. All five are momentary — they apply while held and rest at zero — so nothing can leave the output stuck inverted, and no preset or scene can capture one.
| Gesture | Address | What it does |
|---|---|---|
| Flash | /punch/flash | White out. Space is the key. |
| Black | /punch/black | Blackout — kills the colour, keeps the coverage. |
| Invert | /punch/invert | Inverts the finished picture. |
| Freeze | /punch/freeze | Holds the picture. The set keeps moving underneath, so releasing snaps to where the music got to. |
| Strobe | /punch/strobe | Beat-synced. /punch/strobe_div sets beats per cycle. |
On the performance layout they are the PUNCH row: press and hold, or shift-click to latch one on. A learned MIDI note is momentary for free, because the gesture rests at zero — press to fire, release to stop, with no mode to get stuck in.
Gravity
Four wells that pull or push the particles, sitting above whatever shape is
on screen. Positive strength attracts, negative repels; radius sets the reach;
/gravity/amount is the master depth for the whole layer.
Gravity has its own 16-pad grid, with its own store, blend time and curve, so a gravity move can be fired independently of a scene change — the field can lurch without the look changing, or both can move at once on different timings.
Performance faders
The CONTROLS row is fourteen assignable faders plus the master. Click a fader's caption to assign it to any parameter; click learn beneath it and move a controller to bind MIDI.
The master is /master/dim, the panic fader. It is
never captured by a preset or a scene, so nothing you fire can undo a blackout
you reached for.
Shapes and geometry
/shape/mode sweeps through eight forms, and fractional values sit
between two of them — it is a sweep, not a switch:
0 sphere · 1 torus · 2 knot · 3 grid · 4 shell · 5 Lorenz · 6 Aizawa · 7 cloud pair · 8 sphere again
The range wraps, so the top morphs the Aizawa attractor back into the sphere.
/shape/morph adds an extra blend into the next form and
/shape/twist shears the whole field.
Lorenz and Aizawa are real strange attractors, integrated per particle rather than drawn as a static curve — the field genuinely flows along the attractor, which is why they read as motion rather than as a picture of a shape.
Point clouds, words and images
Eight cloud slots: two built-in attractors and six loadable. Files, typed words, images and a live stream all compete for the six.
From a file
Drag a .ply, .xyz, .csv or
.pts onto the window, or start with --cloud scan.ply.
PLY reads both ASCII and binary little-endian, with per-point colour where the
file has it. A file that will not parse is a warning, never a startup failure.
From a word
Open the panel's clouds section, type into the
type a word field and press Enter. The string is rasterized
with the app's own font into a cloud you can morph against any other slot.
Typed clouds persist as text:WORD and come back after a restart.
From an image
Drag a .png or .jpg on. Position comes from the pixel
grid, colour from the pixel, and a shallow relief from luminance. Fully
transparent pixels are skipped, so a logo keeps its silhouette instead of
arriving as a rectangle.
From a live stream
vizz --live-cloud tcp://192.168.1.9:9000 # connect to a streaming app
vizz --live-cloud listen://0.0.0.0:9000 # wait for one to connect here
vizz --live-cloud /tmp/live.ply # re-read a file as it is rewritten
While it is listening, the panel prints the address to send to —
send to 192.168.1.42:9848 — with a button to copy it. Point your
sender at that. It is only shown for a listen:// address: if you
are dialling out to host:port instead, the address that matters is
the other machine's.
Showing and morphing them
A cloud you load is shown. Dropping a file, typing a word,
naming one with --cloud, or the first frame of a live stream all
select the new slot as /cloud/a and point
/shape/mode at the cloud pair. The shape glides there rather than
cutting, so the field turns into the cloud over about a second.
The one case that does not move the shape is the bank coming back at startup: the clouds you loaded in earlier sessions are restored to their slots, but the look you left behind is left alone.
/cloud/a and /cloud/b pick the two slots and
/cloud/morph blends between them. Slot choice is stepped — half a
slot is not a cloud — while the morph is swept and modulatable, so it can be
driven from an LFO, the beat clock or an audio band.
Particles keep their index across the blend, so the same point travels from one cloud to the other rather than the field being re-scattered. A blend between two unrelated clouds passes through a shapeless middle: that is inherent to index-based morphing. Clouds with related structure morph far better than arbitrary pairs.
Clouds are centred and uniformly scaled into the same box the procedural
shapes use, so /particles/spread means one thing everywhere.
Fewer points than the slot holds means each is repeated with a small
deterministic jitter, because a dense clump at the origin is far more
visually wrong than slight duplication.
Live video as a point cloud
vizz --video-source test # the built-in pattern
vizz --video-source "OBS" # an NDI source, matched as a substring
vizz --list-ndi # what is on the network
A video input arrives as a cloud in its own slot. That is
the whole design: /cloud/a, /cloud/b and
/cloud/morph select and blend it, the palette tints it, the
spread scales it, and it morphs against a scan or an attractor with nothing
else needing to know it is live. The first frame points the shape at it, the
way a dropped file does.
Each particle takes a fixed cell of the picture and stays there, so the
field reads as a cloud of the video rather than noise sampled from
it. Position comes from the cell, colour from the pixel, and depth from
whatever /video/relief names.
| Control | What it does |
|---|---|
/video/depth | How far the relief pushes along z. Signed, because a picture standing proud of the plane and one sunk into it are different looks; 0 is the flat picture, which is a look too. |
/video/relief | What does the pushing: luminance, hue, saturation or chroma. Luminance is the honest default; the colour ones are what a flat, evenly lit source needs. |
--video-source test is a diagnostic. "Nothing
on screen" has two causes — the feed, or the wiring — and they need telling
apart. The pattern is generated in-process and goes through the identical
path, so a blank output with it running is a fault in vizz, and a blank
output without it is the network or the sender. It moves on purpose: a still
pattern proves a frame arrived once, a moving one proves frames are still
arriving.
Vector layers
The print side of the app: four layers of hard-edged procedural pattern —
rings, stripes, checker, polygon, star, rays, dots — in flat ink colours from
a shared four-slot palette (/pal/0..3), composited with
multiply/screen/difference-style blends. Two layers at near frequencies
interfere into moiré. Edges are analytically antialiased and stay razor-sharp
at any resolution.
Start from a preset. Three vector looks ship after the six
particle ones — Interference, Poster and Signal.
Recall one and the print look arrives whole; the particle field steps aside
via /particles/count 0, which is part of the look and comes back
with the next particle preset.
The performance layout grows a LAYERS strip when any layer is on — hidden otherwise, like the gravity grid. Per layer: the ink swatch, the generator (click cycles, right-click back — off is on the wheel), the blend mode, and drags for opacity and frequency. Faders remain the way to ride a layer parameter under MIDI.
| Control | What it does |
|---|---|
/vec/place | scene (default): behind the particles, inside the feedback chain — trails, punch and the tone-map apply, layering works. print: after the post chain — exact ink bytes, untouched by feedback, and deliberately out of reach of punch and particles. |
A preset captures its placement. A /vec/place override sent in
the same burst as a recall loses to the preset — send it after the recall
lands.
The "Pulse" patch ships in the modulation canvas's load menu: the kick band gates a snap envelope that blinks layer 2 out on each hit, and a four-beat phasor drifts layer 1's phase. Load it over any vector preset and the layers move with the music.
Camera moves
A camera move is the one gesture you cannot make by hand: orbiting smoothly
for eight bars while also firing pads is two jobs and one pair of hands.
/camera/move picks one of ten canned paths, move_bars
sets how long a cycle takes, and move_size how far it travels.
| Move | What it is for |
|---|---|
| orbit | A full turn per cycle. The one that can run all night. |
| sway | Handheld: a partial turn back and forth, the horizon breathing under it. |
| push | In and back out — the build. |
| pull | Out and back in — the reveal. |
| crane | Rising over the top, looking down by the end. |
| spiral | A helix: turn, rise and close all together. The drop. |
| look around | The camera holds still and its aim sweeps — standing in one place and turning your head. |
| fly through | In through the front, out the back, and round again. |
| walkthrough | Four legs and four corners of a square, at eye level, inside the cloud. Moving through a house. |
| drift | Very slow, never repeating inside a song, always moving. For long ambient sections. |
A move is added to the camera your faders already describe, never written back into it. Switch one off and you are exactly where you were, and a move running is never a reason you cannot still steer. Rate is in bars, so a move locks to the same clock as the sequencer and the LFOs.
/camera/at_x, at_y and at_z aim the
camera at a point in the world — which is what makes a walkthrough possible.
Pan moves the target in the camera's own screen plane, so it keeps the subject
centred by construction: right for framing, and no use for travelling.
Light
Two movable lamps and a directional sun, on top of an ambient level. Everything defaults to off, and off means identical — ambient is 1 and every lamp is 0, which multiplies the picture by exactly one, so no preset you have ever saved looks any different.
Lamps light a volume. Each has a position, a reach, a level and a colour, and falls off smoothly with distance. Walk one through a scan and it reveals the scan. This works on everything — imported clouds, procedural shapes, live video — because it only needs to know where a point is.
The sun lights surfaces. A wall facing the light comes up, a
wall facing away goes down. That needs to know which way each point faces, so it
only does anything on a cloud that has normals — see below.
/light/shape is the dial between the two readings: at 0 a lamp is
pure volumetric falloff, at 1 it respects the surface.
Lamps are white until you colour them. Each has a hue and a tint, because hue on its own has no value that means "no colour" — hue 0 is red, so a lamp described by hue alone would turn your scan red the moment you raised it to see what it did.
/light/torch puts lamp 1 on the camera. It is the partner to a
walkthrough: light arriving from where you are is what makes moving through a
scan read as moving, rather than as the scan fading up.
Normals
A point cloud is points. Whether it also knows which way its surface faces
depends on whoever exported it, and most exports leave the field empty. vizz
reads nx/ny/nz from a PLY when they are
there, and works them out from each point's neighbours when they are not — about
a tenth of a second when a cloud loads. A cloud that genuinely has no surface —
a procedural shape, a video frame — is left alone, and the sun simply does
nothing to it rather than lighting it from a direction nobody measured.
Shading is two-sided: a surface always faces you. Fitting a plane to neighbours cannot tell a normal from its opposite, and picking consistently across a whole cloud still comes out backwards for a scan of a room, where the surfaces you want lit face inwards. There are no shadows here for two-sidedness to contradict, and the alternative is half your scan going black for a reason you cannot diagnose from the front of a stage.
Colour and palettes
/color/palette starts at 0 = plain HSV and
crossfades up through four cosine gradients — warm, ember, ice, neon — with
your own loaded palettes from 5 up. /color/spread is how much of
the gradient the field spans; /color/drive picks what chooses each
particle's colour:
0 index · 1 radius · 2 depth · 3 height
Driving by depth is what makes a cloud read as three-dimensional on a flat screen. Driving by index keeps a scan's own colours coherent.
Your own palettes: drop a palette file on the window and it loads into the next slot and selects itself. Re-dropping an edited file reloads it in place, so you can iterate in a text editor with the app running. Imported cloud colour multiplies the palette rather than replacing it, so the palette still works as a tint and an uncoloured cloud is unaffected.
Effects
The output chain, in order: feedback, mirror, glow, RGB shift.
| Control | What it does |
|---|---|
/fx/trail | Feedback — how much of the last frame survives. The single most useful control in the app. Capped below 1.0, because at 1.0 nothing decays and the frame saturates to white within seconds. |
/fx/zoom | Per-frame zoom of the feedback. With trail up, this is a tunnel. |
/fx/spin | Per-frame rotation of the feedback. With zoom, a vortex. |
/fx/mirror | Off, mirror, quad, kaleido. A switch, not a sweep. |
/fx/glow | Bloom lift. |
/fx/shift | Radial RGB split — chromatic aberration. |
Camera and room
A real camera looks into the field: /camera/distance,
/camera/orbit, /camera/elevation,
/camera/fov, and /camera/pan_x /
/camera/pan_y to slide the view off-centre.
/camera/focus and /camera/defocus give real
depth of field — with defocus up, near and far particles bloom into soft
discs and the field gains depth a flat render cannot fake.
The room
An optional wireframe room sized to the frame, for forced perspective and
parallax. Raise /room/brightness to see it.
/room/depth sets how far back it goes,
/room/converge how hard the grid converges, and
/room/vanish_x / /room/vanish_y move the vanishing
point — which is how you match the room to where the audience actually sits
relative to the screen.
/room/anchor places the cloud between the front and back of the
room, and /room/embed bends the cloud's own perspective toward the
room's, so the particles sit in the space rather than floating in
front of a drawing of one. The built-in Stage preset is this
set up and ready.
Audio reactivity
vizz analyses an audio input into four bands plus a broadband level, and those are available as modulation sources.
- Open the panel's audio section and pick an input from the
device list. (Or start with
--audio-device "Scarlett"— it matches on a substring;--list-audioprints the names.) - Play something typical of your set — not a quiet intro.
- Press fit. Every band's gain is scaled from the last few seconds of what actually arrived.
- Check the meters. Each band shows two stacked bars: what modulation receives on top, what is arriving underneath. A red mark on the right means the band is pinned and the gain is too high.
Each band has editable low and high edges in Hz and a gain in dB. The defaults are low / lo-mid / hi-mid / high, but a kick-only band of 40–90 Hz driving size is often more musical than a broad one.
There is no correct default gain. It depends on the interface, the track and how hard it is being driven. fit is the honest answer — it reads your actual signal instead of guessing.
Tempo and the beat clock
One beat clock drives the strobe, scene autopilot, phasors and beat triggers. It can get its tempo three ways:
- Tap — the tap button on the status line, or a learned MIDI button. Four taps is enough.
- Detected from the audio input.
- MIDI clock from a DJ mixer or DAW. vizz follows the wire with a tempo estimator built to shrug off USB jitter; a Start message resets the downbeat.
Tap and auto always win control back, so a stale clock source can never lock you out mid-set.
The modulation graph
Press G. Nodes on a canvas: sources on the left, operators in the middle, parameter sinks on the right. Drag from an output to an input to wire them.
Sources
| LFO | Free-running or beat-locked, several shapes. |
| Band | One of the four audio band envelopes, 0–1. |
| Level | Broadband level. |
| Phasor | A ramp 0–1 over N beats, locked to the beat clock, so the whole patch can phase off one saw. |
| Beat trigger | A pulse on each beat division boundary. Every trigger of the same division fires together. |
| Constant | A fixed value — useful as an offset or a manual depth. |
Operators
| Curve | Bend a linear signal into something with shape. |
| Math | Two inputs: add, multiply, min, max… |
| Scale | Affine — the workhorse for turning a unipolar source bipolar (multiply 2, add −1) or trimming a range. |
| Smooth | Asymmetric attack/release slew. Tames anything twitchy. |
| Quantise | Snap to N steps — turns a smooth sweep into something that lands. |
| Sample & hold | Latch a value when a trigger rises. |
| Gate | 1 above a threshold, 0 below, with hysteresis so a band hovering at the line does not chatter. |
| Envelope | Attack/decay fired by a rising edge. Retriggering climbs from the current level instead of clicking to zero. |
A worked patch: kick in, size punches out
- Add a Band node set to band 1 (the low band).
- Add a Gate and wire the band into it. Raise the threshold until it only opens on the kick.
- Add an Envelope — short attack, decay to taste — and wire the gate into its trigger.
- Add a Param sink addressed to
/particles/sizeand wire the envelope in. Set depth for how far it moves.
Swap the Band for a Beat trigger and the same chain is tempo-locked instead of audio-driven, with nothing else rewired.
Patches are saved separately from presets. The canvas remembers its pan, zoom and patch name across launches. A wire that would make a loop is refused with a reason, and a Param node aimed at nothing says missing rather than failing silently.
MIDI
Controllers connect automatically — plug one in and it appears in the panel's midi section. Nothing needs configuring first.
Learning a control
- Go to the performance layout (P).
- Click learn under the fader you want, or on a scene pad, or on a preset button.
- Move the knob or press the pad. The binding appears as a chip
(
ch1 cc21) under the control.
While learning, the panel echoes whatever is arriving — so a controller that is not sending at all is immediately distinguishable from a mapping problem. Bindings are saved as you make them and survive a restart.
Notes are momentary, CCs are absolute. A learned note on a punch gesture fires while held and releases when you let go, because the gesture rests at zero. A CC on a fader takes the fader's full range.
MIDI clock
If the device sends clock, vizz can follow it — see Tempo. Clock traffic can never be captured by a learn: an armed learn ignores clock messages, so arming a learn while a mixer is running does not immediately bind to the clock.
OSC
Send standard OSC to UDP 7000 (change it with
--osc-port). Float, int, double and bool arguments are all
accepted. Unknown addresses and malformed packets are logged and ignored —
control input can never crash the renderer.
# TouchOSC, Max, Pd, a Python script — anything that speaks OSC
/fx/trail 0.92
/shape/mode 5
/preset/recall 3
Every parameter in the app is an OSC address, and every address in the table below is a parameter — the panel is generated from the same table, so the two can never drift apart. The full list is below.
Two families of addresses are listened for without being parameters:
/composition/columns/N/connect and
/composition/decks/N/select, which are what Resolume Arena emits
when a column is launched and when a deck is chosen. They are translated into
/column/fire and /deck/select on the way in, and only
while following is on.
Keyboard
| 1–9, 0 | Fire preset slot 1–10 |
| Space | Flash — white out while held |
| Tab | Show or hide the control panel |
| P | Performance layout |
| G | Modulation canvas |
| / | Filter the parameter list |
| ? | The shortcut list, on screen |
| F11 | Fullscreen on the window's monitor |
| Esc | Leave fullscreen; otherwise quit (asks once) |
Esc asks before quitting because it is one key, next to nothing, on a machine whose whole job is not going black. A second press within a few seconds confirms.
Syphon, NDI and output sizes
Syphon (macOS)
On by default. vizz publishes a source named vizz that appears in
Resolume, VDMX, MadMapper and TouchDesigner with no configuration. It is
zero-copy — the texture is shared on the GPU, never read back — so it costs
almost nothing. Disable with --no-syphon; rename with
--syphon-name.
NDI (all platforms)
vizz --ndi # publish as "vizz"
vizz --ndi --ndi-name Deck1 # or under your own name
NDI needs the runtime installed on the machine; vizz loads it at runtime and says so clearly if it is missing. Frames are read back asynchronously through a ring, so a slow network drops frames on the NDI output and never stalls the renderer.
Render size and output size
In the panel's outputs section: set the output size (720p / 1080p / 1440p / 4K presets, or type one), and a render multiplier from 0.5× to 2×. Rendering below output size is the cheapest way to hold frame rate on a laptop; above it is supersampling.
16-bit float master gives the feedback chain headroom before it clips — worth it when trails and glow are heavy. It doubles the master texture's memory and is off by default.
Recording
vizz records the master output as a PNG sequence. Every finished frame is a finished file, so a crash mid-take costs nothing already written.
- Press the REC chip on the performance layout's status strip. (The panel's outputs section has the same control, if that is where you already are.)
- The chip turns red and counts elapsed time, frames written and frames dropped.
- Press it again to stop. A notice names the folder.
Takes land in ~/Movies/vizz/vizz-<timestamp>/ on macOS and
~/Videos/vizz/… elsewhere. Assemble one with ffmpeg:
ffmpeg -r 60 -i frame_%06d.png -pix_fmt yuv420p take.mp4
Alongside the frames is a frames.csv of per-frame wall-clock
times, so a take that dropped frames can be assembled honestly instead of
silently running fast.
Recording never stalls the show. A slow disk drops frames on the recording only, and the drops are counted on the chip rather than hidden. A full disk stops the take with a notice instead of retrying sixty times a second. Expect drops at 1080p60 — PNG encoding is at the edge of one core — and lower the output size if the count climbs.
/record/active is an ordinary parameter, so OSC and a learned MIDI
button can start and stop a take too.
Fullscreen and venue setup
F11 fills the monitor the window is on — drag the window to the projector first, then press it. The first Esc leaves fullscreen.
vizz --fullscreen # start fullscreen
vizz --fullscreen --monitor 1 # on a specific monitor
The last F11 choice is remembered between launches, so a machine that always runs fullscreen does not need telling twice.
A venue checklist
- Set the output size to match what the projector or the VJ software expects, before the doors open.
- Pick the audio input and press fit while the opening act is playing, not during soundcheck silence.
- Learn your controller once — the map is saved and reloads next time.
- Store the looks you plan to use into scene pads, in the order you expect to play them.
- Check
/master/dimis bound to something you can reach in the dark. - If you are feeding another app, confirm it sees the Syphon or NDI source before you go fullscreen.
Every OSC address
Every parameter, with its range and default. This table is checked against the running app by a test, so it cannot drift out of date.
| Address | Range | Default | Meaning |
|---|---|---|---|
/particles/count | 0 – 500000 | 60000 | live particle count |
/particles/size | 0.001 – 0.2 | 0.015 | sprite size |
/particles/speed | 0 – 4 | 0.6 | motion rate (phase-continuous) |
/particles/spread | 0.05 – 3 | 1.2 | field radius |
/particles/hue | 0 – 1 | 0.58 | base hue |
/particles/saturation | 0 – 1 | 0.8 | color saturation |
/particles/brightness | 0 – 2 | 1 | value multiplier |
/shape/mode | 0 – 8 | 0 | geometry; fractional values morph: sphere · torus · knot · grid · shell · Lorenz · Aizawa · cloud pair · sphere again |
/shape/morph | 0 – 1 | 0 | extra blend into the next form |
/shape/twist | 0 – 2 | 0 | shear and vertical twist |
/fx/trail | 0 – 0.98 | 0 | feedback: how much of last frame survives |
/fx/zoom | 0.9 – 1.1 | 1 | per-frame zoom of the feedback (tunnels) |
/fx/spin | -0.1 – 0.1 | 0 | per-frame rotation of the feedback |
/fx/mirror | 0 – 3 | 0 | 0 off · 1 mirror · 2 quad · 3 kaleido |
/fx/glow | 0 – 1 | 0.25 | bloom lift |
/fx/shift | 0 – 1 | 0 | radial RGB split (chromatic aberration) |
/punch/flash | 0 – 1 | 0 | white-out while held — Space, a punch button, or a learned MIDI note |
/punch/black | 0 – 1 | 0 | blackout while held; rgb only, coverage stays |
/punch/invert | 0 – 1 | 0 | invert the finished picture while held |
/punch/freeze | 0 – 1 | 0 | hold the picture; the set keeps moving underneath |
/punch/strobe | 0 – 1 | 0 | beat-synced strobe while held |
/punch/strobe_div | 0.25 – 4 | 0.5 | beats per strobe cycle |
/color/palette | 0 – 15 | 0 | palette row: 0 hsv · 1 warm · 2 ember · 3 ice · 4 neon · 5+ loaded palettes |
/color/spread | 0 – 1 | 0.12 | how much of the palette the field spans |
/color/drive | 0 – 3 | 0 | what picks the colour: 0 index · 1 radius · 2 depth · 3 height |
/cloud/a | 0 – 8 | 0 | first slot of the cloud morph pair |
/cloud/b | 0 – 8 | 1 | second slot of the cloud morph pair |
/cloud/morph | 0 – 1 | 0 | blend position between the pair |
/video/depth | -2 – 2 | 0.6 | how far the picture's relief pushes along z; 0 is flat |
/video/relief | 0 – 3 | 0 | what pushes it: 0 luminance · 1 hue · 2 saturation · 3 chroma |
/lN/kind (N = 1–4) | 0 – 7 | 0 | layer generator: off · rings · stripes · checker · polygon · star · rays · dots |
/lN/freq (N = 1–4) | 0.5 – 64 | 8 | pattern frequency |
/lN/phase (N = 1–4) | 0 – 1 | 0 | pattern phase offset; unsmoothed, so steps snap |
/lN/drift (N = 1–4) | -2 – 2 | 0.1 | how fast the pattern walks on its own, turns/sec of visual time; 0 is still, negative reverses |
/lN/duty (N = 1–4) | 0.05 – 0.95 | 0.5 | ink/paper ratio within a period |
/lN/sides (N = 1–4) | 2 – 16 | 4 | polygon/star sides; a sweep, fractional counts morph |
/lN/inset (N = 1–4) | 0 – 1 | 0.5 | star valley depth |
/lN/fold (N = 1–4) | 0 – 12 | 0 | kaleidoscope wedges; below 2 is off |
/lN/invert (N = 1–4) | 0 – 1 | 0 | 0 fill · 1 invert |
/lN/x (N = 1–4) | -2 – 2 | 0 | layer centre, sideways |
/lN/y (N = 1–4) | -2 – 2 | 0 | layer centre, vertical |
/lN/rot (N = 1–4) | -2 – 2 | 0 | layer rotation, turns |
/lN/scale (N = 1–4) | 0.05 – 8 | 1 | layer scale |
/lN/color (N = 1–4) | 0 – 3 | 0 | which ink the layer prints with |
/lN/blend (N = 1–4) | 0 – 6 | 0 | normal · multiply · screen · add · difference · exclusion · subtract |
/lN/opacity (N = 1–4) | 0 – 1 | 1 | layer opacity |
/pal/0/r | 0 – 1 | 0.05 | ink 0 (near-black), r channel |
/pal/0/g | 0 – 1 | 0.05 | ink 0 (near-black), g channel |
/pal/0/b | 0 – 1 | 0.05 | ink 0 (near-black), b channel |
/pal/1/r | 0 – 1 | 0.92 | ink 1 (red), r channel |
/pal/1/g | 0 – 1 | 0.1 | ink 1 (red), g channel |
/pal/1/b | 0 – 1 | 0.14 | ink 1 (red), b channel |
/pal/2/r | 0 – 1 | 0.1 | ink 2 (blue), r channel |
/pal/2/g | 0 – 1 | 0.3 | ink 2 (blue), g channel |
/pal/2/b | 0 – 1 | 0.95 | ink 2 (blue), b channel |
/pal/3/r | 0 – 1 | 0.98 | ink 3 (yellow), r channel |
/pal/3/g | 0 – 1 | 0.8 | ink 3 (yellow), g channel |
/pal/3/b | 0 – 1 | 0.05 | ink 3 (yellow), b channel |
/vec/place | 0 – 1 | 0 | where the stack renders: 0 scene (behind particles, in the feedback chain) · 1 print (after post, exact ink) |
/camera/distance | 0.4 – 12 | 3.5 | orbit distance from the field |
/camera/orbit | -3.15 – 3.15 | 0 | orbit angle around the field |
/camera/elevation | -1.4 – 1.4 | 0.34 | height angle of the orbit |
/camera/fov | 0.2 – 2 | 0.9 | field of view, radians |
/camera/focus | 0 – 12 | 3.5 | focus distance |
/camera/defocus | 0 – 1 | 0 | depth-of-field blur amount |
/camera/pan_x | -4 – 4 | 0 | sideways pan of the view |
/camera/pan_y | -4 – 4 | 0 | vertical pan of the view |
/camera/at_x | -8 – 8 | 0 | where in the world the camera is aimed, X |
/camera/at_y | -8 – 8 | 0 | where in the world the camera is aimed, Y |
/camera/at_z | -8 – 8 | 0 | where in the world the camera is aimed, Z |
/camera/move | 0 – 10 | 0 | canned path: off, orbit, sway, push, pull, crane, spiral, look around, fly through, walkthrough, drift |
/camera/move_bars | 1 – 64 | 8 | bars per cycle of the move |
/camera/move_size | 0 – 1 | 0.5 | how far the move travels |
/room/brightness | 0 – 1 | 0 | wireframe room visibility |
/room/depth | 1 – 20 | 7 | how deep the room extends |
/room/fade | 0 – 1 | 0.75 | distance fade of the room lines |
/room/converge | 0 – 1 | 0.35 | perspective convergence of the grid |
/room/vanish_x | -1 – 1 | 0 | vanishing point, sideways |
/room/vanish_y | -1 – 1 | 0 | vanishing point, vertical |
/room/anchor | 0 – 1 | 0.35 | where the cloud sits between front and back |
/room/embed | 0 – 1 | 0 | how much the room's perspective bends the cloud |
/light/ambient | 0 – 1 | 1 | how much light there is everywhere; 1 is the unlit picture |
/light/shape | 0 – 1 | 1 | how much a surface's own orientation counts (needs normals) |
/light/torch | 0 – 1 | 0 | lamp 1 rides the camera |
/light/N/x (N = 1–2) | -6 – 6 | 0 | lamp N position, X |
/light/N/y (N = 1–2) | -6 – 6 | 0 | lamp N position, Y |
/light/N/z (N = 1–2) | -6 – 6 | 0 | lamp N position, Z |
/light/N/radius (N = 1–2) | 0.05 – 8 | 1.5 | lamp N reach |
/light/N/level (N = 1–2) | 0 – 2 | 0 | lamp N brightness |
/light/N/hue (N = 1–2) | 0 – 1 | 0 | lamp N colour |
/light/N/tint (N = 1–2) | 0 – 1 | 0 | how much of lamp N's colour to use; 0 is white |
/sun/level | 0 – 2 | 0 | directional key brightness (needs normals) |
/sun/azimuth | -3.15 – 3.15 | 0.8 | which way the sun is, around |
/sun/elevation | -1.57 – 1.57 | 0.6 | which way the sun is, up |
/sun/hue | 0 – 1 | 0.12 | sun colour |
/sun/tint | 0 – 1 | 0 | how much of the sun's colour to use; 0 is white |
/gravity/amount | 0 – 1 | 0 | master depth of the gravity layer |
/gravity/N/x (N = 0–3) | -3 – 3 | 0 | well N position, X |
/gravity/N/y (N = 0–3) | -3 – 3 | 0 | well N position, Y |
/gravity/N/z (N = 0–3) | -3 – 3 | 0 | well N position, Z |
/gravity/N/strength (N = 0–3) | -2 – 2 | 0 | pull (positive) or push (negative) |
/gravity/N/radius (N = 0–3) | 0.05 – 4 | 1 | well reach |
/gravity/fire | 0 – 16 | 0 | fire gravity scene 1–16 on change; 0 = none |
/gravity/time | 0 – 60 | 2 | gravity blend time, seconds |
/gravity/curve | 0 – 4 | 1 | 0 linear · 1 smooth · 2 ease in · 3 ease out · 4 cut |
/gravity/auto | 0 – 1 | 0 | gravity autopilot on/off |
/gravity/bars | 0.25 – 16 | 4 | bars between gravity autopilot steps |
/bg/red | 0 – 1 | 0.004 | background red |
/bg/green | 0 – 1 | 0.004 | background green |
/bg/blue | 0 – 1 | 0.008 | background blue |
/bg/alpha | 0 – 1 | 1 | background opacity; 0 delivers the field on nothing |
/preset/recall | 0 – 64 | 0 | recall preset N on change; 0 = none |
/scene/fire | 0 – 16 | 0 | fire scene 1–16 on change; 0 = none |
/scene/time | 0 – 60 | 2 | scene blend time, seconds |
/scene/curve | 0 – 4 | 1 | 0 linear · 1 smooth · 2 ease in · 3 ease out · 4 cut |
/scene/auto | 0 – 1 | 0 | scene autopilot on/off |
/scene/bars | 0.25 – 16 | 4 | bars between scene autopilot steps |
/deck/select | 0 – 24 | 0 | turn to page 1–24 on change; 0 = none |
/column/fire | 0 – 16 | 0 | fire column 1–16 — the scene pad and the gravity pad of that number, together |
/record/active | 0 – 1 | 0 | record the master to a PNG sequence; 1 starts, 0 stops |
/master/dim | 0 – 1 | 1 | master fader |
Files and folders
~/.config/vizz/projects/<show>/ | One show — everything below this line lives in one of these |
presets/ | Your saved looks, one JSON each |
patches/ | Modulation graphs |
grid.json | The scene grid, as the live deck left it |
gravity-grid.json | The gravity grid, likewise |
decks.json | The set list: every page of pads, and which one is live |
macros.json | Your fader assignments |
~/.config/vizz/open.json | Which show is open |
~/.config/vizz/midi.json | Your controller map — the rig, so it stays put when the show changes |
~/.config/vizz/settings.json | Output size, audio device, loaded clouds, fullscreen — the rig too |
~/Movies/vizz/ | Recordings (macOS; ~/Videos/vizz/ elsewhere) |
Built-in presets are compiled into the binary rather than written to disk, so they are always present, cannot be half-installed, and cannot be lost by clearing the config directory.
Upgrading from a build without shows moves what was in
~/.config/vizz/ into projects/Show 1/ on first launch.
Nothing is copied and nothing is left behind.
Command line
vizz --help # every flag
vizz --width 1920 --height 1080 # output size for this run
vizz --fullscreen --monitor 1 # start on a projector
vizz --ndi --no-syphon # NDI only
vizz --audio-device "Scarlett" # match an input by substring
vizz --list-audio # print input names and exit
vizz --cloud scan.ply # load a cloud at startup
vizz --osc-port 7001 # listen somewhere else
vizz --headless --frames 600 --report bench.json
On macOS, double-clicking the app runs 1280×720 with OSC on 7000 and Syphon on; the window title shows the live settings. Flags need a terminal run.
Troubleshooting
I loaded a cloud and nothing changed
Loading one points /shape/mode at the cloud pair for you, so the
usual cause is something moving it back: a preset, a scene, or a modulation
route driving /shape/mode. Check what the shape
group's mode row reads — cloud pair is the position that
shows clouds — and whether it carries a ~.
The picture goes white and stays white
/fx/trail is very high and the frame is saturating. Bring it
down. (It is capped below 1.0 precisely so this is recoverable.)
Nothing reacts to the music
Check the panel's audio section shows a device and the meters are moving. If the bars move but nothing on screen does, no modulation is routed yet — see the graph. If the bars are pinned to full, press fit.
My controller does nothing
Open the panel's midi section: if the device is not listed, it is a connection problem, not a mapping one. If it is listed, arm a learn and watch the echo line — it names whatever arrives.
The receiving app cannot see vizz
Check the status line: each output has a dot, green when live. Syphon is macOS-only. NDI needs its runtime installed, and the panel says so if it is missing.
I set layer parameters and nothing changed
Check the layer's kind is not off — kind 0 removes the
layer regardless of its other settings — and check /vec/place:
in print placement the stack replaces the finished frame, so if
another look is on screen the stack may be in scene placement behind
an opaque particle field. Recalling a vector preset resets all of this.
Frame rate is low
Open health in the panel for frame-time percentiles and the
over-budget count. Lower the render multiplier first, then
/particles/count. Heavy feedback plus glow at 4K is the usual
culprit.
It asks twice before quitting
That is deliberate. Esc is one key next to nothing, and a single press ending the show is the one destructive keystroke worth guarding.