Skip to content

Parameter Priority

Every slider in Lightbridge has a single number behind it. That number might be sitting on the slider you dragged, coming out of an audio-reactive modulation modulation modulation Drives a parameter automatically. Can run from audio, an LFO, or the beat. Learn more → , tracking a MIDI knob on your controller, or getting overridden by a palette swatch — sometimes all of those things at once. This page is about what happens when those layers disagree.

The five layers

A parameter’s final value is resolved from the top of this list down:

  1. MIDI CC MIDI binding midi A physical knob, fader, or pad on a controller wired to a parameter. Sits at the top of the priority chain. Learn more → — if the parameter is bound to a MIDI control, the physical knob wins.
  2. Modulation — if the parameter has a modulation, the modulation result wins.
  3. Palette palette palette Seven named colours per set, bound to any source's colour parameters. Change the swatch, recolour every bound source at once. Learn more → — if the parameter is a colour component and is bound to a palette swatch, the swatch value wins.
  4. Slider (stored) — the value stored in the show file. This is what you’d see if nothing else were active.
  5. Factory default — the value the parameter ships with, used when nothing else is set.

The rule is simple: the highest layer that has a value wins. You don’t average. You don’t combine. You don’t crossfade. You pick.

Why it’s a priority chain, not a sum

The priority model is a deliberate design choice. Every other model we tried made live performance harder:

  • Summing (slider + mod + palette) drifts unpredictably. A small modulation on top of a hand-moved slider turns into something you can’t reason about in the dark.
  • Averaging makes modulation feel limp. You can never fully commit to a mod.
  • Last-writer-wins depends on timing, which is unstable frame-to-frame.

Picking the highest active layer means two things are always true: (a) if you grab a MIDI knob, you know it will take over — there is no other layer that can override it; (b) if you turn off every override, the slider value is exactly where you left it.

A worked example

Suppose the Scale parameter on your Voronoi source has:

  • Slider dragged to 0.6
  • A modulation bound to the kick band, range 0.3 → 0.9, currently outputting 0.72
  • A MIDI CC from your controller’s knob 3 currently at 0.55
  • No palette (Scale isn’t a colour param)

Priority evaluation:

LayerValueActive?Winner?
MIDI CC0.55Yes
Modulation0.72Yes
PaletteNo
Slider0.6
Defaultvaries

MIDI is the highest active layer, so Scale resolves to 0.55 this frame. The modulation is still running, the slider is still at 0.6, but neither of them is what the renderer sees.

Now imagine you let go of the MIDI knob and unbind it. MIDI drops out of contention. Modulation is the next active layer, so Scale is now 0.72. The slider still hasn’t moved.

Delete the modulation. Modulation drops out. Scale is now 0.6 — your slider value.

What about palette?

The palette’s priority position is subtle. The palette only overrides parameters that are colour sliders (hue, saturation, brightness on a source’s main colour, background colour, etc.). For those, the palette substitutes the swatch’s HSB value when the source has bound that colour group to a channel. Any parameter that isn’t a colour, or has its palette channel set to Off, is invisible to the palette and falls through to the next layer.

This is why binding the hue of two different sources to the same swatch recolours them both when you edit that swatch: the palette runs for every colour parameter independently and pushes the same swatch value into each.

Advanced — how the priority chain is implemented

The priority chain isn’t scattered across the codebase — it’s implemented as a sequence of overlays, each of which reads the current value and returns either the same value (passthrough) or an overridden one:

store → palette overlay → modulation overlay → (MIDI CC write on bind) → render

(applyModOverlay.ts is the function that does this for modulation; the palette has its own overlay step; MIDI writes directly to the store when a CC moves.)

Two performance details matter:

  • Delta gating — an overlay only pushes a new value when it has changed by more than 1e-5. Kills a huge amount of wasted work when a modulation is steady or a knob is parked.
  • Structural sharing — if the modulation overlay determines that nothing changed this frame, it returns the original object reference instead of a new one. That lets React skip re-renders at the component level with zero work.

You don’t need to know any of this to use the app, but it’s why modulation on 200 params doesn’t cost 200× more than modulation on one param.

Gotchas

  • A slider that doesn’t seem to do anything almost always means a higher layer is active. Check for a modulation icon on the slider, a MIDI binding label, or a palette channel chip.
  • A modulation that doesn’t seem to do anything can mean a MIDI bind is overriding it. Unbind the MIDI, or re-check which controller element is on the same address.
  • A colour parameter that won’t move can mean it’s bound to a palette channel. Set the channel to Off and the slider comes back to life.
  • Changes to the slider still persist even when a higher layer overrides them. Your drag updates the stored value; it just doesn’t take effect this frame. When the override drops, your drag is what comes back.

Where to go from here