cue:o2p — Object-to-Path Animation

o2p(...) animates an SVG object along a named <path>.
It supports path traversal, directional modes, rotation modes, looping, OSC output, partial-path motion, trigger-based activation, delayed start, visual pre-start states, interactive touch/drag control, grouped fader presets with launcher bars, pause-drag repositioning (spatial scores), live console hot-reload, and multi-client sync.


BASIC FORM

o2p(path:<id>, dur:<seconds>, mode:fwd)

Required:

Key Meaning
path The ID of the <path> element to follow

TIMING

dur:<seconds>

Duration of one full traversal of the path.

o2p(path:orbit, dur:6)

In mode:alt, the full A→B→A cycle lasts dur seconds.

tdelay:<seconds>

Delays the start of the animation after the cue triggers.

o2p(path:orbit, tdelay:3)

tdelay is real-time and independent of score position.


SPEED CONTROL

bipolar:1

Enables bipolar speed mode for duration-bound animations. This transforms the speed fader from unipolar (slow→fast) to bipolar (reverse→stop→forward).

Fader Position Speed Effect
Left (0) Maximum reverse speed (anticlockwise)
Center (0.5) Stopped
Right (1) Maximum forward speed (clockwise)
o2p(path:orbit, dur:fader1.t[0.1,4], bipolar:1)

Use Cases:

Without bipolar (default), the speed fader is unipolar:


INITIAL VISIBILITY

init:

Controls how the object looks before the animation begins.

Value Effect
show Visible immediately (default)
hide Hidden until triggered
ghost Semi-transparent (30% opacity), no interaction
fadein(seconds) Fade in over specified seconds
armed Ghost opacity (0.7), click to start animation
armed(opacity) Custom ghost opacity, click to start
armed(opacity, fade) Custom opacity and fade duration, click to start

The object is positioned at its correct starting location before motion begins.

o2p(path:orbit, tdelay:4, init:hide)
o2p(path:ring, dur:8, init:fadein(2), trig:playhead)
o2p(path:spiral, dur:12, init:armed(0.3))

DIRECTION MODES

Mode Meaning
forward / fwd 0 → 1 along path
reverse / rev 1 → 0
alternate / alt back-and-forth motion
o2p(path:curve, mode:alt, dur:8)

Alternate and path shape

Alternate mode is path-shape-aware:


PAUSE-DRAG REPOSITIONING

drag:1

Enables free drag repositioning when an armed animation is paused. Default is off.

o2p(path:orbit, dur:10, init:armed, drag:1)

Without drag (default): click pauses the animation in place, click again resumes from the same position. Standard behaviour for musical scores.

With drag:1: click pauses, then the object can be dragged freely off the path. On resume, the object snaps to the nearest point on the path and continues from there. The original path range (start/end) is preserved — only the first cycle after drag starts from the new position; subsequent cycles use the full original range.

This is designed for spatial audio scores where a performer repositions a sound source during a pause, then resumes the trajectory from the new location.

o2p(path:traj_spiral, dur:25, init:armed(0.7, 18), drag:1,
    spatial:dome, bounds:dome_bounds, format:aed, osc:1,
    oscAddr:"/iem/source/1")

PATH SEGMENTS

o2p(path:ring, start:0.2, end:0.8)

The object travels only between normalized positions start and end.


ROTATION

rotate: Description
none no rotation
aligned align to tangent direction
locked fixed heading using rotlock
spin continuous rotation (rotspeed, rotdir)

Additional keys:

Key Meaning
rotoffset add angular offset
rotlock fixed heading in degrees
rotspeed seconds per full rotation
rotdir ±1 direction multiplier

LOOPING

o2p(path:orbit, loop:3)
o2p(path:orbit, loop:0)   // infinite

OSC OUTPUT

o2p(path:orbit, osc:true)

Emits:

/o2p/<uid> <pathT> <normX> <normY> <angle>

Custom OSC address:

o2p(path:orbit, osc:true, oscAddr:myController)

Emits:

/myController <pathT> <normX> <normY> <angle>

TRIGGERING

Trigger Behavior
trig:auto starts automatically in page mode
trig:edge starts when the playhead intersects in scroll mode
trig:touch no auto-animation; object is draggable along path

Cues may also be activated programmatically.
tdelay applies after the trigger is detected.


TOUCH MODE (Interactive Control)

The trig:touch mode transforms the o2p animation into an interactive controller. Instead of automatically animating, the object waits for user interaction.

o2p(path:fader, trig:touch, osc:true)

Behavior:

Use Cases:

Example — Linear Fader:

o2p(path:verticalLine, trig:touch, osc:true, oscAddr:fader1)

Example — Circular Knob:

o2p(path:knobArc, trig:touch, start:0.1, end:0.9, osc:true, oscAddr:knob1)

Visual Indicator: Touch mode objects display an orange hit-label ring (instead of purple) to indicate they are draggable.


FADER GROUPS AND PRESETS

