Score

rotate() --- Rotation Cue

rotate(...) rotates an SVG element using either:

demo-rotate — a dot and its path turning together as one group, and an angle sequence stepping by quarter turns
demo-rotate — a dot and its path turning together as one group, and an angle sequence stepping by quarter turns

Rotation can be:


BASIC FORMS

To an angle, and stay

rotate(45)            at once
rotate(135, dur:2)    turning there over 2 beats, from where it is
rotate(-90)           the same as 270

From the live console: sq > rotate(45).

Continuous

rotate(dir:1, dur:1)

Sequence

rotate(values:[0,120,240], dur:2)

Pattern sequences

rotate(values:Pseq([0,45,10],inf), dur:Pseq([1,0.2,2],inf))


⚡ TRIGGERING


⏱ TRIGGER DELAY --- tdelay:<beats>

Delays motion after trigger:

rotate(values:[0,120,240], tdelay:2)


INITIAL VISIBILITY --- init:

Controls appearance before rotation begins.

Value Effect
show Visible immediately (default)
hide Hidden until triggered
ghost Semi-transparent (30% opacity), no interaction
fadein(beats) Fade in over that many beats
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 element adopts the initial rotation angle immediately, then waits.

rotate(values:[0,90,180], tdelay:4, init:ghost) rotate(dir:1, dur:10, init:fadein(1), trig:playhead) rotate(dir:-1, dur:85, init:armed)


🎚 SEQUENCE PARAMETERS

Key Meaning


values list of angles or pattern dur beats per step (or pattern) mode loop, once, alternate interp smooth or step hold pause after tween (smooth only)


CONTINUOUS ROTATION

rotate(dir:1, dur:2) rotate(dir:-1, dur:3, ease:"easeInOutSine")


FOLLOWING --- rotate(follow(...))

The angle can follow a signal, as a scale's size can: the first value (or values:) is the angle, so a follow() there points the object wherever the signal is.

bass > rotate(follow(bass.t, 0, 360)) each note points it: low to high, once round bass > rotate(follow(bass.t, 0, 360), glide:0.3) swinging there over 0.3 beats, not jumping dial > rotate(follow(fader.t, -135, 135)) a knob's pointer

Each new value sets the angle at once; glide: (beats) eases to it. axis:, pivot: and back: work as for any turn, and it publishes its angle. To make the speed follow instead, bind dur: -- rotate(dur:follow(bass.t, 8, 0.3)): 8 beats a turn at one end, 0.3 at the other.


AXIS --- axis:x | y | z

What the turn is about. z (the default) turns the object in the page, as on a clock face. y turns it front to back about the vertical, like a card flipping; x turns it top over bottom about the horizontal.

rotate(dur:3, axis:y) a card turning, again and again rotate(values:[0, 180], dur:1, axis:y) flipped once, to its back rotate(dur:4, axis:x, back:hide) tumbling, seen only from the front

The score is flat (SVG), so the turn has no perspective: the object narrows to its edge, then widens again, mirrored -- the near side never looks bigger than the far one. Everything else is as for any turn: dur, dir, ease, loop, patterns, pivot (the line it turns about passes through it), follow(), sync, OSC.

A pattern draws a new axis as it goes -- at each step of a sequence, each value a following rotate takes, each turn of a spin:

bass > rotate(follow(bass.t, 0, 360), axis:Pchoose(x, y, z)) a new axis every note rotate(dur:2, axis:Pseq(x, y)) x one turn, y the next

An SVG drawing has no back: from behind, the object is its front, mirrored. back:hide hides it while it faces away (an angle between 90° and 270°), as a card shows nothing while its back is to you.


PIVOT --- pivot:[x, y]

An element turns about the centre of its bounding box. pivot:[x, y] turns it about a named point instead, in the element's own units. The point needs no measuring, so it holds while the element is hidden, and it can lie outside the element.

rotate(dir:1, dur:8, pivot:[39.8, 246.3], trig:auto)

scale() takes the same parameter, so an object can grow away from the point it turns about rather than from its own middle.

The older data-pivot="x,y" attribute still works and means the same thing. Where both are present the cue wins, so a pivot can be changed in the DSL editor without hunting through the SVG for one set long ago.

An element turning about a distant point sweeps a wide circle, and the offscreen observer can decide it has left the view and stop it. Add observer:0 when the pivot is far from the element.

A label inside a turning sky, turned back at the same speed about its star, keeps upright and travels with the star:

<g data-pivot="39.8,246.3"
   data-oscilla="rotate(dir:1, dur:follow(speedFader.t, 120, 1000, 670), bipolar:1, trig:auto, observer:0, sync:0, uid:label-a)">
  <text …>label</text>
</g>

OFFSCREEN PAUSING --- observer:0

Animations pause automatically when their element scrolls offscreen (saving CPU/GPU) and resume when it returns. This applies to all animation cues (rotate, scale, fade, color, o2p).

Some animations must keep running offscreen — typically ones whose steps drive audio or OSC. Opt out with:

rotate(values:Pseq(0, 90, inf), dur:0.5, osc:2, observer:0)

Exempt automatically (no observer:0 needed):


UID --- Live Updates

UID lets multiple cues address the same animation:

rotate(values:[0,90,180], uid:r1) rotate(uid:r1, dur:0.5)

OFF --- off:1

Stops the turning where it has got to and leaves the element at that angle:

organ > rotate(off:1)

Run the rotate again to start it from there. In the live console Ctrl+Shift+. on the line does this.


OSC OUTPUT (optional)

Rotation can send OSC every frame or per‑step.

