ui()
ui() applies performer-facing UI changes during playback — most commonly to hide or reveal interface elements such as the playhead, play zone, overlays, or control panels.
It can target:
- HTML elements using a CSS selector
- SVG animation elements using the
uidparameter (including associated HTML overlays)
The UI cue system was primarily designed to manage visibility of the playhead and play zone, allowing scores to shift between guided, open, or reduced-UI performance states.
Syntax
Selector mode (HTML elements)
ui("<css selector>", opacity:<number>, scale:<number>, x:<px>, y:<px>, rotate:<deg>, visible:<bool>, color:<css>, bg:<css>, z:<number>, dur:<beats>)
UID mode (SVG animations + overlays)
ui(uid:<animation-uid>, visible:<bool>, action:toggle, dur:<beats>, click:1)
What it affects
Selector mode
- Works on: HTML UI elements
- Does not affect: SVG score elements
- Multiple matches: All matching elements are updated
UID mode
- Works on: SVG animation elements registered in
oscillaAnimRegistry - Also affects: Associated HTML overlays (hit labels, OSC controls, etc.)
- Single target: Matches by animation uid
UID Mode — Targeting SVG Animations
The uid parameter targets SVG animation elements by their unique identifier. This is especially useful for:
- Hiding/showing animated polygons or objects
- Toggling visibility of elements and their associated overlays
- Creating clickable toggle buttons in the score
Syntax
ui(uid:<animation-uid>, visible:true|false, dur:<beats>)
ui(uid:<animation-uid>, action:toggle, dur:<beats>)
What gets hidden/shown
When targeting by uid, the following are affected:
- SVG element — The animated element itself
- HTML overlays — Hit labels, OSC controls, waveforms associated with that uid
- O2P overlays — Object-to-path animation overlays
Example: Hide a polygon
ui(uid:rot_triangle, visible:false, dur:0.3)
Example: Toggle visibility
ui(uid:rot_triangle, action:toggle)
Starting state — trig:auto
A ui() with trig:auto runs once, after the project has finished loading —
after the saved show/hide state has been restored, so it has the last word:
ui(uid:figureA, visible:false, trig:auto)
keeps a group hidden at the start even if a later cue revealed it in the last session. The playhead never fires it. Reveal it later from a chained cue:
zoom(to:sky, scale:2, dur:6, next:"ui(uid:figureA, visible:true, dur:4)")
dur: fades it in (and out) over that many beats.
Moving a fader — value: and step:
ui(uid:speedFader, value:0.5, dur:3)
ui(uid:speedFader, value:restore, dur:4)
With value: the uid names a touch fader (an o2p(…, trig:touch)), and the
cue moves it as if it were dragged, gliding over dur beats: the handle
moves, everything bound to it with follow() follows, and other clients see
the same. value:restore glides back to where the fader was before the last
value: — so a score can stop a motion bound to a bipolar fader (0.5 is its
centre, at rest) while something is read, and afterwards give back whatever
speed the performer had chosen.
Relative steps — step:
ui(uid:spd1, step:0.05) // faster
ui(uid:spd1, step:-0.05) // slower
A step from wherever the fader is. On a fader that sets the speed of a
bipolar motion (o2p(…, dur:follow(spd1.t, …), bipolar:1)) the step is
measured from the centre: + is faster in whichever direction the motion is
going, − is slower and stops at the centre rather than crossing it, and from
rest + goes forward. On any other fader the step is literal: + moves the
handle right. (value:+0.05 would not do: the parser reads it as 0.05.)
flip, hold, rest, go
| value | does |
|---|---|
flip |
t → 1−t. On a bipolar speed fader: reverse at the same speed. On a 0/1 fader: a switch. |
hold |
Stop at the centre (0.5), keeping the speed it had; pressed again, give that speed back. |
rest |
Stop at the centre, as hold, but never give back. |
go |
Give back a held speed; does nothing to a fader that is moving. |
The held speed belongs to the fader, not to the client that held it: it
travels with the fader's position to every client (and to those that join
later), so anyone can release a hold someone else made. rest and go are
for a bar that stops or starts several faders at once, where a toggle would
flip each one its own way. (stop cannot be a value: it is a cue name.)
Several at once — a list of uids
ui(uid:[spd1,spd2,spd3,spd4], step:0.05)
ui(uid:[spd1,spd2,spd3,spd4], value:[0.62,0.64,0.6,0.63])
uid: takes a list. A single value:/step:/phase: applies to each; a
list pairs with the uids in order.
Seeking a motion — phase:
ui(uid:L1, phase:0.14)
ui(uid:[L1,L2,L3,L4], phase:[0, 0.14, 0.34, 0.61])
Puts a running motion (o2p, rotate, scale, color) at that point of its path
and carries on from there, at whatever speed it has. For o2p the number is a
path position, the same space as start: and end: — so a set of starting
points written as start:s can be returned to with the same numbers. Every
client makes the same jump: the seek is sent with the shared server time it
happened, and a client hearing it later carries it on by the time since.
Then another cue — next:
ui(uid:L1, phase:0.14, next:"ui(uid:spd1, value:0.625)")
ui(uid:[L1,L2], phase:[0,0.14], next:versionSpeeds)
Runs once this cue is done — after dur, at once if there is none. As with
zoom() and text(), the value is a quoted cue, or the id of
an element whose cue is run (give that element trig:none, so only the chain
fires it). A chain is how one press does two things: above, back to the
version's starting point and then to its speed.
A list runs its cues in order, one straight after another, and a quoted cue
can carry its own next: in single quotes:
ui(uid:[L1,L2], phase:[0,0.14],
next:["ui(uid:[spd1,spd2], value:[0.62,0.64])", "ui(uid:[spd1,spd2], value:rest)"])
ui(uid:L1, phase:0, next:"ui(uid:spd1, value:0.62, next:'ui(uid:spd1, value:rest)')")
Who may press it — allow: and lock:
ui(uid:spd1, step:0.05, click:1, allow:[director])
ui(uid:spd1, step:0.05, click:1, allow:[director], lock:"plxLock.t>0.5")
o2p(path:track1, uid:spd1, trig:touch, allow:[director], lock:"plxLock.t>0.5")
allow: names the clients that may operate the control — their name, as
given by ?role= in the link or chosen in the client list. Anyone else's
press does nothing, and a short notice says who it is for. With lock: the
restriction holds only while that condition is true (the same form as
if:, read from the bus), so a score
can be open to everyone and closed to all but a director during a
performance. The switch can be any fader, for instance a small one only the
director may move, toggled by a button:
o2p(path:lockTrack, uid:plxLock, trig:touch, allow:[director])
ui(uid:plxLock, value:flip, click:1, allow:[director])
It is checked wherever a person operates something: click:1 buttons,
dragging (or double-tapping) a touch fader, and clicking an armed motion or an
o2p target button. A cue fired by the score, by OSC or by another client is not
a person pressing here and is not checked — OSC from a pedal in the room still
works while the score is locked.
Creating Clickable Toggle Buttons
Combine ui() with click:1 to create native SVG toggle buttons:
<g data-oscilla="ui(uid:rot_triangle, action:toggle, click:1)">
<rect x="0" y="0" width="50" height="30" rx="4" fill="#d4af37"/>
<text x="25" y="20" text-anchor="middle" fill="#000">TRI</text>
</g>
When clicked, this toggles visibility of the element with uid:rot_triangle and all its associated overlays.
See button() documentation for more on click:1.
Initial Visibility State
When using click:1 with a ui() cue, you can set the initial visibility state of the target element using the visible parameter:
<!-- Target starts HIDDEN - click to show -->
<g data-oscilla="ui(uid:rot_triangle, action:toggle, visible:false, click:1)">
<path d="M 20,-18 L 38,18 L 2,18 Z" style="fill:#d4af37"/>
<text x="20" y="38">TRI</text>
</g>
<!-- Target starts VISIBLE (default) - click to hide -->
<g data-oscilla="ui(uid:rot_pentagon, action:toggle, click:1)">
<path d="M 15,-10 L 25,-3 L 21,9 L 9,9 L 5,-3 Z" style="fill:#9370db"/>
<text x="15" y="22">PENT</text>
</g>
visible:false— Target element starts hidden when the page loadsvisible:trueor omitted — Target element starts visible (default behavior)
This is useful for scores where certain animated elements should be revealed during performance rather than shown from the start.
Toggle State Indicator
When using click:1 or drag-click:1 with action:toggle, the clickable overlay displays a visual indicator showing the current state of the target element:
- Green solid outline — Target element is currently visible
- Red dashed outline — Target element is currently hidden
This helps performers quickly identify which elements are toggled on or off without needing to search the score.
The indicator updates automatically when:
- The toggle button is clicked
- Initial visibility is set via
visible:false - Visibility state is restored from saved JSON
- A preset is recalled that includes visibility state
indicator:0 leaves a button plain, with no outline — for a text button
that only opens and closes a panel:
ui(uid:panelA, action:toggle, click:1, visible:false, indicator:0)
State Persistence
Visibility state is automatically saved to JSON and restored when the score reloads. The indicator reflects the correct state on load by checking (in priority order):
- Saved JSON state — Previously persisted visibility
visible:parameter — Initial state defined in the cue- Default — Visible (true)
Multiple Buttons for Same Target
If multiple toggle buttons target the same uid, all their indicators stay synchronized. Clicking any one of them updates all indicators for that target:
<!-- Both buttons toggle the same element and stay in sync -->
<g data-oscilla="ui(uid:rot_triangle, action:toggle, click:1)">
<rect x="0" y="0" width="45" height="24" fill="#d4af37"/>
<text x="22.5" y="16">TRI</text>
</g>
<g data-oscilla="ui(uid:rot_triangle, action:toggle, click:1)">
<rect x="100" y="0" width="45" height="24" fill="#d4af37"/>
<text x="122.5" y="16">TRI 2</text>
</g>
Preset Integration
Visibility state is included when saving and recalling presets. This allows presets to capture both fader positions AND which UI elements are visible or hidden, restoring the complete performance state.
Built-in UI toggle button
Oscilla includes a GUI button in the bottom-right corner that toggles:
- Playhead
- Play zone
on and off.
This button uses the same internal UI logic as the ui() cue and exists to allow fast switching between visible guidance and reduced interface modes during performance.
Parameters
Common
dur(beats, default0) — Transition durationclick:1— Make the element clickable (creates invisible HTML overlay)drag-click:1— Make the element both draggable AND clickable (click triggers action, drag repositions)tooltip:"text"— Add hover tooltip text
Selector mode only
visible:true|falseopacity(number)x,y(px)scale(number)rotate(degrees)color(CSS color)bg(CSS background color)z(z-index)
Transforms are stateful and combined.
UID mode only
uid:<string>— Target animation by unique identifier; a list[a,b,…]forvalue:/step:/phase:visible:true|false— Set visibility explicitly (also sets initial state when used withclick:1)action:toggle— Toggle current visibility statevalue:<0-1>|restore|flip|hold|rest|go— move a touch fader (a list pairs with a list of uids)step:<±n>— move a touch fader by a stepphase:<0-1>— seek a running motionnext:— a cue to run when this one is doneallow:[names],lock:"cond"— who may press it
Examples
Selector mode (HTML elements)
// Fade out playhead
ui("#playhead", opacity:0, dur:0.3)
// Hide playhead
ui("#playhead", visible:false, dur:0.3)
// Hide multiple elements
ui("#playhead, #playzone", visible:false, dur:0.25)
// Show multiple elements
ui("#playhead, #playzone", visible:true, dur:0.25)
UID mode (SVG animations)
// Hide a polygon animation and its overlays
ui(uid:rot_triangle, visible:false, dur:0.5)
// Show it again
ui(uid:rot_triangle, visible:true, dur:0.5)
// Toggle visibility
ui(uid:rot_pentagon, action:toggle)
Clickable toggle buttons (click:1)
<!-- Create a clickable SVG button that toggles the triangle -->
<g data-oscilla="ui(uid:rot_triangle, action:toggle, click:1)">
<rect x="0" y="0" width="45" height="24" rx="4" fill="#d4af37"/>
<text x="22.5" y="16" font-size="10" text-anchor="middle">TRI</text>
</g>
<!-- Toggle the pentagon -->
<g data-oscilla="ui(uid:rot_pentagon, action:toggle, click:1)">
<rect x="55" y="0" width="45" height="24" rx="4" fill="#9370db"/>
<text x="77.5" y="16" font-size="10" text-anchor="middle">PENT</text>
</g>
Draggable + clickable toggle buttons (drag-click:1)
Use drag-click:1 when you want the toggle button to be repositionable:
<!-- Draggable toggle button - drag to move, click to toggle -->
<g data-oscilla="ui(uid:synthLevels, action:toggle, drag-click:1, visible:false)">
<rect x="0" y="0" width="50" height="48" rx="4" fill="#f0f0f0" stroke="#999"/>
<!-- Drag handle lines at top -->
<line x1="15" y1="6" x2="35" y2="6" stroke="#888" stroke-width="1.5"/>
<line x1="15" y1="10" x2="35" y2="10" stroke="#888" stroke-width="1.5"/>
<!-- Icon -->
<text x="25" y="38" text-anchor="middle" font-size="18">🎚</text>
</g>
- Click triggers the toggle action (show/hide the target element)
- Drag repositions the button (position is persisted)
- The drag handle lines indicate it's draggable
Numbers from a signal
opacity, scale, rotate, x, y and dur all take a follow():
ui(selector:".panel", opacity: follow(adc.amp), dur:0)
These are sampled when the cue fires, not followed. A ui() cue is a
command — "put this element at opacity 0.5 over 0.3 beats" — carried out by a CSS
transition; a value that kept arriving would be writing the very property the
transition is ramping, and the two would fight.
Each parameter has its own range, so a bare follow(src.t) lands somewhere
sensible: opacity 0–1, scale 0–4, rotate 0–360°, x/y ±500px,
dur 0–10 beats. Give follow() your own numbers to override.
Design notes
- Intended for performance-state control
- Touch- and tablet-friendly
- Reduces visual clutter
- Suitable for audience-facing projections
- UID mode allows control over animated elements and their overlays as a unit
click:1enables creation of custom toggle buttons using native SVG visualsdrag-click:1combines draggability with click actions for repositionable controls
Tip: use ← → or ↑ ↓ to navigate the docs