Touch-mode faders can be collected into named groups. A group enables saving and recalling all fader positions as named presets, tweening between presets with easing, and sequencing preset recalls over time.

Grouping Faders

Add group:<name> and uid:<id> to each touch-mode fader:

o2p(path:track1, trig:touch, group:mixer1, uid:vol, osc:true)
o2p(path:track2, trig:touch, group:mixer1, uid:pan, osc:true)
o2p(path:track3, trig:touch, group:mixer1, uid:send, osc:true)

All three faders register under window._o2pTouchGroups["mixer1"] and can be saved/recalled as a unit. The group value is freeform — any string.

Launcher Bar

When a group registers, a launcher bar appears below the group's bounding box. It provides direct access to presets and sequences without opening the full preset panel.

The launcher includes:

An orange toggle circle appears to the right of the group. Click it to show or hide the launcher bar.

Preset Manager Panel

The preset panel (keyboard shortcut Alt+Shift+O, or gear button on launcher) provides full preset management:

Console access: window.o2pPresetUI.toggle()

Preset Data

Each preset stores the normalized position of every fader in the group:

{
  "mixer1": {
    "vol": { "t": 0.75 },
    "pan": { "t": 0.3 },
    "send": { "t": 0.5 }
  }
}

If a fader has a rotation handle (hmode:), a p value is also stored:

{ "vol": { "t": 0.75, "p": 0.6 } }

Console API

window.o2pPresets.save("intro")           // save current positions
window.o2pPresets.recall("intro")         // instant recall
window.o2pPresets.recall("intro", { dur: 2, ease: "easeInOutSine" })  // tween
window.o2pPresets.list()                  // list all preset names
window.o2pPresets.delete("intro")         // delete a preset

// Sequences
window.o2pPresets.defineSequence("drift", [
  { preset: "intro", dur: 3 },
  { preset: "verse", dur: 2 },
  { preset: "chorus", dur: 4 }
], { loop: true })
window.o2pPresets.playSequence("drift")
window.o2pPresets.stopSequence()

ROTATION HANDLES

Touch-mode faders can have a secondary rotation handle that adds a second control dimension. This is useful for parameters like pan, filter cutoff, or any value that benefits from a rotary control alongside the linear fader.

hmode:<continuous|limited>

Determines the rotation behavior:

rotrange:<degrees>

Sets the arc range for hmode:limited. Default is 270 degrees.

o2p(path:track1, trig:touch, group:mixer1, uid:vol, hmode:limited, rotrange:270, osc:true)

handle:<elementId>

Reference a user-drawn SVG element as the rotation handle instead of the auto-generated one:

o2p(path:track1, trig:touch, group:mixer1, uid:vol, hmode:limited, handle:knob1, osc:true)

Where knob1 is the ID of an existing SVG element in the score.


TARGET BUTTONS

Click-type o2p elements can control other faders in the same group using the target parameter. This allows creating buttons that set specific fader positions — useful for presets, mute/unmute toggles, or recall buttons.

Basic Target Button

Use target:<faderId> and targetT:<value> to create a button that sets a fader to a specific position when clicked:

o2p(path:btnPath, trig:click, group:mixer1, target:vol, targetT:0.8)

When clicked, this sets the vol fader in mixer1 to t=0.8.

Toggle Button

Use toggle:<val1>,<val2> to alternate between two positions on each click:

o2p(path:btnPath, trig:click, group:mixer1, target:vol, toggle:0,0.8)

First click sets vol to t=0.8, second click sets it to t=0, and so on.

Example: Mute/Unity Buttons for a Mixer

<!-- Fader paths -->
<path id="track0" d="M 0,120 L 0,0" />
<path id="track1" d="M 30,120 L 30,0" />

<!-- Faders -->
<rect id="o2p(path:track0, trig:touch, group:mixer, uid:in0, osc:1)" ... />
<rect id="o2p(path:track1, trig:touch, group:mixer, uid:in1, osc:1)" ... />

<!-- Toggle button paths (minimal) -->
<path id="btn0" d="M 0,130 L 0,131" style="stroke:none" />
<path id="btn1" d="M 30,130 L 30,131" style="stroke:none" />

<!-- Toggle buttons: click to toggle fader between mute and 0dB -->
<rect id="o2p(path:btn0, trig:click, group:mixer, target:in0, toggle:0,0.8, osc:1)" ... />
<rect id="o2p(path:btn1, trig:click, group:mixer, target:in1, toggle:0,0.8, osc:1)" ... />

The toggle values 0,0.8 correspond to:

Parameters

Parameter Description
target:<id> The uid of the fader to control (must be in same group)
targetT:<value> Set fader to this t value (0-1) on click
toggle:<v1>,<v2> Alternate between two t values on each click

Note: target requires group to be set. The target fader must also be registered in the same group.


TOGGLE STATES (Interactive Claiming)

Toggle states provide an interactive claiming mechanism independent of OSC output. Performers can double-click to cycle through named states, with visual feedback and optional callbacks. This is useful for instrument assignment, ownership indication, or any multi-state interaction.

