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

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:

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:

  1. deg(d,o) (scale-based symbolic pitch)
  2. hz() / midi() (absolute pitch)
  3. 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.

BYPASS

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.

<rect id="osc(addr:&quot;arp/v0/mute&quot;, 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:&quot;arp/v0/mute&quot;, 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/ pitchType, pitchA, pitchB, size, env, density, [optional root]

PACKET FORMAT WITH dur: (note events)

/oscilla/ pitchType, pitchA, pitchB, size, env, density, [optional root], gate

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


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