Score

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:

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

UID mode


UID Mode — Targeting SVG Animations

The uid parameter targets SVG animation elements by their unique identifier. This is especially useful for:

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:

  1. SVG element — The animated element itself
  2. HTML overlays — Hit labels, OSC controls, waveforms associated with that uid
  3. 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>

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:

This helps performers quickly identify which elements are toggled on or off without needing to search the score.

The indicator updates automatically when:

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):

  1. Saved JSON state — Previously persisted visibility
  2. visible: parameter — Initial state defined in the cue
  3. 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:

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

Selector mode only

Transforms are stateful and combined.

UID mode only


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>

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

Tip: use ← → or ↑ ↓ to navigate the docs