Basic Syntax

o2p(path:orbit, dur:10, toggleStates:[idle, claimed])

Parameters

Key Description
toggleStates Array of state names to cycle through on double-click
toggleColors Optional array of colors per state. Use #client for claiming client's color
onToggle Cue expression to execute when state changes

Toggle Colors

Define colors for each state:

o2p(path:orbit, dur:10,
    toggleStates:[idle, claimed],
    toggleColors:[default, #client])
Color Value Effect
default Use the default overlay color
#client Use the claiming client's assigned color
#ff0000 Any hex color

onToggle Callback

Execute a cue expression when the state changes. Supports variable substitution:

Variable Value
$uid Animation UID
$state New state index (0-based)
$stateName New state name
$clientIndex Client's 1-based index in playbackClientNames preference
o2p(path:orbit, dur:10, uid:src1,
    toggleStates:[idle, claimed],
    toggleColors:[default, #client],
    onToggle:"osc(addr:/source/claim, uid:$uid, state:$state, client:$clientIndex)")

State Persistence

Toggle states are:

Independence from OSC

Toggle states are independent from osc:1. Having toggleStates does not affect continuous OSC output. You can have claiming UI without OSC, OSC without claiming, or both together.


LIVE CONSOLE EDITING

Running o2p animations can be modified in real time from the live console. Pick an element, edit DSL parameters in the editor, and evaluate.

What happens:

Editable parameters: dur, loop, mode, ease, start, end, osc, oscAddr

Identity parameters (path, uid, init) cannot be changed mid-flight.


MULTI-CLIENT SYNC

Armed animations sync across connected clients via WebSocket:

With drag:1, the resume position is transmitted so remote clients restart from the same path location.

Limitation: late-joining clients see armed elements but do not auto-start running animations. A connected client must stop and restart to bring late joiners into sync.


LIVE UPDATE (uid:)

o2p(path:ring, uid:a1)
o2p(uid:a1, mode:alt)

Later cues with the same uid modify running animations.


FULL PARAMETER LIST

Key Description
path required path reference
dur motion duration
mode direction mode
loop repeat count (0 = infinite)
start, end normalized path range
rotate rotation behavior
rotoffset, rotlock, rotspeed, rotdir rotation parameters
ease easing preset
osc OSC emission
oscAddr custom OSC address (without leading /)
uid animation identity tag
trig trigger mode (auto, edge, touch)
tdelay time delay after trigger
init initial visibility: show/hide/ghost/armed
drag enable pause-drag repositioning (default: off)
bipolar bipolar speed mode: center=stop, left=reverse, right=forward (default: off)
spatial spatial mapping mode: dome, quad (see spatialisation docs)
bounds ID of SVG element defining spatial bounds
format spatial coordinate format: aed, xyz
group fader group name (touch mode only)
hmode rotation handle mode: continuous or limited (touch mode only)
rotrange arc range in degrees for hmode:limited (default 270)
handle ID of user-drawn SVG element to use as rotation handle
launcher show launcher bar for group (default: true, set false to suppress)
toggleStates array of state names for double-click cycling
toggleColors array of colors per state (#client for client color)
onToggle cue expression executed on state change

EXAMPLES

o2p(path:orbitA, dur:8, tdelay:3, init:hide)
o2p(path:spiral, rotate:aligned, rotoffset:-90)
o2p(path:ring, start:0.2, end:0.9, mode:alt)
o2p(path:circle, rotate:spin, rotspeed:2, rotdir:-1)

// Touch mode controllers
o2p(path:faderTrack, trig:touch, osc:true, oscAddr:volume)
o2p(path:knobPath, trig:touch, start:0.15, end:0.85, osc:true)

// Grouped faders with preset system
o2p(path:track1, trig:touch, group:mixer1, uid:vol, osc:true)
o2p(path:track2, trig:touch, group:mixer1, uid:pan, osc:true)
o2p(path:track3, trig:touch, group:mixer1, uid:send, osc:true)

// Grouped fader with rotation handle
o2p(path:track1, trig:touch, group:mixer1, uid:vol, hmode:limited, rotrange:270, osc:true)

// Grouped fader with user-drawn rotation handle
o2p(path:track1, trig:touch, group:mixer1, uid:vol, hmode:continuous, handle:knob1, osc:true)

// Spatial score with pause-drag repositioning
o2p(path:traj_spiral, uid:src1, dur:25, mode:fwd, ease:linear,
    init:armed(0.7, 18), drag:1, spatial:dome, bounds:dome_bounds,
    format:aed, osc:1, oscAddr:"/iem/source/1")

// Armed alternate without drag (standard score)
o2p(path:path2, mode:alt, dur:5, loop:0, ease:linear, init:armed)

// Bipolar speed control (center=stop, left=reverse, right=forward)
o2p(path:orbit, dur:speedFader.t[0.5,8], bipolar:1, loop:0)

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