cue:osc --- Discrete OSC Event Cue
osc(...) sends a single OSC message when triggered.
It is event-based, not continuous. It turns drawn objects into discrete control events, typically consumed by environments like SuperCollider, Pd, or Max.
It works especially well with propagate(), because a simple drawing
can become many individual OSC triggers.
BASIC FORM
osc(addr:
Required:
Key Meaning
addr OSC address name (without leading /)
Example:
osc(addr:voice, pitch:y, env:size)
EVENT BEHAVIOUR
- Sends one OSC message per trigger
- No looping, no animation, no scheduler
- Values are sampled at the moment of triggering
- Cue completes immediately
osc() is a logical cue, not an animation cue.
VISUAL PARAMETER SOURCES (NORMALISED)
All visual sources are normalised to 0–1.
Source Meaning
x Object centre X position
y Object centre Y position
size Max(width, height) mapped to viewport
scale Current visual scale value
rotation 0--360 mapped to 0--1
opacity Computed opacity
fill Numeric colour hash
Example:
osc(addr:voice, pitch:y, amp:size)
Here the Y-axis produces a continuous control pitch, interpreted downstream.
SEMANTIC PITCH VALUES
Beyond visual values, osc() supports typed pitch declarations.
Hz (absolute)
osc(addr:voice, pitch:hz(440))
MIDI note number
osc(addr:voice, pitch:midi(60))
Scale degree + octave
osc(addr:voice, pitch:deg(5,3))
Represents:
- scale degree = 5
- absolute octave = 3
The receiver chooses how to interpret the scale.
OPTIONAL ROOT OVERRIDE
You may specify a root in the cue:
osc(addr:voice, pitch:deg(2,4), root:48)
If omitted, the receiver uses its global default (~oscRoot in SC).
PITCH PRIORITY
When multiple forms are present, resolution is:
deg(d,o)(scale-based symbolic pitch)hz()/midi()(absolute pitch)- visual mapping (e.g.,
pitch:y)
Only one pitch representation is used per event.
NOTE EVENTS — TWO APPROACHES
gate: — geometry-driven (recommended for piano-roll scores)
Pair two elements: an on-rect and an off-rect positioned at the note
boundaries. Both use pitch:y so moving them in Inkscape changes pitch.
Scaling the containing group on X adjusts timing without affecting pitch
or note-off alignment.
gate:1 and gate:0 are appended as the last OSC arg. No setTimeout is used.
Generated automatically by scripts/midi-to-svg.js.
dur: — timer-based
When dur: is supplied, a single element sends note-on at trigger time
and note-off after dur seconds via setTimeout. Useful for hand-authored
cues where element geometry doesn't represent note duration.
osc(addr:voice, pitch:midi(60), dur:1.5, trig:playhead)
Caveat: scaling the element in Inkscape after the fact does not
update the baked-in dur: value.
AMPLITUDE OVERRIDE (amp:)
amp: accepts a static 0–1 value that overrides the visual size
field in the OSC payload. Useful when notes are generated from MIDI
velocity rather than element geometry.
osc(addr:voice, pitch:midi(64), amp:0.75, dur:0.5, trig:playhead)
TRIGGERS
Key Meaning
trig:auto Send immediately
trig:playhead Fire when playhead intersects
trig:click Fire on user click
prestate:ghostClickable Arm interaction ahead of time
Example:
osc(addr:voice, pitch:deg(0,4), trig:playhead)
INTERACTIVE BUTTON MODE (click:1)
Adding click:1 to any osc() element turns it into a clickable button.
An invisible HTML overlay is placed over the SVG element; clicks trigger
the cue. The SVG element itself provides the visual appearance.
toggle:1 — binary toggle with explicit state
toggle:1 makes the button cycle between two states. A green outline
appears when the button is on. Crucially, unlike a plain trigger, a toggle
button sends the new state explicitly as 0 or 1 (not always 1).
osc(addr:"arp/v0/mute", click:1, toggle:1)
SuperCollider — SET state, do not toggle blindly:
// ✓ Correct — sets state from explicit value OSCdef(\mute, { |msg| ~muted = msg[1] > 0.5; }, '/oscilla/arp/v0/mute');
// ✗ Wrong — will desync if any message is lost OSCdef(\mute, { |msg| ~muted = ~muted.not; // never do this with toggle:1 }, '/oscilla/arp/v0/mute');
Because msg[1] is the authoritative new state, a single missed or
duplicated message will not cause permanent desync. The next click
always resynchronises both sides.
radio:groupName — exclusive selection
Radio buttons form a group where only one can be active at a time. Clicking a radio button activates it and deactivates all others in the same group. The active button gets a green outline.
<rect id="osc(addr:"gong/a/lp", click:1, radio:filterA)" .../> <rect id="osc(addr:"gong/a/bp", click:1, radio:filterA)" .../> <rect id="osc(addr:"gong/a/hp", click:1, radio:filterA)" .../>
Each radio button fires its own OSC address when selected. SC sets the parameter to a fixed value per button (not toggle logic):
OSCdef(\lp, { |m| ~synth.set(\filterType, 0) }, '/oscilla/gong/a/lp'); OSCdef(\bp, { |m| ~synth.set(\filterType, 1) }, '/oscilla/gong/a/bp'); OSCdef(\hp, { |m| ~synth.set(\filterType, 2) }, '/oscilla/gong/a/hp');
labels:'OFF_TEXT,ON_TEXT' — two-state button text
When combined with toggle:1, swaps the text of the next sibling SVG
<text> element each time the button is clicked.
- First value — shown when toggle is off (e.g. voice playing → show "PAUSE")
- Second value — shown when toggle is on (e.g. voice paused → show "PLAY")
<rect id="osc(addr:"arp/v0/mute", click:1, toggle:1,
labels:'PAUSE,PLAY')"
x="189" y="928" width="90" height="55" rx="6"
style="fill:#666666"/>
<text x="234" y="959" text-anchor="middle" fill="white">PLAY</text>
Initial state: buttons with labels: initialise in the on state
(toggle on, green outline, showing the on-label). This means the SVG
initial text should be the on-label (e.g. PLAY). The corresponding SC
state should also start in the "on" (e.g. paused) condition so both sides
agree without a first-click resync.
group:groupName — preset group registration
Registers the button with a named o2p fader group so its state is captured and restored when presets are saved and recalled.
osc(addr:"arp/v0/mute", click:1, toggle:1, group:arpFaders, labels:'PAUSE,PLAY')
The group name must match the group: parameter used on the fader
elements (o2p(path:..., group:arpFaders, ...)). On preset recall,
toggle buttons are restored by programmatically clicking them, which also
fires the OSC message to resync SC.
Combined example — full mute button
<!-- Voice 0 MUTE — starts paused (PLAY showing), click to run, click again to pause -->
<rect
id="osc(addr:"arp/v0/mute", click:1, toggle:1, group:arpFaders, labels:'PAUSE,PLAY')"
x="189" y="928" width="90" height="55" rx="6"
style="fill:#666666;stroke:#444444;stroke-width:2"/>
<text x="234" y="959" font-size="17px" font-weight="700"
text-anchor="middle" fill="white" pointer-events="none">PLAY</text>
SuperCollider side:
// Start paused (matches button initial on-state)
~players[0].pause;
~muted[0] = true;
OSCdef(\arp_v0_mute, { |msg|
~muted[0] = msg[1] > 0.5;
if (~muted[0]) { ~players[0].pause } { ~players[0].resume };
}, '/oscilla/arp/v0/mute');
Parameter reference
| Parameter | Values | Description |
|---|---|---|
click:1 |
1 |
Make SVG element clickable via invisible overlay |
toggle:1 |
1 |
Binary toggle; sends explicit 0 or 1 to SC |
radio:name |
string | Exclusive radio group name |
labels:'A,B' |
'off_text,on_text' |
Two-state sibling text update |
group:name |
string | Register with o2p preset group |
UID (OPTIONAL)
osc(addr:voice, pitch:y, uid:v1)
UID can be used downstream to associate events with voices/players/etc.
OSC OUTPUT FORMAT (AUTHORITATIVE)
osc() currently sends positional arguments, not keyed pairs.
GENERAL PACKET FORMAT (without dur:)
/oscilla/
PACKET FORMAT WITH dur: (note events)
/oscilla/
Where gate is 1 for note-on and 0 for note-off. All other fields
are identical between the two messages.
Where:
Field Meaning
pitchType 0=none, 1=deg/oct, 2=hz, 3=midi, 4=y-control
pitchA meaning depends on pitchType
pitchB octave (deg mode only)
size 0--1 (overridden by amp: if supplied)
env 0--1
density 0--1 (area-derived)
root optional per-event root override
gate 1=note-on, 0=note-off (present when gate: or dur: set)
SuperCollider decoding for piano-roll scores (pitchType 4 = y-control):
~pitchMin = 24; ~pitchMax = 108; midiNote = (~pitchMin + msg[1] * (~pitchMax - ~pitchMin)).round; gate = msg[msg.size - 1]; // 1 = on, 0 = off
Examples
Control pitch from Y
/oscilla/voice 1 5 3 0.41 0.41 0.12
Absolute pitch
/oscilla/voice 2 440 0 0.18 0.22 0.10
Degree + octave + root override
/oscilla/voice 1 5 3 0.33 0.20 0.15 48
Receivers are expected to decode according to this positional layout.
USE WITH propagate()
propagate( osc( addr:voice, pitch:y, env:size, trig:playhead ) )
Degree constellation examples
propagate( osc( addr:pontalist, pitch:deg(irand(0,11), irand(0,2)), env:size ) )
propagate( osc( addr:pontalist, pitch:deg(${1}, ${2}), env:size, root:48 ), rnd([0,2,4,5,7,9,11]), rnd([0,1,2,3,4,5]) )
DESIGN NOTES
- stateless
- no DOM ID use
- no continuous OSC streaming
- no scale-logic baked into DSL
- musical interpretation lives downstream
- arguments are positional, compact, and receiver-focused
SUMMARY
osc()converts visual gestures into one-shot OSC control events, supporting symbolic, absolute, and control-driven pitch --- while leaving musical semantics to the instrument.
Tip: use ← → or ↑ ↓ to navigate the docs