Live Console
The live console lets you type and execute DSL cue expressions in real time during a performance. Any cue the score system supports can be triggered from the console -- synthesis, audio playback, animation, navigation, OSC messaging, and more.
The console can run in two modes: embedded inside the score view, or as a standalone window using the dedicated view system. In standalone mode, commands are sent over WebSocket to the score window and executed there.
Try It: the Live Coding Demo
The Live Coding demo (demo-livecode, under Sound & space) opens straight into perform mode on a dark, empty stage with no bars (double-click near the top or bottom edge for them), with a buffer of short snippets: sounds that change each time you run them, shapes, a turning triangle panning a saw, a dot on a line driving an organ's volume (and turned into a fader), scope views and palettes, a turning scope, audio drawn live. Ctrl+Enter on any line.
Live coding is meant for dark mode (the demo turns it on). Dark mode inverts the page, so a light score turns dark; what the console makes -- the code, shapes, sound boxes -- keeps the colours you give it, so fill:#111 is black and neon stays neon (very saturated colours come out a touch muted).
A project can do the same for itself:
"liveConsole": "perform"(or"open") in itspreferences.jsonopens the console when it loads;text/livecode.txtin the project is the starting buffer, used whenever your own buffer for that project is empty -- clear it and reload to get the starting text back.
The Projector: Visuals There, Code Here
With the projector as a second display, run
projector()
in the console. A second window of the same project opens (allow pop-ups for the page if the browser asks): drag it to the projector and press F11. It shows only the visuals -- no bars, no console -- and runs every line you run, as you run it. It is silent: its sounds play inside it, so its scopes draw them, but only your laptop window is heard. Random values (rand(), irand(), birand(), rand([...]), choose(a, b, ...)) are rolled once on the laptop and the same numbers are used in both windows, so the triangle is where it is on both screens and the scope shows the pitch you hear. (Inside a pattern -- Pseq, Prand ... -- randomness is drawn per step in each window.)
Ctrl+Shift+P on the laptop puts the code on the projector too -- the same text, caret, scroll and run flashes, sized to the projector -- and takes it off again.
If the projector window is reloaded or opened late, it asks the laptop for what has been run so far and catches up. The window is the project's URL with &follow=1, which you can also open by hand.
Other set-ups: one window in perform mode, mirrored to the projector, shows code over visuals on both; a ?view=live window gives a console with no visuals of its own, driving a score window elsewhere.
Opening the Console
In the score view: click the >_ button in the top bar, or open it programmatically.
As a standalone window: add ?view=live to the URL:
http://localhost:3000/?project=my-score&view=live
This opens a lightweight window with only the live console and WebSocket connection -- no score rendering, no audio context, no animation loop.
Perform Mode
For a concert, Ctrl+Shift+F (or the ⛶ button in the header) turns the console into the top layer of the score: the code fills the screen in large type, each line on a dark band so it reads over any score, and the line you run flashes green, or red if it was refused. The last few lines of output run along the bottom. The line you are typing stays on the middle of the screen, moving down or up: the code scrolls under it, so the typing stays in the top half and the bottom is left to the visuals. The first lines too: there is blank room above the code to scroll into. Tweaking one sound with the visuals up, Ctrl+Shift+B leaves only the block you are in on the screen (the #$ block, or the lines round the cursor up to a blank line); move into another block and it shows that one instead. Ctrl+Shift+B again leaves only the line at the cursor (the whole expression, if it runs over several lines), and once more brings back all the code.
The code takes the mouse -- the column from its left edge to the end of its longest line, first line to last, blank lines included: click to put the cursor there, drag to select, double click for a word, scroll with the wheel or a two-finger swipe (a swipe started on the code keeps scrolling it to the end), pinch to size the text. Alt+drag moves the whole block; Alt+double click puts it back. Right of the code and below it the mouse goes through to the score, so you can still drag a scope, press a button or move a fader. The keyboard stays in the code: after a click on the score, typing carries on where the cursor was.
A− / A+ change the size of the code. Perform mode keeps its own size, so the panel stays small when you switch back.
Ctrl+. stops every sound, like the stop button, in either mode -- after a small warning: Enter to stop, Esc to leave it playing (pressing Ctrl+. twice stops at once). Ctrl+, stops everything that moves on the screen instead, and leaves the sound playing.
A reload comes back as you left it: the console open or not, perform mode on or off, the panel where you put it, and the cursor and scroll in the code. This is remembered in this browser only.
Panel Sections
The console has three vertically stacked sections. Drag the bars between them to resize.
Editor -- a text area for writing DSL expressions. Type a cue expression and press Ctrl+Enter to execute the current line, or Ctrl+Shift+Enter to execute all lines.
Output -- shows execution results, errors, and (in standalone mode) a browsable list of all cue expressions found in the score.
Signals -- a live monitor of all ParamBus signals. Shows the current value of every active signal path. Use the filter field to narrow the display.
The panel is draggable (by its header bar) and resizable (all edges and corners).
Keyboard Shortcuts
| Key | Action |
|---|---|
Ctrl+Enter |
Execute the current line |
Ctrl+Alt+Enter |
Execute the line on every voice of a poly: sound (Ctrl+Enter adds a voice) |
Ctrl+_ |
Freeze the line: its synths on their last note, its animations where they are (hold()); again to let them go on |
Ctrl+Shift+Enter |
Execute all lines |
Ctrl+Shift+E |
Enter cue browser (standalone mode). Not Ctrl+J: that is the browser's downloads |
Ctrl+Shift+L |
Show / hide the console (perform mode is kept while hidden) |
Ctrl+Shift+P |
Show / hide the code on the projector window (projector()) |
Tab |
Take the suggestion shown faintly after the cursor (two spaces when there is none) |
Ctrl+Shift+H |
The cheatsheet: the console's keys and a reminder of the language, faint beside the console (over the score's right edge in perform mode). It takes no clicks and you go on typing; Ctrl+Shift+H or Esc puts it away |
Tab on a #$ line |
Fold its block away, or bring it back (Folding) |
Enter |
A new line. Inside an unclosed bracket -- synth(uid:a, -- the new line carries the expression on, indented two spaces past where it began |
Ctrl+Space |
List the suggestions to pick from: arrows to choose, Tab / Enter to take, Esc to close. (It never opens by itself: what can go where shows as a faint reminder in the line) |
Ctrl+= / Ctrl+- |
Bigger / smaller code (the panel and perform mode keep a size each); the browser's zoom while the console is closed |
Ctrl+Shift+D |
Help: the page of the cue under the cursor (or the call it is inside), in a new tab |
Ctrl+. |
Hush: asks first -- Enter stops every sound, Esc leaves it; Ctrl+. twice stops at once |
Ctrl+Shift+, |
still(all): asks first, Enter stops every animation and text and puts the screen back to plain -- screen(off), every gui()'s effects off, trails wiped; the sound plays on |
Ctrl+, |
Still: asks first, Enter stops every animation and text (still()) and what the texts were saying; the sound plays on. Esc leaves it, Ctrl+, twice stops at once |
Ctrl+Shift+. |
Stop what the line under the cursor started, and leave its text alone (Ctrl+Enter brings it back) |
Ctrl+C / Ctrl+X (nothing selected) |
Copy / cut the whole line |
Ctrl+Shift+K |
Delete the line, or every line the selection touches (the clipboard is left alone) |
Ctrl+V (after a line copy) |
Paste it as a line above the cursor's: copy + paste duplicates a line |
Ctrl+Shift+F |
Perform mode on/off |
Ctrl+Shift+B |
Focus, in turn: only the block at the cursor shows -- the #$ block it is in, or the lines round it up to a blank line --, then only the line (the expression) at the cursor, then all the code; the rest out of sight, in its place; the projector shows the same |
Ctrl+Shift+S |
Copy the line, or every line the selection touches, to the project's stash (see The Stash) |
Ctrl+Alt+Shift+S |
The same, to the global stash beside your projects |
Escape |
Exit cue browser |
Target Selection
Some cue types operate on a specific SVG element -- rotate, scale, o2p, color, fade. Before executing these, you need to select a target.
In the score view: click pick and click on an element in the score. Or type a uid or element ID into the target input and press Enter.
In standalone mode: the pick button is unavailable (no local score). Instead, type into the target input -- it autocompletes from all element IDs in the score. When you insert a cue expression from the browser, the element is targeted automatically since the expression IS the element's ID.
Cue types that do not need an element (synth, audio, speed, nav, osc, stop, etc.) execute without a target.
Cue Browser
In standalone mode, the console fetches the project SVG on open and lists all DSL cue expressions in the output panel. You can interact with this list to select and insert cues into the editor.
Keyboard workflow
- Press
Ctrl+Shift+Eto enter browse mode - Navigate with arrow keys (or
j/k) - Press
Enterto insert the selected expression at the cursor - Focus returns to the editor automatically
- Press
Escapeto exit without inserting
You can also click any entry to insert it.
Multi-line Expressions
The editor supports multi-line DSL expressions. Lines with unbalanced parentheses are joined to the next line, so long expressions can be split for readability:
synth(
wave:sin,
freq:440,
dur:2,
amp:0.6
)
Ctrl+Enter detects which expression the cursor is inside and executes the whole thing.
A blank line always ends an expression, so keep a multi-line block together. // comments can sit on their own line or at the end of one.
Folding Blocks Away
A line starting with #$ heads a block: everything below it, up to the next
#$ line. Tab on that line folds the block away; the line stays, with
what is written on it, and shows how much is hidden. Tab again brings it
back.
#$ -- drums -- ▸ 3 lines
#$ -- pads, for the second half --
synth(uid:pad,
wave:saw, freq:110)
A folded block is still there:
Ctrl+Enteron a#$line runs its block, folded or not;Ctrl+Shift+.there stops what it started.Ctrl+Shift+Enter(run all) runs folded blocks too.- The buffer is saved whole, and which blocks were folded is remembered. Other players see your buffer whole.
- Deleting a folded
#$line deletes its block too, as in any editor;Ctrl+Zbrings both back, still folded. Typing the#$away (the line stays) brings the block back below it.
The #$ line is a comment (#), so it never runs as code itself.
Blocks inside blocks
More $ is a bigger block: a block runs to the next head with as many $
or more, so a #$$ line holds every #$ block under it. Ten synths in
their own #$ blocks are wrapped by one line above them:
#$$ synths ▸ 14 lines (kick ... pad in it)
#$$ visuals
#$ shapes
opened:
#$$ synths
#$ kick ▸ 2 lines (still folded)
#$ snare
synth(uid:sn, …)
…
#$ pad
#$$ visuals
- Folding a bigger block takes the folds inside it with it; opening it again
leaves them folded as they were.
#$$$wraps#$$blocks in turn. Ctrl+Enteron a bigger head runs everything in it, the inner blocks too.- Saved and loaded with the nesting kept;
Ctrl+Zon a fold brings the block back with the folds inside it still folded.
Running a Line Again
A sound you run from the console belongs to the line that made it. The first time you run a sound cue without a uid, the console writes one into the line:
synth(freq:rand(100, 400))
becomes
synth(uid:s1, freq:rand(100, 400))
From then on, running the line again updates s1, here with a new random frequency, instead of starting another voice you could never reach. The name moves with the line through any edit, Ctrl+Z takes it out again, and you can rename it to something meaningful. Synths are named s1, s2…, audio a1…, audioPool p1… and audioImpulse i1…. A uid you write yourself is left alone. Use poly: when you want new voices to stack.
Patterns Come In on the Beat
A synth with a pattern, run from the console, starts on the next beat
(quant:1), so lines run at any moment come in together and stay in step;
quant:4 waits for the bar, quant:0 starts at once. A plain synth with
no pattern starts at once. See
synth: On the beat.
Building a Score Live
shape() draws a shape into the score where you are looking: sides:0 is a circle, 1 a line, 2 a lens, 3 a triangle and so on. Animation cues after it on the same line act on it, and its signals can drive sound:
shape(sides:3, size:0.15) rotate(dur:4, loop:0)
synth(uid:s2, freq:rand(100, 400), pan:follow(sh1.sin, -1, 1))
The console names the shape sh1 on the first run, as it names sounds. Run the line again to change the shape in place; comment it out and run it to take it away.
Changing how it looks
gui() reaches whatever a uid draws, a sound's box or a shape, by name:
gui(s3, size:1) // the scope fills the screen
gui(s3, size:[0.5, 0.3], at:[0.75, 0.2]) // half wide, near the top right
gui(s3, stroke:#ff2a6d, width:3, fill:#000) // trace colour and width, background
gui(sh1, at:[0.7, 0.4], fill:#f60)
hush(s3) silences a sound the same way, by name, and an animation can take a sound's box as its target: s3 > rotate(dur:6, loop:0) turns the box with its scope in it.
In perform mode the keyboard belongs to the code: after a click on a score button, the next key you type goes into the code, not to the transport.
A fader with a life of its own
A made line is a path, so a dot can travel it with o2p(), and anything can follow the dot's t (0..1 along the line):
shape(sides:1, size:0.5, at:[0.5, 0.8])
shape(sides:0, size:0.04, fill:#f60) o2p(path:sh1, mode:alt, dur:1, loop:0)
synth(uid:s1, freq:220, amp:follow(sh2.t, 0, 0.1))
Running an o2p() line again from the console carries on from where the dot is, with the new settings, instead of starting over:
- change
dur:ormode:and it changes on the move - add
trig:touchand it stops where it is and becomes a fader: drag it, or move it from the code withui(uid:sh2, value:0, dur:8)for a slow fade - take
trig:touchout again and it sets off from where you left it loop:1lets it run out: one more pass from where it is, then it stops
Suggestions
As you type, the best completion shows faintly after the cursor -- syn → synth(, wa inside a synth → wave:, sa after wave: → sawtooth, gui(or → organ, , follow(self.s → sin -- and Tab takes it. Ctrl+Space lists the others to pick from.
What can go where shows as a faint reminder at the end of the line -- to read, not to pick from:
organ > rotate( dur · dir · ease · loop · pivot · trig
organ > rotate(dur:2, dir · ease · loop · pivot · trig
synth(uid:b, wave: sine · square · sawtooth · triangle · noise …
synth(uid:b, env env · envPerStep
synth(freq:follow( organ · me · sq · /pedal/1
synth(freq:follow(sq. angle · cos · norm · rad · sin · tvel · vel · x · y
In follow( it shows what there is to follow: the names you have made, what
is publishing signals right now, and the OSC addresses heard so far (type /
and they complete). After a name and a dot, that name's own channels, as it
publishes them now -- self. is the line's own shape; something not running yet
shows the usual channels.
After a cue's bracket, and after each comma, it shows the parameters most lines use and that are not written yet; after a parameter with values of its own (wave:, view:, ease:, trig: …) its values; on a whole name that begins others too (env / envPerStep, audio / audioPool) all of them. Type a letter and it goes: the faint completion carries on a word at a time. An expression laid out over several lines is one expression: on its third line, the suggestions are still the synth's. With the cursor at a bracket, the bracket it pairs with is marked. It knows the cues, each cue's parameters (leaving out the ones already written), the choices of a parameter that has them (ease: on any animation: the easing names; inside choose(, Prand([ and the other patterns too: wave:choose(sq → square), the names you have made (written in the buffer or alive: sounds, shapes, texts) and the signals after a name and a dot.
Object parameters come whole: env: offers {a:0.02, d:0, s:1, r:0.1} (the defaults, to change), and filter:, delay:, reverb: likewise; inside the braces, the keys you have not written yet.
You need not know how a value begins. Type the colon and the choices are listed (view: → wave, polar, phase, xy, spectrum, ridge, waterfall; wave: → sine, square, sawtooth … kick, snare, hat; scale: → major, minor, dorian …), or type any part of one: view:fall lists waterfall, and Tab puts the whole word in place of what you typed. Those that begin with what you typed come first.
The Stash
Lines you mean to use in a set but not to show before you do. Write a line in the console (or select several), press Ctrl+Shift+S, and it is copied to the project's stash -- the console keeps its copy, so delete it if the screen should start blank. From then on the suggestions offer it back:
- Type the start of a stashed line and the rest shows faintly after the cursor, as any suggestion does;
Tabtakes all of it. Only its start is shown (syn→th(uid:drone, wave:saw) …), and a phrase of several lines comes back whole. - Remember a word of it but not how it begins? Type the word and
Ctrl+Space: stashed lines that contain it are listed last, and taking one replaces what you typed. They never show by themselves.
The stash is a plain text file in the project folder, to edit by hand as well: a phrase is a run of lines, a blank line ends it, and lines starting with # are notes. Lines selected together with blank lines between them are stashed as separate phrases, and a phrase already there is not written twice.
The global stash. Beside your projects, in the projects folder, one more vocab.live holds the lines common to every set: Ctrl+Alt+Shift+S copies to it. The suggestions offer the project's stashed lines first, then the global ones. Only the computer running Oscilla has one; no other laptop is sent it.
Whose stash. The computer running Oscilla keeps vocab.live. In a shared session every other laptop keeps its own, vocab-<name>.live beside it, named by what that laptop calls itself in the name dialog -- so set your name before you stash, and keep it (a new name starts a new, empty stash; the old file stays). The console's output says which file a line went to.
Who can see it. No stash is ever shown on the score or the projector until you type it, the server never hands a .live file out as a download, and stashes are left out of a project export. The host's vocab.live cannot be reached from any other laptop. A guest's stash is kept by name only: someone on the same network who knows your name, and knows how, could ask the server for it.
When Something Is Wrong
Every expression is checked before it runs, and nothing on the line plays if any part of it fails. The output says why, with a caret under the word it could not read:
expected ) before "freq"
synth(uid:a freq:440)
^
Problems found after a cue starts, such as an audio file that is not there, also appear in the output for a couple of seconds after you run something.
Signal Monitor
The bottom section shows all active ParamBus signals in real time. Each row displays a signal path and its current value, updated at 5 fps.
Use the filter field to narrow the display. For example, typing freq shows only signal paths containing "freq".
This is useful for verifying that control bindings are working -- you can see whether a fader's t value is changing, what frequency a synth is receiving, etc.
Standalone Mode Details
The standalone ?view=live window is a lightweight client. It connects to the same WebSocket server as the score window but does not load the score, initialise audio, or run the animation loop.
What happens when you execute a command:
- The console sends a
livecode_execmessage via WebSocket - The server relays it to all other connected clients
- The score window receives it, resolves the target element, and calls
handleCueTrigger
This means multiple performers can have their own live console windows open, all sending commands to the same score.
Live Rects: Audio Waveforms and Synth Scopes
When you trigger audio or synth cues from the console, there is no score element to anchor the visual display. The system creates ephemeral SVG rects in the viewport automatically. Audio cues get waveform contours with playback cursors. Synth cues get real-time oscilloscope traces. Both use the same rect system.
A synth's rect shows only its scope; add overlay:1 or overlay:2 to the synth to get the line of parameters across its top. Each unique uid gets its own rect. Rects stack vertically from the center of the viewport. They can be dragged by their body and resized by their edges. They persist until the voice stops or the global stop button clears them.
The waveform parameter controls where the display goes:
| Value | Behaviour |
|---|---|
self (default) |
Voice gets its own rect |
none |
No visual display |
<uid> |
Display renders into another voice's rect |
By varying uid, poly, and waveform, you can build anything from a single combined display to a multi-rect spatial layout.
Audio Examples
Single file with its own waveform
audio(src:drone.wav, loop:0, fade:2, uid:pad1)
A blue dashed rect appears with the waveform rendered inside and a cursor tracking playback.
Multiple independent layers
Each uid gets a separate rect. Use this to visually separate different sound roles:
audio(src:"noise/rain.wav", amp:0.6, loop:0, uid:rain)
audio(src:"pads/drone.wav", amp:0.3, speed:0.5, uid:drone)
audio(src:"perc/click.wav", amp:0.8, loop:0, uid:clicks)
Three separate rects, each with its own waveform and cursor. Drag them apart to organise the display.
Layering voices on a shared waveform
Use the same uid with poly:0 to stack multiple voices onto one rect. Each voice gets its own coloured peak layer and independent cursor:
audio(src:"pads/drone.wav", loop:0, speed:1, uid:mix, poly:0)
audio(src:"pads/drone.wav", loop:0, speed:0.5, uid:mix, poly:0)
audio(src:"pads/drone.wav", loop:0, speed:0.25, uid:mix, poly:0)
One rect, three cursors sweeping at different speeds, three peak contours in different colours from the 12-colour palette. Useful for building chordal textures from a single source.
Hot-updating a running voice
In mono mode (default poly:1), retriggering the same uid updates the running voice without restarting it. Execute these one at a time:
audio(src:drone.wav, loop:0, uid:myDrone)
audio(src:drone.wav, speed:0.5, uid:myDrone)
audio(src:drone.wav, amp:0.3, pan:-0.5, uid:myDrone)
audio(src:drone.wav, in:2, out:8, uid:myDrone)
audio(src:other.wav, uid:myDrone)
audio(src:drone.wav, loop:1, uid:myDrone)
Speed and gain change immediately via smooth ramps. In/out points and src changes take effect on the next loop iteration. Setting loop:1 finishes after the current iteration.
Reverse playback
Negative speed plays the file backwards. The waveform mirrors horizontally and the cursor sweeps right-to-left:
audio(src:texture.wav, speed:-1, loop:0, fade:2, uid:rev1)
Polyphony with voice cap
Cap overlapping voices. When the limit is reached, the oldest voice stops:
audio(src:grain.wav, loop:0, speed:rand(0.3, 2), uid:grains, poly:4)
Execute this repeatedly. Each trigger creates a new voice at a random speed. After four voices, the fifth stops the oldest.
AudioPool Examples
One-shot from a folder
Each execution selects and plays one file from the pool:
audioPool(path:"sfx/birds", mode:shuffle, pan:rand(-1, 1), uid:birds)
The waveform redraws on each trigger to show whichever file was selected. Execute repeatedly to walk through the pool.
Polyphonic pool
Allow multiple pool hits to overlap:
audioPool(path:"sfx/wood", mode:rand, amp:rand(0.3, 1), pan:rand(-0.8, 0.8), poly:5, uid:wood)
Execute rapidly to build up a dense layered texture with random pan positions.
AudioImpulse Examples
Start a texture process
Impulse runs autonomously once triggered -- no need to re-execute:
audioImpulse(
path:"sfx/rain",
rate:30,
jitter:0.5,
amp:rand(0.2, 0.6),
pan:rand(-1, 1),
speed:rand(0.8, 1.3),
fadeout:"40%",
poly:6,
lifetime:process,
uid:rain1
)
A rect appears with a base waveform. Coloured peak layers and cursors appear and disappear as hits fire and complete.
Update a running impulse
Retrigger with the same uid to change parameters on the fly:
audioImpulse(path:"sfx/rain", rate:60, jitter:0.8, poly:8, uid:rain1)
Rate and jitter update immediately. Subsequent hits use the new values.
Granular cloud
Push the rate high and the speed range wide for granular-style textures:
audioImpulse(
path:"grains/voice",
rate:200,
jitter:0.9,
amp:rand(0.05, 0.2),
pan:rand(-1, 1),
speed:rand(0.1, 4),
fadein:0.01,
fadeout:rand(0.01, 0.1),
poly:12,
lifetime:process,
uid:voiceGrain
)
Synth Examples
Simple tone with scope
A live oscilloscope trace appears in an ephemeral rect:
synth(uid:tone, wave:sine, freq:440, amp:0.1)
Chord with envelope
Multiple oscillators rendered as a single scope:
synth(uid:pad, wave:sine, freq:[440, 550, 660], env:{a:2, r:3}, amp:0.12)
Hot-updating a synth
In mono mode (default), retriggering the same uid updates the running voice. Wave type, frequency, filter, amp, and pan update smoothly:
synth(uid:pad, wave:sine, freq:440, amp:0.1)
synth(uid:pad, wave:triangle, freq:330, amp:0.15)
synth(uid:pad, freq:[220, 330, 440], amp:0.12)
synth(uid:pad, filter:{type:lp, freq:800, q:4})
Random chords
rand() expressions evaluate fresh on each execution. Run the same line repeatedly to get different voicings:
synth(
uid:cluster,
wave:triangle,
freq:[rand(200, 800), rand(300, 1000), rand(400, 1200)],
env:{a:2, r:3},
amp:0.1
)
Separate scopes for separate roles
Give each synth its own uid to get independent rects. Drag them apart to arrange by function:
synth(uid:bass, wave:saw, freq:55, amp:0.1)
synth(uid:mid, wave:triangle, freq:440, amp:0.08)
synth(uid:high, wave:sine, freq:2200, amp:0.05)
Three rects appear, each showing the waveform shape of its oscillator.
Stacking scopes on a shared display
Use waveform:uid to direct one synth's scope into another synth's rect. Both scopes render in the same element with distinct colours:
synth(uid:pad1, wave:sine, freq:[440, 550, 660], amp:0.12)
synth(uid:pad2, wave:square, freq:880, waveform:pad1, amp:0.08)
pad1 gets a rect with its scope. pad2's scope stacks into pad1's rect in a different colour. Useful for visually grouping related voices.
Poly overlapping voices
With poly:N, each execution creates a new overlapping voice. All poly voices share a single scope rect:
synth(
uid:swell,
wave:sine,
freq:[rand(100, 800), rand(200, 1200)],
env:{a:4, r:6},
dur:8,
amp:0.1,
poly:12
)
Execute repeatedly. Each trigger creates a two-oscillator chord with random frequencies, fading in over 4 beats, auto-stopping at 8 beats with a 6-beat release tail. Up to 12 voices overlap. All scope traces stack in one rect with distinct colours. The 13th voice steals the oldest.
Poly cloud building
Combine rand(), dur, env, and poly to build evolving textures by repeated execution:
synth(
uid:cloud,
wave:triangle,
freq:[rand(33, 333), rand(33, 533), rand(33, 1333)],
env:{a:6, r:4},
dur:12,
amp:0.08,
poly:20
)
Each execution adds a new three-oscillator chord with random frequencies. Voices fade in over 6 beats, sustain, then auto-stop at 12 beats with a 4-beat release. One rect holds all scopes.
Signal-bound synth
Bind synth parameters to controlXY faders or o2p animations. Move the fader to modulate the synth in real time:
synth(uid:ctrlDrone, wave:saw, freq: follow(fader1.t, 80, 800), amp: follow(fader1.y, 0, 0.2))
Check the signal monitor panel to verify values are flowing.
Mixing Audio and Synth
Shared display for different source types
Audio waveforms and synth scopes use the same live rect system. Direct a synth scope into an audio rect using waveform:uid:
audio(src:drone.wav, loop:0, amp:0.3, uid:layer1)
synth(uid:synPad, wave:sine, freq:[220, 330], waveform:layer1, amp:0.1)
The audio waveform and synth scope share the same rect. The synth scope renders on top in a distinct colour.
Separate rects for separate roles
For clarity during performance, keep different sound roles on separate rects:
audio(src:"pads/warm.wav", loop:0, fade:3, uid:backing)
audioImpulse(path:"sfx/rain", rate:20, poly:4, uid:texture)
synth(uid:lead, wave:saw, freq:440, filter:{type:lp, freq:1200}, amp:0.08)
Three rects, each with its own visual identity. Drag them to different screen positions to create a spatial performance layout.
Layered drone setup
Build a complex layered drone from the console, mixing file playback with synthesis:
audio(src:"pads/drone.wav", loop:0, speed:0.5, fade:4, uid:droneFile)
audio(src:"pads/drone.wav", loop:0, speed:0.25, fade:4, uid:droneFile, poly:0)
synth(uid:droneSyn, wave:saw, freq:55, filter:{type:lp, freq:400, q:2}, amp:0.06)
synth(uid:droneOvertone, wave:sine, freq:[110, 165, 220], waveform:droneSyn, amp:0.04)
The two audio voices share one rect (same uid with poly:0, two peak layers, two cursors). The two synths share another rect (droneOvertone's scope targets droneSyn). Two rects total, four sound sources, each pair visually grouped.
External Audio Search Path
By default, audio cues resolve files from the project's audio/ directory, falling back to the shared shared/audio/ directory. When working from a blank template or using samples stored elsewhere on the system, you can point the server at an external directory.
Set a path
audiopath(/home/rob/samples)
audiopath("/mnt/audio library/field recordings")
The server validates that the path exists and is a directory. On success, the output shows what's available at the root (subdirectories and audio file count). All audio resolution -- single file, pool, and impulse -- now falls back to this directory when files are not found in the project.
Query the current path
audiopath()
audiopath(status)
Reports the currently active path, or "not set".
Clear the path
audiopath(clear)
Removes the external path. Audio resolution reverts to project + shared only.
How it works
The server mounts the external directory at /ext-audio/. The fetch chain for all audio cues becomes: project directory, then external path, then shared directory. Pool and impulse file listings also scan the external path when the project directory is empty.
The path is per-server-session (not persisted across restarts). All connected clients benefit from the mount -- if one performer sets the path from a standalone live console, the score window's audio resolution picks it up immediately.
Example workflow
// Point at a sample library
audiopath(/home/rob/freesound-pack)
// Now use folders from that library
audioPool(path:rain, mode:shuffle, uid:rain1)
audioImpulse(path:birds, rate:20, jitter:0.6, poly:6, uid:birds1)
audio(src:"drone/low-hum.wav", loop:0, uid:hum)
// Done with external samples -- revert
audiopath(clear)
Spatial Control
Spatial sources (Spatialisation) are ordinary o2p and controlXY cues, so the console drives them like any other.
Move the hand-placed sources: controlXY presets
ui(action:'controlXYRecall', preset:'front_left')
ui(action:'controlXYRecall', preset:'overhead', dur:3, ease:'easeInOutSine')
A preset stores where each handle of a controlXY pad is; recalling it
with dur: moves them there, and a spatial pad sends the positions in
space all the way. Sequences of presets:
ui(action:'controlXYDefineSequence', name:'orbit_snap', steps:'front,right,rear,left,front')
ui(action:'controlXYSequence', seq:'orbit_snap', dur:2, ease:'easeInOutSine', loop:true)
ui(action:'controlXYSequenceStop')
Presets apply to controlXY handles, not to o2p sources.
Stop and restart a source on a path
window.runningAnimations.get("src1").pause() // with drag:1 it can now be dragged
window.runningAnimations.get("src1").resume() // carries on from the nearest point of its path
Change a source while it runs
Run its o2p again with new values — it carries on from where it is:
o2p(path:traj_spiral, uid:src1, dur:8, spatial:dome, bounds:dome_bounds, osc:1)
Watch it
The signal monitor shows each source's src1.azi, src1.elev and
src1.dist (0..1), and a pad's mixer.src4.azi and so on.
Stopping
Stop one sound, or several
hush(uid:rain1)
hush(uid:[pad, rain1], rel:3)
hush(uid:X) silences whatever is called X: a synth (with all its poly voices), an audio file, an audioPool or an audioImpulse. rel: is the release in beats.
Stop one animation
off:1 stops an animation where it has got to: a turning box stays at its angle, a moving shape where it is. Run the line again to start it from there.
organ > rotate(off:1)
sh1 > trans(off:1)
It works for rotate, scale, trans, color and o2p. An o2p is taken down, not made a fader: for a fader, run it again with trig:touch. Other animations on the same element carry on.
Stop the line under the cursor
Ctrl+Shift+. takes back what the line under the cursor started, and leaves the line as it is:
| The line | What stops |
|---|---|
synth(uid:s1, …), audio(uid:a1, …) |
the sound: hush(uid:[s1]) |
shape(uid:sh1, …) rotate(…) |
the shape goes, with the sounds and turns it carried |
text(uid:sz, …) |
the text: text(uid:sz, stop:1) |
organ > rotate(…) |
the turn: organ > rotate(off:1) |
The line it runs instead shows in the output. Ctrl+Enter on the line starts it again.
Comment it out and run it
Put // in front of a block and run it with Ctrl+Enter: it is the same as Ctrl+Shift+. on it.
// synth(uid:s1, freq:rand(100, 400))
// organ > rotate(dur:1.6, loop:0)
Stop everything
Ctrl+., or:
hush()
This is the same as the stop button: every sound stops, and the scopes and waveforms go.
For the screen, Ctrl+,, or:
still()
Every animation stops where it has got to, every text() goes (and what stay:1 left), and the trig: lines that draw stop watching, so nothing new comes in on the beat. The sound plays on.
stop() is not for sound. It stops the transport; play(), rewind() and seek() work it too (see the transport cues).
Related
- Dev: Live Console -- technical reference
- Spatialisation -- sources moved in space: paths, by hand, SuperCollider and Pd receivers
- controlXY -- presets, sequences, and spatial positioning
- synth -- synth cue reference
- audio -- audio cue reference
- audioPool -- pool cue reference
- audioImpulse -- impulse cue reference
- audio_shared -- shared audio features
- Cue System -- overview of all cue types
- Control & Modulation -- signal routing
Tip: use ← → or ↑ ↓ to navigate the docs