OSC Output — Oscilla → Max, Pd, SuperCollider

Almost every moving or interactive object in an Oscilla score can stream its state to other software as OSC. The server sends UDP to 127.0.0.1:57120 by default — sclang's port, so SuperCollider hears it with no setup. Every address starts with /oscilla/.

This page is the receiving side. For messages into Oscilla see OSC Input; for the discrete osc() event cue see cue:osc.


Example patches

Three ready-made receivers, one panel per object type, with bangs, faders and number boxes wired to every address in the table below:

Environment File Notes
Pure Data oscilla-osc-in.pd vanilla 0.47+, no externals (netreceive -u -boscparse)
Max oscilla-osc-in.maxpat stock objects only (udpreceiveroute)
SuperCollider oscilla-osc-in.scd OSCdefs driving a Qt window; sine blip per notation note when the server is booted

Open the demo project demo-osc-send in Oscilla, open one of the patches, press play. Sections A–F of that score drive o2p, oscCtrl and osc() triggers; section G holds rotate, scale, fade, trans, metro, synth and notation objects, all with osc:1. The same three files ship inside the demo project folder (pd/, sc/, max/).

To point them at your own score, change the uid in each route / OSCdef to the uid of your object.


Turning OSC on

Every destination receives every message. This is not a split: each application hears the whole stream and ignores what it does not recognise. Two programs on one machine cannot share a UDP port, which is the only reason more than one destination is needed.

Rates: continuous streams (rotate, scale, fade, trans, o2p, control lanes) are throttled to one message per address every 33 ms in the browser and de-duplicated within 30 ms on the server. Discrete events (triggers, waypoint arrivals, notation notes, metro beats) are never dropped.


Address reference

f float, i int, s string. <uid> is the object's uid: (or its element id when none is given).

Object Address Arguments
rotate(osc:1) /oscilla/rotate/<uid> deg f rad f norm f (0–1)
scale(osc:1) /oscilla/scale/<uid> sx f sy f (1 = authored size)
fade(osc:1) /oscilla/fade/<uid> opacity f
trans(osc:1) /oscilla/trans/<uid> total f (whole waypoint list, 0–1) legT f (current leg) x f y f idx i (waypoint being headed for, −1 = home)
/oscilla/trans/<uid>/waypoint i — sent on each arrival
o2p(osc:1) /oscilla/o2p/<uid> t f (path position) normX f normY f angle f worldX f worldY f sweepX f sweepY f p f
/oscilla/o2p/<uid>/vertex i — sent when passing a path vertex
controlXY() /oscilla/controlXY/<uid> (…/<handleId> with several handles) normX f normY f [normP f]
oscCtrl(addr:…) /oscilla/control/<addr> v f (the lane's value, already scaled to its min:max:) t f (position along the lane, 0–1)
oscOut(addr:…, a: follow(…), b: follow(…)) /oscilla/<addr> one f per named parameter, in the order written — any signal, already mapped by its follow()/map(). See Sinks
osc(addr:…) /oscilla/<addr> pitchType f pitchA f pitchB f size f env f density f [root f] [gate f] — see cue:osc
osc(click:1, toggle:1) /oscilla/<addr> state i (0/1)
notation() /oscilla/notation/<uid>/note i i midi f freq f cents i style s name s
metro(osc:1) /oscilla/metro/<uid> beat i bpm f — one message per beat
synth(osc:1) /oscilla/synth/<uid> state s (start step update stop) freq f amp f dur f (lifetime / step length / release) wave s
audio(osc:1) /oscilla/audio/trigger file s vol f loop i
audioPool(osc:1) /oscilla/audio/pool file s amp f pan f pitch f fadeIn f fadeOut f
audioImpulse(osc:1) /oscilla/audio/impulse same as pool
audio stop /oscilla/audio/stop file s (all) fadeOut f (seconds)

Receiving

Pure Dataoscparse turns /oscilla/rotate/wheel 12 0.2 0.03 into the list oscilla rotate wheel 12 0.2 0.03; after list trim a chain of route objects peels the address off one word at a time:

[netreceive -u -b 57120]
|
[oscparse]
|
[list trim]
|
[route oscilla]
|
[route rotate scale fade trans o2p control notation metro synth audio]
|                                  (right outlet: osc() addresses such as kick)
[route wheel]
|
[unpack f f f]      -> deg, rad, norm

When the first remaining atom is a string (audio file names, synth state), route makes it the selector; put a [list append] before [unpack s f i] to turn it back into a list. Both quirks are wired up in the example patch.

Maxudpreceive decodes OSC itself, so the address is the message selector:

[udpreceive 57120]
|
[route /oscilla/rotate/wheel /oscilla/scale/pulse]
|                                |
[unpack f f f]                   [unpack f f]

Use slider with @floatoutput 1 @size 1. for 0–1 values, dial @size 360. for degrees.

SuperCollider — one OSCdef per address; strings arrive as Symbols and msg[0] is the address:

OSCdef(\wheel, { |msg| ("deg" -> msg[1]).postln }, '/oscilla/rotate/wheel');
OSCdef(\note,  { |msg| Synth(\blip, [\freq, msg[3]]) }, '/oscilla/notation/tune/note');
// wildcards: every oscCtrl lane
OSCFunc({ |msg| msg.postln }, '/oscilla/control/*');

If Oscilla was started with another --osc-out port: thisProcess.openUDPPort(57122).


Surge XT

An address beginning surge/ is translated onto the second OSC destination and sent as a Surge parameter:

oscaddr:"surge/macro/1"        written on the cue
        ↓
/param/macro/1  0.73           sent to --osc-out2

Start the server with --osc-out2 53280 (Surge's default OSC input port, which has to be enabled in Surge under menu → Workflow → OSC Settings). One float between 0 and 1, which is what Surge's /param takes, so a control lane keeps its default min:0 max:1 and the height you draw is the parameter value. This works from any cue that carries an oscaddr: — control lanes, faders driven by o2p, osc() events.

Parameter paths look like a/filter/1/cutoff — scene, section, index, name. Right-click any knob in Surge and choose Copy OSC Address to get the exact one. Macros are macro/1macro/8 and are the same in every patch, which makes them the natural place to put controls you want a player to aim freely.

The prefix reaches Surge's whole OSC vocabulary, not only parameters: writing surge/mnote sends /mnote note velocity to play a note, surge/patch/load_user changes patch, and surge/mod/macro_1 wires a macro to a parameter. Surge's own reference, linked from its OSC settings, lists all of it.

The /param/… translation goes to every destination, alongside the ordinary /oscilla/… message, so the same drawn gesture can move Surge and a Pd or SuperCollider patch at once. See the demo-surge-xt project for a score wired up this way.


Port conflicts

sclang binds 57120 while it runs, so Pd or Max cannot open it at the same time. Either quit SuperCollider, start Oscilla with --osc-out 57122 and change the port in the patch, or keep 57120 for SuperCollider and add --osc-out2 57122 for the second application.


See also

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