Types & units
The float policy
Section titled “The float policy”Every continuous or normalized value is a float (f). Integers are only for
genuine identifiers, counts and flags.
This has its own page because it is the single most common integration bug in engine↔SuperCollider work, and because of how it fails.
Game engines are cheerfully permissive about numeric literals. Send 1 where
the contract says float, and most OSC libraries will happily encode an i. On
the SuperCollider side, msg[1] is then an Integer rather than a Float.
Nothing throws. Nothing logs. Arithmetic mostly still works. But
1 / 2 is 0 in integer arithmetic where 1.0 / 2 is 0.5, and somewhere
three layers down a filter cutoff lands at 0 Hz and the wall goes silent for
reasons that have nothing visible to do with the message that caused it.
Three things enforce the rule so you do not have to remember it:
- The type tag string is on every message on this site, rendered with each glyph directly above the argument it types.
- The validator fails the build if a declared argument type and the type tag string disagree, or if an example carries a value of the wrong type.
- The generated code coerces at the boundary. The SuperCollider senders call
.asFloat; the C# examples carry thefsuffix. Neither side can send the wrong thing by accident through the generated path.
The monitor catches the remaining case — an engine that constructs a packet by hand — and names the offending argument.
Type tags
Section titled “Type tags”| Tag | Meaning | Used for |
|---|---|---|
i | int32 | Identifiers, counts, flags, enum values |
f | float32 | Everything continuous: gains, positions, times, normalized values |
s | string | Mode ids, parameter names, version strings, free text |
b | blob | Nothing yet |
A trailing * on a type tag string means the last argument repeats to the end
of the message. /mode/synth/notes declares i*: zero to steps key indices.
Such an array is either a fixed length, or count: variable with a maxCount
the monitor enforces — an empty /mode/synth/notes is legal and means “no
notes”.
h, d, T, F and N are legal in the schema but unused. Prefer i and
f — some engine-side OSC libraries handle the exotic tags badly or not at all.
Indices into things only known at runtime
Section titled “Indices into things only known at runtime”Some integers index something whose size is only known when the installation
is running. They are declared with ref instead of a range:
ref | Legal values | Sized by |
|---|---|---|
hold | 0 … holdCount − 1 | the last /wall/config |
zone | 0 … zoneCount − 1 | the running mode’s zoneCount init param |
speaker | 0 … speakers − 1 | SuperCollider’s output layout |
A hard-coded range: [0, 239] on a hold index would be correct on exactly one
wall. The validator rejects a hold index without ref: hold, and rejects a
ref with a range; the monitor checks each one against the configuration it
has actually seen on the wire.
Ranges
Section titled “Ranges”Every argument that has a meaningful range declares one, and the site shows it.
Values outside the declared range are clamped, not rejected. A receiver that drops an out-of-range message turns a small sender bug into silence, which is much harder to debug than a slightly wrong sound. Clamp, use the value, and log it once.
Normalized 0.0 – 1.0 is the default idiom for anything continuous. It moves
the question of what the value means physically into SuperCollider, where the
curve can be tuned without touching the protocol or the engine.
Units are declared per argument and rendered on every message:
normalized—0.0 – 1.0. Preferred for gains, levels, progress, intensities.seconds— float seconds, never milliseconds and never samples.midinote— a float, so microtonal tunings and glides stay possible without a protocol change.bpm— beats per minute, a float.meters— physical wall dimensions.
Send gain as a normalized fader position rather than decibels. Decibels invite an argument about which reference and which curve; a fader position does not, and SuperCollider applies the curve.
timestamp arguments are engine time since session start, as monotonic
float seconds. They are for ordering events and for measuring latency.
They are not for scheduling. The two clocks are not synchronised and there
is no attempt to synchronise them. Anything that needs to happen at a specific
moment in the future carries a duration instead — see holdTime on
/mode/arcade/objective,
which says “5.0 seconds” rather than naming an absolute instant that
SuperCollider would have to translate.
The one mode with a musical clock, Climbing Synth, settles it by giving the
clock to SuperCollider outright: the arpeggiator runs at audio precision there,
and SuperCollider reports each step to the engine with /mode/synth/step so
the lights follow the sound, never the other way round.