Oscilla Live — live coding with Oscilla
oscilive for short: the live-coding side of Oscilla, the same program.
Oscilla can be played as a live coding instrument: you write lines of code in the console, run them, and sound and image appear on the score as you go. The image is drawn by the sound (scopes, phase portraits, spectra), and the sound can be steered by the image (a shape's place, its turning, its speed). This page takes you from an empty console to a set. The Live Console page is the reference for every key and panel.
Try it: press Random example on Oscilla Live's landing for one of the examples, playing.
Straight in: Oscilla Live (/live on Oscilla's
server) is the console on its own page, no project: alone, or in a room
with others.
The basics
Run a line: put the cursor on it and press Ctrl+Enter. A block of
lines with no blank line between them runs as one expression.
Run it again and the same thing changes: a sound or shape without a
uid: gets one the first time (s1, sh1), written into the line, so
the next run reaches it rather than stacking a second one.
Stop things:
Ctrl+. |
Stop every sound now |
Ctrl+, |
Stop every animation and text now (still()); the sound plays on |
Ctrl+Shift+. |
Stop what the line under the cursor started (its text stays; Ctrl+Enter brings it back) |
// in front of a line, then Ctrl+Enter |
The same: what it started stops |
hush(saw) · hush(organ, rel:4) · hush() |
One sound, one with a 4-beat release, all |
organ > rotate(off:1) |
One animation, left where it got to |
shape(remove:all) |
Every shape |
For performing: Ctrl+Shift+F lays the code over the score in large
type, Ctrl+Shift+L hides and shows it, Ctrl+= / Ctrl+- size it.
Night mode suits a dark stage.
Suggestions appear as you type: the rest of a word shows faintly,
Tab takes it, Ctrl+Space lists the others. After a parameter with set
values (wave:, view:, trig: …) the list opens by itself, and
env: offers the whole {a:0.02, d:0, s:1, r:0.1} to change. Lines you
stash with Ctrl+Shift+S come back the same way, from their first letters
(see A set, in practice).
Sound
A synth
synth(wave:triangle, freq:rand(80, 200), amp:0.05)
wave: sine, square, sawtooth (or saw), triangle, noise, pink, brown.
freq: in Hz, a note name (~A4), or a list for a chord:
synth(wave:sine, freq:[rand(200, 600), rand(300, 900)], dur:6, env:{a:2, r:3}, amp:0.03, poly:8)
dur: (no pattern) is how long the voice lasts; env: its envelope;
poly:8 lets up to eight voices overlap, so each run adds one.
Keep amp: low (the docs give 0 to 0.25): several voices add up.
Patterns
A pattern in a parameter makes the synth step, one value per dur::
synth(uid:s1, dur:0.25, freq:Pseq(220, 330, 440, inf), amp:0.05)
synth(uid:s2, dur:0.12, freq:Pexprand(70, 1060, inf), amp:0.05)
Pseq(a, b, c, inf) |
in order, for ever |
Prand(a, b, c, inf) |
at random (may repeat) |
Pxrand(a, b, c, inf) |
at random, never the same twice |
Pshuf(a, b, c, inf) |
shuffled, then that order again |
Pwhite(lo, hi, inf) |
anywhere between lo and hi |
Pexprand(lo, hi, inf) |
between lo and hi, evenly across octaves (for pitch) |
Pseq(rand(100, 300), 440, inf) |
rand() inside a pattern: rolled again each time |
Two rules that catch people:
- The last number is the repeat count.
Prand(200, 400, 600)chooses between 200 and 400, 600 times. End withinf(or a count):Prand(200, 400, 600, inf). (Pwhite(100, 1000)is the exception: the range, for ever.) - Square brackets are a chord.
Prand([220, 330], inf)plays the chord 220 + 330 every step;Prand(220, 330, inf)chooses between them.
Notes, not a glide
By default a stepping synth glides from value to value under one
envelope. envPerStep:1 gives every step its own attack and release:
synth(uid:s2, dur:0.12, freq:Pexprand(40, 666, inf), poly:3, envPerStep:1, env:{a:0.002, d:0.08, s:0, r:0.02})
s:0 lets each note die away within its step (plucked); s:0.4 holds it
(gated). An envelope longer than the step is squeezed to fit.
On the beat
Patterns run from the console start on the next beat, so lines run at any moment come in together and stay in step:
synth(uid:a, dur:0.25, freq:Pseq(220, 330, 440, inf)) next beat (the default)
synth(uid:b, dur:0.12, freq:Pwhite(100, 900, inf), quant:4) next bar
synth(uid:c, dur:0.5, freq:Pwhite(60, 90, inf), quant:0) at once
A beat is a second (60 bpm) until tempo(120) says otherwise; dur: is
in beats, so a pattern speeds up and slows down with the tempo, and a change
lands on a whole beat. A pattern with a
fixed dur: also comes in on its own step grid, so every run of
dur:0.12 steps together, whenever it was started. Steps are timed on the
audio clock, so patterns stay locked for as long as they run. See
synth: On the beat.
Scales
Numbers of a scale instead of Hz: degree: counts its notes from root:.
A random melody that always sounds in key, and a change of mode by running
the line again:
synth(uid:m, degree:Prand(0, 2, 4, 5, 7, inf), scale:minor, root:~A3, dur:0.25)
synth(uid:m, degree:Prand(0, 2, 4, 5, 7, inf), scale:dorian, root:~A3, dur:0.25)
synth(uid:p, degree:Pseq([0, 2, 4], [3, 5, 7], [4, 6, 8], inf), scale:minor, root:~A2, dur:2, wave:saw, amp:0.03)
See synth: Degrees of a scale.
Drums, FM, drive
synth(uid:k, wave:kick, dur:1)
synth(uid:h, wave:hat, dur:0.25, amp:Pseq(0.1, 0.03, 0.06, 0.03, inf))
synth(uid:b, degree:Pseq(0, 0, 3, 5, inf), scale:minor, root:~A1, dur:0.5, envPerStep:1, fm:{ratio:0.5, index:4, decay:0.3}, drive:0.4)
wave:kick, snare and hat are struck once a step; fm:{ratio, index} bends
a note with a second one (decay: lets the brightness fall away each note);
drive: rounds the wave off into grit. See
synth: Drums,
FM and Drive.
Sound files
audio(uid:moog, src:"noise/moog-mother32-A.wav", loop:0, amp:0.3)
audioPool(uid:drops, path:drops, amp:follow(dot.t, 0, 0.8))
audioImpulse(uid:rain, path:drops, rate:follow(tri.norm, 10, 120))
On Oscilla's server, files live in Live's own folder, _live, in its audio/; on a static host, give URLs. audio plays one file,
audioPool one from a folder each time, audioImpulse keeps firing them
at rate: per minute. Their level, pan, speed (and an impulse's rate) can
follow a signal; see Linking.
Image
Shapes
shape(uid:tri, sides:3, size:0.25, at:[0.3, 0.5], fill:#ff2a6d) rotate(dur:4, loop:0)
sides: 0 a circle, 1 a line, 2 a lens, 3 and up a polygon. size: a
fraction of the screen (or [w, h]), at: the centre, 0..1 across and
down. Anything after the shape on its line acts on it: rotate, scale,
trans, color, o2p (along a path). Later, reach it by name:
tri > rotate(dur:1, loop:0)
tri > rotate(off:1)
m:, n1:, n2:, n3: instead of sides: make a superformula form:
stars, flowers, blobs, each number a follow() if you like. morph: glides
a shape into its new outline:
shape(uid:tri, m:5, n1:0.3, n2:0.3, n3:0.3, morph:2)
shape(uid:tri, m:follow(bass.low, 2, 9), n1:1, n2:7, n3:8)
Many at once
shape(uid:cl, n:12, sides:irand(3, 8), size:rand(0.03, 0.12), at:[rand(0.1, 0.9), rand(0.2, 0.8)], fill:#f6f740) rotate(dur:rand(2, 9), loop:0)
n:12 makes a cluster: twelve shapes, each with its own roll of every
rand(). Run it again for a new one. A swarm on a path:
shape(uid:wire, sides:1, size:[0.8, 0.1], at:[0.5, 0.94], stroke:#444)
shape(uid:swarm, n:8, sides:0, size:0.025, fill:#05d9e8) o2p(path:wire, dur:rand(1, 4), mode:alt, loop:0)
Text
text(uid:sz, shuffle:"ŠĐČĆŽšđžćč", unit:char, dur:0.3, loop:0, style:"color:#f6f740;font-size:20em")
gui(sz, at:[0.75, 0.3])
A new letter every 0.3 beats in a new order each pass. Drag it with the
mouse, or place it with gui. text(uid:sz, stop:1) stops it.
Drawings from blocks/
use(orion, size:0.6, at:[0.3, 0.5]) rotate(dur:20, loop:0)
use(ursaminor, stroke:#ff2a6d, fill:#f6f740)
use(path:constellations, index:rand(0, 87))
use(name) puts an SVG from the project's blocks/ folder on the stage
(in a folder of its own, or blocks/ itself; src:"folder/file" names one
exactly). Its name is the file's, so orion > rotate(...),
gui(orion, size:0.8) and use(uid:orion, remove:1) reach it, and
Ctrl+Shift+. on its line takes it away. size: and at: are as for a shape.
path: with index: picks from a folder; that line is named after the
folder, so running it again swaps the drawing instead of adding one.
The file is drawn into the score, not shown as a picture: stroke:
recolours its lines, fill: what is filled, width: the line width, and
each id inside it can be reached with the drawing's name in front. A dot
can run along a constellation's lines while it turns:
shape(uid:star, sides:0, size:0.015, fill:#05d9e8) o2p(path:orion_lines, dur:8, loop:0)
The file loads a moment after the line runs, so run the o2p line after
the drawing is up. See use().
The look of things — gui
gui(name, …) changes how anything you made looks: a sound's box, a
shape, a cluster, a text.
gui(organ, size:[0.6, 0.55], fill:#000, view:polar, palette:neon, width:2)
organ > rotate(dur:12, loop:0)
A sound's box draws what it plays. view: chooses how:
view: |
|
|---|---|
wave |
the oscilloscope (the default) |
polar |
the signal wrapped round the middle: chords make rosettes |
phase |
the signal against itself a moment later: Lissajous-like figures |
spectrum |
level per frequency |
ridge |
the wave's recent past as stacked lines, each hiding those behind: span: beats held (bigger is slower), rows: lines, fill:none see-through |
waterfall |
the same with the spectrum |
size: at: |
where and how big (fractions of the screen) |
stroke: width: |
the trace's colour and line width |
palette: |
a colour per voice: live, grey, primary, pastel, neon |
trail: |
beats a trace takes to fade: 0.3 a smear, 2 a tail, 8 a long one |
fill: border: opacity: |
the box |
lag: / cycle: |
for view:phase: the delay in samples, or as a fraction of the sound's cycle (cycle:0.25: a sine stays a circle at any pitch) |
The phase view is worth exploring: what reshapes it is pitch against the
delay, the wave shape, a detuned pair (freq:[110, 110.7]), a resonant
filter. See gui: The phase view.
On shapes and clusters, gui sets fill, stroke, width, opacity,
size (single shapes), and:
gui(choir, trail:2) every member leaves a tail as it moves
gui(choir, fill:scope, stroke:#f6f740) each shape draws its own voice inside it
Linking image and sound
Everything that moves publishes signals: a rotation its angle, sin,
cos, norm (0..1 per turn) and tvel (turns per second), a shape its
x and y, an object on a path its t. follow() reads one into a
parameter, mapped to a range:
synth(uid:saw, wave:saw, freq:110, amp:0.03, pan:follow(tri.sin, -1, 1))
The triangle's turning pans the saw. map(src.ch, inLo, inHi, outLo, outHi) reads only part of a signal's range. A path becomes a fader:
shape(uid:track, sides:1, size:[0.6, 0.1], at:[0.5, 0.85], stroke:#555)
shape(uid:dot, sides:0, size:0.04, fill:#05d9e8) o2p(path:track, mode:alt, dur:0.4, loop:0)
synth(uid:organ, wave:square, freq:[110, 220.7], amp:follow(dot.t, 0, 0.05))
Add trig:touch to the dot's line and run it: it stops where it is and
you can drag it. ui(uid:dot, value:0, dur:8) fades it by code.
self. on a shape's line means that shape, and in a cluster each
member. A cluster that sings: each shape's height its pitch, its place
its pan, its spin its brightness:
shape(uid:choir, n:12, sides:irand(1, 8), size:rand(0.03, 0.12), at:[rand(0.1, 0.9), rand(0.2, 0.8)], fill:#f6f740) rotate(dur:rand(2, 9), loop:0) synth(wave:saw, freq:follow(self.y, 900, 90), pan:follow(self.x, -1, 1), filter:{type:lp, freq:follow(self.tvel, 200, 4000), q:6}, amp:0.02, waveform:none)
The projector
projector() opens a second window for the projector (a second display):
visuals only, running every line you run, silently and with the same
random rolls. Ctrl+Shift+P shows your code there too, or takes it off.
Rehearse on the real display: sizes and colours read differently on a
projector.
Recording — capture()
capture() records the window -- the score, the code if it is showing --
with Oscilla's own sound; capture() again stops and saves.
capture() the window's size, mp4, 30 fps, into recordings/ capture(size:720) 720 high, the window's shape kept capture(size:[1920, 1080], fps:60) exactly that, 60 frames a second capture(format:webm, wav:1) webm, and the sound alone as a WAV beside it capture(path:recordings/takes) another folder in the project capture(stop) stop and save
- The sound is Oscilla's own mix, as it is heard (after VOL): no microphone, no room, no other programs.
- The files go into the project as they are recorded, a second at a
time, named by when they started (
capture-2026-10-12_21-04-33.mp4); a crash keeps what was recorded.wav:1writes a 16-bit WAV of the same mix beside the video. - mp4 is H.264 video with AAC sound where the browser has AAC (macOS, Windows) and Opus sound where it does not (Linux); players and ffmpeg read both. webm is VP9 + Opus.
- In the Oscilla app it starts at once. In a browser, the browser asks first which tab to share (that cannot be skipped from a web page), and some browsers on Linux cannot capture the screen at all.
- A red mark shows for two seconds as it starts -- the page is what is
recorded, so it is in the first frames -- then goes; the console's header
counts the time (
oscilive ● 1:24). - What is in the picture is this window as it is: hide the console
(
Ctrl+Shift+L) for the visuals alone, or play in perform mode for the code over them. The projector window does not record (it would be a second recording of the same set).
Encoding video while playing costs some processing: try a whole set on the machine you will play on before relying on it.
A set, in practice
- Start sparse. One sound, one shape; let the image grow from the sound.
- Keep a stop within reach:
Ctrl+Shift+.for one line,Ctrl+.for all the sound,Ctrl+,for all that moves. - Keep ready lines you mean to use, out of sight: write one, press
Ctrl+Shift+S, and it goes to the set's stash (see Two stashes). Start from a blank console; typing the start of a stashed line offers the rest (Tabtakes it),Ctrl+Spacefinds one by any word in it. See The Stash. Or keep them on screen: a comment block, each line brought in by deleting its//and running it. - Mind the GPU on a projector: many trails and big scopes together cost frames. Watch for stutter in rehearsal and simplify.
- A reload brings the console back as you left it (the code, the cursor, perform mode); what was sounding and moving has to be run again, so keep the lines of the moment where you can find them.
Two stashes
| Where | Holds | Add to it | |
|---|---|---|---|
| The set's | vocab.live in the project |
the lines for this piece | Ctrl+Shift+S |
| Global | vocab.live in the projects folder |
patterns you reach for in every set | Ctrl+Alt+Shift+S |
The suggestions offer both: the set's lines first, then the global ones.
Both are plain text, to edit by hand as well (a blank line ends a
phrase, # starts a note). The global stash belongs to the computer
running Oscilla: a guest laptop in a shared session keeps its own stash
in the set, and is never sent yours.
See also
- Live Console: every key, panel and option
- synth(): every synth parameter and pattern
- gui(): views, trails, palettes, the phase view
- shape(): shapes, clusters,
self.
Tip: use ← → or ↑ ↓ to navigate the docs