Climbing Synth
The wall becomes an instrument. Holds in the Keyboard Area are notes of a scale; the notes being held fill the slots of a looping arpeggiator in the order they were pressed, and empty slots stay silent. Holds in the Effects Area shape the sound in real time — reverb, delay, filtering and so on — so some children compose while others play the mixing desk.
If the notes stay the same for three seconds the pattern freezes and keeps playing after everyone lets go. The Reset Hold clears it.
This is the one mode where SuperCollider owns the clock. The arpeggiator runs at audio precision inside SuperCollider; it reports every step back with /mode/synth/step so the lights pulse in time. The engine owns everything else: which keys are held, the freeze timer, the reset hold, and how each effect hold behaves.
Mode id: synth · Status: draft · Since: 0.2.0 · Spec: Prototype Use Cases, pp. 6–7
Loading
Section titled “Loading”Load this mode with /mode/load and type tags sssffis:
NetAddr("127.0.0.1", 57120).sendMsg('/mode/load', "synth", "pentatonic", "", 60.0, 110.0, 4, "marimba");Init parameters
Section titled “Init parameters”Sent in this exact order, immediately after the mode id.
| # | Name | Type | Range / default | Description |
|---|---|---|---|---|
| 1 | scale | s | pentatonic, major, minor, dorian, customdefault pentatonic | Pitch set. Every combination of notes in any of these sounds consonant, which is the point — no child can play a wrong note. |
| 2 | customScale | s | — | Semitone offsets from the root, space-separated, used only when scale is custom — e.g. “0 2 4 7 9”. Empty otherwise. |
| 3 | rootNote | f | 36.0 – 72.0 midinote default 60.0 | The note key 0 plays. Higher keys climb the scale, wrapping into higher octaves. |
| 4 | tempo | f | 60.0 – 180.0 bpm default 110.0 | Arpeggiator tempo. One step per sixteenth note. |
| 5 | steps | i | 2 – 8 default 4 | Arpeggiator slots — the most notes that can sound in the pattern. |
| 6 | instrument | s | synth, piano, marimba, stringsdefault marimba | The instrument voice. |
Runtime parameters
Section titled “Runtime parameters”Changed while the mode runs, with /mode/param name value. Always a float, always clamped, never an error if the name is unknown.
| Name | Range / default | Description |
|---|---|---|
tempo | 60.0 – 180.0 bpm default 110.0 | Change tempo without reloading. |
effectDepth | 0.0 – 1.0 default 0.7 | Overall effect intensity. Scales every effect amount, so a teacher can make the Effects Area gentler without touching its holds. |
NetAddr("127.0.0.1", 57120).sendMsg('/mode/param', "tempo", 110.0);Configured in the engine
Section titled “Configured in the engine”Settings from the use case that never cross the wire. The engine applies them; SuperCollider does not need them, so they are not in the protocol.
| Setting | |
|---|---|
| Keyboard and Effects Area layout | Which holds are keys and which are effects. The engine sends key and effect indices, never hold positions, so the layout can change freely. |
| Freeze timeout | Three seconds by default. The engine runs the timer and sends /mode/synth/freeze; SuperCollider does not need to know the duration. |
| Effect behaviour | Whether an effect hold is temporary (while held), cumulative (each touch adds) or persistent (toggles). The engine applies the behaviour and sends the resulting level — see /mode/synth/effect. |
| Visual animations and feedback | Including the per-step pulse, driven by /mode/synth/step. |
Messages
Section titled “Messages”/mode/synth/notes
The notes currently in the arpeggiator, in slot order.
i* -
keysi int32 0 – 47repeats 0 – steps times - Key indices in the order they were pressed, 0 = rootNote. Zero to `steps` values; slots beyond the list are silent rests. An empty message means no notes.
/mode/synth/notes 0 2 4 NetAddr("127.0.0.1", 57120).sendMsg('/mode/synth/notes', 0, 2, 4); osc.Send(Osc.ModeSynthNotes, 0, 2, 4); The WHOLE pattern, every time it changes — never "add note" or "remove note". A dropped note-on would otherwise leave a key stuck in the pattern until reset; a dropped full list is corrected by the next one.
Key indices, not pitches: SuperCollider maps them through the scale. Changing the scale therefore never requires the engine to recompute anything.
/mode/synth/freeze
Whether the pattern is frozen.
i -
frozeni int32 0 | 1 - 1 — keep looping the current pattern even when every hold is released. 0 — follow /mode/synth/notes live.
/mode/synth/freeze 1 NetAddr("127.0.0.1", 57120).sendMsg('/mode/synth/freeze', 1); osc.Send(Osc.ModeSynthFreeze, 1); While frozen the engine keeps sending /mode/synth/notes unchanged, so a restarted SuperCollider picks the frozen pattern straight back up.
/mode/synth/reset
The Reset Hold was pressed. Clear the pattern.
/mode/synth/reset NetAddr("127.0.0.1", 57120).sendMsg('/mode/synth/reset'); osc.Send(Osc.ModeSynthReset); Plays the reset gesture and returns every effect to zero. The engine follows it with an empty /mode/synth/notes and /mode/synth/freeze 0, so the state is correct even if this message is lost.
/mode/synth/effect
The current level of one effect.
sf -
effects string "reverb" | "delay" | "filter" | "modulation" | "pitch" | "stutter" - Which effect. filter is a low-pass sweep; pitch transposes the pattern by up to an octave; stutter is the rhythmic variation.
-
amountf float32 0.0 – 1.0normalizeddefault 0.0 - Absolute level, 0.0 off. Scaled by the effectDepth runtime param.
/mode/synth/effect delay 0.6 NetAddr("127.0.0.1", 57120).sendMsg('/mode/synth/effect', "delay", 0.6); osc.Send(Osc.ModeSynthEffect, "delay", 0.6f); An absolute level, never a touch. The engine turns effect-hold touches into levels according to each hold's behaviour, and sends the result. A dropped message is corrected by the next change or by /sys/sync.
Effects only sound while the pattern has at least one note, per the use case; SuperCollider keeps the levels while silent.
/mode/synth/step
The arpeggiator just played a step.
ii -
stepIndexi int32 0 – 7 - Which slot, 0..steps-1.
-
keyi int32 -1 – 47 - The key index that sounded, or -1 for a silent slot.
/mode/synth/step 2 4 ~osc[\modeSynthStep].value(2, 4); // handler for Osc.ModeSynthStep Sent on every step, even silent ones, so the engine can pulse the lit key holds in time with the sound. At 180 bpm this is 12 messages a second. The engine should not use it to drive anything audible.