Enable via:

osc:1

Specify an OSC address:

rotate(values:[0,90,180], osc:1, oscaddr:"/spatialisation/source1")

Signals for follow()

A rotation publishes, under its uid (the element's id when no uid: is given):

channel value
angle degrees, 0--360
norm 0--1 around the turn
sin, cos 0--1, smooth: once up and down per turn
vel, tvel speed, in degrees and turns per second

norm is a sawtooth: a spin goes from 1 straight back to 0 each turn. To drive something that should swing rather than jump, such as a pan, follow sin or cos:

shape(uid:sh1, sides:3) rotate(dur:4, loop:0) synth(uid:s2, freq:220, pan:follow(sh1.sin, -1, 1))

Payload format

Every message includes:

Field Meaning


deg wrapped degrees 0--360 rad radians (deg * π/180) norm normalized 0--1 (deg/360) uid animation UID timestamp ms since epoch

Example conceptual payload:

{
  "type": "osc_rotate",
  "uid": "r1",
  "deg": 135.0,
  "rad": 2.356,
  "norm": 0.375,
  "addr": "/spatialisation/source1"
}

NOTE: Internally angles may accumulate, but outgoing OSC always wraps to 0--360.

On‑screen overlays

When OSC is enabled, a small overlay shows:

/spatialisation/source1 — deg:135.0

Overlays can be globally toggled.


NETWORK SYNCHRONIZATION

By default, rotation animations are synchronized across all connected clients. The first client to start an animation becomes the leader and broadcasts positions every 10 seconds. Late-joining clients sync to the leader's position.

sync: — Enable/Disable Sync

Value Effect
sync:1 Enable network sync (default)
sync:0 Disable sync — animation runs independently per client

Use sync:0 for animations that should vary between performers:

rotate(values:[0,90,180], dur:2, sync:0)


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.

Basic Syntax

rotate(dir:1, 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:

rotate(dir:1, 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
rotate(dir:1, dur:10, uid:myRotation,
       toggleStates:[idle, claimed],
       toggleColors:[default, #client],
       onToggle:"osc(addr:/polygon/claim, uid:$uid, state:$state, client:$clientIndex)")

State Persistence

Toggle states are:

Independence from OSC

Toggle states are independent from osc:1. You can have:

// Continuous rotation OSC + claiming system
rotate(dir:1, dur:10, uid:src1, osc:1, oscaddr:"/spat/src1",
       toggleStates:[idle, claimed],
       onToggle:"osc(addr:/claim, uid:$uid, state:$state, client:$clientIndex)")

SIGNAL-BOUND PARAMETERS

Animation speed and duration can be controlled in real-time by binding to fader signals from the control plane.

Signal Reference Syntax

rotate(dur: follow(fader.t, 120, 1), dir:1, uid:spinner)

The signal reference format is:

follow(source.channel, min, max[, default][, curve])
Component Description
source UID of the controlling fader/o2p
channel Signal channel (t, x, y, angle)
min-max Output range (maps 0-1 input to this range)
default Optional starting value
curve Optional curve: lin, exp2, exp3, log, sqrt

Examples

// Duration controlled by fader: 120 beats (slow) to 1 beat (fast)
rotate(dur: follow(speedFader.t, 120, 1), dir:1, uid:wheel)

// With exponential curve for finer slow-speed control
rotate(dur: follow(speedFader.t, 120, 1, exp3), dir:-1, uid:spinner)

// Starting at 60 beats per rotation
rotate(dur: follow(speedFader.t, 120, 1, 60), dir:1, uid:dial)

// Full syntax with default and curve
rotate(dur: follow(speedFader.t, 120, 1, 30, exp3), dir:1, uid:myRotate)

How It Works

  1. The fader publishes its position (0-1) to the control plane
  2. The value is mapped through the specified range and curve
  3. The animation's duration updates in real-time
  4. Shorter duration = faster rotation

See Control Plane documentation for full signal reference syntax.


FULL PARAMETER LIST

Key Description


values angle sequence or pattern dur duration or pattern (supports signal refs) mode loop logic interp step or smooth hold pause after tween dir direction (continuous mode) ease easing curve uid animation identity trig trigger mode tdelay trigger delay init initial visibility state osc enable OSC (0/1/2) oscaddr explicit OSC address sync network sync (0/1, default 1) 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

rotate(values:[0,120,240], dur:4, tdelay:2)

rotate(values:Pshuf([0,180],inf), dur:1, mode:alternate, osc:1, oscaddr:"/fx/rot1")

rotate(dir:-1, dur:3, init:hide, tdelay:1)


Summary

Demo

See demo-rotate — continuous spin, stepped and smoothed value lists, and patterned durations.

http://localhost:8001/?project=demo-rotate

Turning about something that moves — pivot:<id>

pivot:[x, y] fixes the centre for good. Name an element instead and the centre is read from it as the rotation runs, so it turns about wherever that element has got to:

rotate(dir:-1, dur:758, pivot:pole-marker, trig:auto, uid:sky)

This is what a precessing sky needs. The pole is not a place, it is a thing that travels — over the piece it moves from one star to another — and a fixed pivot leaves the sky circling the star it started on, so the trails drawn after the pole has moved still come out centred on the old one. Following the marker, the circles are centred wherever the pole is now.

The centre is re-read every fourth frame, which costs nothing when the pivot is a fixed pair. A marker may be invisible (visibility:hidden): it is measured through its box and matrix, not its client rect, so an unseen marker still works. One inside the turning group is measured in the group's own frame, so it does not chase its own rotation.

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