Browser-based live coding of audio and visuals: Web Audio synthesis with an animated scope for each sound, and SVG shapes, text, the camera and stage effects driven by audio analysis, camera motion and a beat clock.

Play

Room

With others: each sees the others' code and counts the same beats. The first to join opens the room with the password they give.

Read

© 2026 Rob Canning · AGPL-3.0

Oscilla Live (olive)

A browser-based live-coding environment for audio and visuals -- olive, for short. Each line is a cue: run it (Ctrl+Enter) and it takes effect at once; run an edited line and the running sound or drawing is updated in place, without restarting.

Scopes

Every sound is drawn in a box of its own by an analyser-driven scope, redrawn each frame: waveform, polar, phase, X/Y, spectrum, ridge, waterfall or barcode. viz() sets its view, position, size, colour, trail and rotation. See Visualisations.

Signals

Every sound publishes its level, pitch, spectral bands and step events; the beat clock, MIDI controllers and the audio input publish theirs. Any parameter can follow one, e.g. follow(bass.level, 0, 360).

Timing

Times are in beats of one tempo. Patterns (Pseq, Prand, Pwhite …) sequence values; | scale(minor) quantises pitches to a scale; quant: aligns a change to the beat grid; trig: runs a line on a signal, e.g. trig:every(4, beat.hit).

Implementation

Audio uses the Web Audio API: oscillators, filters, envelopes and reverb in the audio graph, AudioWorklets for custom processing, and an AnalyserNode per sound for its scope and signals. Graphics are SVG updated per frame, with canvas for trails and ridges. Plain JavaScript modules, no installation. On an Oscilla server, rooms share code between performers over a WebSocket relay and synchronise their clocks.

Visualisations

Each sound's scope

Every synth draws what it plays in a box of its own, redrawn every frame from its sound. One line chooses how: viz(name, view:…). The box keeps its view for every voice and every later run.

waveviz(bass)The oscilloscope: level across time. The default.
polarviz(bass, view:polar)The wave wrapped round the middle of the box: angle is time, distance is level. Chords and harmonics make rosettes.
phaseviz(organ, view:phase, cycle:0.25)The sound against itself a moment later: ellipses, diamonds and knots, turning as voices detune. A sine is a circle at cycle:0.25.
xyviz(x1, view:xy, y:y1)One sound across, another up, as an oscilloscope in X/Y mode draws a stereo pair: Lissajous figures, turning slowly when the two are a little detuned.
spectrumviz(pad, view:spectrum)Level per frequency, low on the left, on a log scale.
ridgeviz(bass, view:ridge, rows:48, span:8)The wave's recent past as stacked lines, the newest in front, each hiding what is behind where it rises.
waterfallviz(pad, view:waterfall)The same stack, of the spectrum.
barcodeviz(pad, view:barcode, rows:96)The spectrum as black and white bars: a column on while its band is louder than its own average.
external inputadc(ch:l, uid:left) adc(ch:r, uid:right) viz(left, view:xy, y:right)The external audio input — a microphone, a mixer's send, another laptop — drawn as a synth is, in any view: here a stereo input's left against its right.

Shaping the box

trail:2 leaves fading tails; spin:20 turns each voice's trace, one turn in 20 beats; color:#ff2a6d (or colour:) colours the trace; palette:neon gives a poly: sound's voices a colour each, palette:[red, #05d9e8, blue] one of your own; blend:difference mixes the box with what is under it; size:1 and at:[0.2, 0.3] place it; bass > rotate(dur:8) turns the whole box.

Laying out the stage

grid() cuts the stage into columns and rows; cell: puts a box, a shape or a word in one, and the cells can be dealt out again on the beat. In a cell, size: and at: are shares of it; the effects stay on through every move.

grid()grid(3, 2, show:1)3 columns, 2 rows, drawn faint and numbered while you lay them out; grid(4) 4 by 4, cols:[2, 1, 1] the first twice as wide, gap:0.02 space between.
cell:viz(bass, cell:4, across:3)A box in a cell, counted along the rows from 1 (or [col, row]) — here the whole bottom row; down: spans rows.
shape(n:6, sides:5, cell:free)Each member of a cluster in an empty cell, at random; cell:any any one.
movinggrid(shuffle, trig:every(4, beat.hit))Everything dealt out to other cells every bar; grid(turn) moves each one cell on.
viz(lead, cell:pseq(1, 3, 2, inf), trig:beat.hit)One box walking a path of cells on the beat; cell:free to an empty one each time.

The rest of the picture

Around the scopes, the stage itself: CSS filters and blend modes, live SVG copies of the stage, and SVG shapes and words moved on their transforms — most of their values able to follow() a sound.

screen()screen(kaleido:6, turn:pseq(0, 15, 30, 45, inf), trig:beat.hit)The whole stage folded, mirrored or tiled (kaleido, mirror, tile), as live copies of itself.
screen(warp:follow(bass.low, 0, 80), split:6)Warped as through moving water; colour split as through a bad lens; hue, invert, contrast, blur, pixel too.
on one boxviz(bass, kaleido:6, hue:120)Any of those on one sound's box, the rest of the stage as it is; a shape takes the colour ones.
blendviz(pad, blend:difference)How boxes, shapes and words mix where they overlap: difference cuts holes, screen adds light, multiply adds ink.
shape()shape(uid:a, m:5, n1:0.3, n2:0.3, n3:0.3, morph:2)Polygons, circles and superformula forms — stars, flowers, cogs — morphing one into another.
shape(n:12, sides:irand(3, 8), size:rand(0.03, 0.12))Clusters of them, each its own; fill:scope draws a shape's own sound inside it.
motionbass > rotate(follow(bass.t, 0, 360))rotate(), scale(), color(), trans() on anything drawn, stepped by patterns or following a sound.
text()text(pick:["NOW", "DROP"], dur:0.2, trig:every(4, beat.hit), flash:white)Words picked, cycled or scrolled, arriving zoomed or blurred, flashed on the beat.
image(), video()image(uid:p, src:"https://…/plate.jpg", at:[0.75, 0.3], size:0.3)Pictures and film placed on the stage as shapes are, a folder dealt out as a slideshow (path:plates, dur:1); viz(p, blur:2, warp:20) puts a box's effects on a picture.
scatter()s > scatter(0.4, dur:2)A drawing's paths thrown about, and brought back with scatter(0).

The scopes, in the viz() docs · grid() · screen(), blend and scatter · shape()