Live

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:

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

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

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

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