Visuals
Cheap tricks on what is already drawn -- in the spirit of Hydra, without a renderer: the stage is folded, repeated and tinted as it is.
screen(...) -- the whole stage
screen(kaleido:6) six mirrored wedges
screen(kaleido:8, turn:45) ... the wedge taken from 45° round (0 is up)
screen(mirror:x) the left half, mirrored right (y: top over bottom)
screen(tile:3) a 3 x 3 grid of the stage
screen(hue:120, invert:1, contrast:1.5) colour
screen(saturate:2, blur:2, pixel:8)
screen(warp:30) rippled, as through moving water (px at most)
screen(warp:60, grain:400) ... in big slow swells (grain: the ripples' size, px)
screen(warp:follow(bass.low, 0, 80)) the stage melting with the bass
screen(split:6) red and blue pulled 6 px either side of green
screen(split:follow(every(4, beat.hit), 0, 20)) a glitch on every bar
screen(hue:follow(bass.level, 0, 360)) any of them can follow a signal
screen(invert:follow(every(4, bass.hit), 0, 1)) a flash on every 4th note (hit: every note)
screen(off) all back to normal
screen(invert:follow(beat.div(4), 0, 1)) a strobe on the clock: every 16th (div(2) 8ths, div(3) triplets)
screen(invert:follow(every(8, beat.hit), 0, 1)) ... every 8 beats (beat.hit: each beat, every(4, beat.hit): each bar)
text(pick:["BASS", "DROP", "NOW"], dur:0.2, fade:20, trig:every(4, beat.hit),
style:"font-size:160px; font-weight:900") a word flashed with it, a new one each bar
text(pick:["WTF", ["BRO", "WHAT"], ["ONE", "TWO", "THREE"]], dur:0.1, trig:every(4, beat.hit))
... a pair in 8ths, three in triplets, on the beat
text(pick:["WTF", ["BRO", "WHAT"]], dur:0.1, trig:every(4, beat.hit), flash:white, style:"color:black")
... each word its own flash (flash:invert inverts instead)
screen(clear) every trail wiped, once
screen(clear, trig:every(4, bass.hit)) ... on every 4th note -- with the flash above, a clean slate each time
Every one of these also goes on a single sound's box or shape, the rest
of the stage left as it is -- gui(s3, kaleido:6, hue:120),
gui(s3, warp:follow(s3.level, 0, 40)), gui(s3, fx:off)
(gui()).
kaleido:N |
N wedges, every other one mirrored, about the centre. Only what is in the first wedge is seen -- turn it with turn: |
turn:deg |
where the kaleidoscope's wedge is taken from: 0 up, 90 right. The fold takes that wedge, from the middle of the stage outwards, so what is to be folded must be in it: something in the middle and round looks the same folded. turn:Pseq(0, 15, 30, 45, inf), trig:beat.hit steps it round on the beat, each step a new pattern; turn:follow(beat.phase, 0, 360) sweeps it once round a beat |
mirror:x / y / xy |
one half mirrored onto the other (xy: a quarter, four ways) |
tile:N |
the stage repeated N x N |
hue:deg |
colours turned round the wheel |
invert:0..1 |
towards the negative |
contrast:k / saturate:k / bright:k |
1 as it is |
blur:px |
soft |
pixel:px |
blocks of that size (a thin line comes out as dots) |
warp:px |
the stage pushed about by drifting noise, up to that far: the ripples flow round once every 8 beats |
grain:px |
the size of warp's ripples (150 by default): 40 a shiver, 500 a slow swell |
split:px |
red one way, blue the other, green where it was: the colour fringes of a bad lens or a glitch. Negative swaps them. |
One of kaleido, mirror, tile at a time (that order wins); the colour
ones go on top of whichever. 0 turns one off.
A new screen(...) changes only what it names: screen(hue:90) after
screen(kaleido:6) keeps the kaleidoscope. Every value can be a
follow(), so the screen moves with the sound.
grid(...) -- the stage in cells
grid(3, 2, show:1) 3 columns, 2 rows -- drawn faint and numbered while you lay them out
gui(bass, cell:4, across:3) bass's box along the whole bottom row
gui(lead, cell:1, view:polar) cells count along the rows from 1 ...
gui(pad, cell:[3, 1]) ... or [column, row]
shape(uid:dots, n:2, sides:5, cell:free) a cluster: each member an empty cell
grid(show:0) the lines away, the cells kept
grid(shuffle, trig:every(4, beat.hit)) everything dealt to other cells every bar
grid(turn, trig:beat.hit) ... or each one cell on, every beat (turn:-1 back)
gui(lead, cell:Pseq(1, 3, 2, inf), trig:every(2, beat.hit)) lead's box walking round three cells
gui(lead, cell:free, trig:every(4, beat.hit)) ... or to an empty one, every bar
grid(off) no grid: things stay where they were
grid(cols, rows) cuts the stage into cells: grid(4) is 4 by 4,
grid(rows:3) three bands one above another, cols:[2, 1, 1] makes the
first column twice as wide as the others, and gap:0.02 leaves a gap
between cells (a share of the stage).
cell: on a gui() or a shape() line puts it in a cell: a sound's box
fills its cell, a shape takes 0.8 of the cell's shorter side, a text sits at
its middle. With cell:, size: and at: are shares of the cell --
gui(s1, cell:2, size:0.5, at:[0, 0]) is half the cell, at its top-left
corner -- and across: and down: let it span more than one cell. cell:
takes a number, [col, row], free (an empty cell, at random), any,
choose(1, 4, 6), irand(1, 6), or a pattern (Pseq, Prand ...), which
moves one step each time the line runs. Each of these lines can run on a
signal (trig:), and still() stops them.
What is in a cell stays in it: a new grid (grid(4) after grid(3, 2))
puts everything in its cell again, a shuffle or a turn moves it, and
gui(s1, at:[0.5, 0.5]) takes it out of the grid. The effects
(kaleido:, blur: ...) stay through every move. A box across two cells
that a shuffle puts in the last column is moved back in, over its
neighbour. A shuffle, a free or an any is rolled on each machine on its
own: in a room, each player's stage is dealt its own way.
blend: -- mixing what overlaps
shape(n:8, m:5, n1:0.3, n2:0.3, n3:0.3, size:rand(0.2, 0.5), at:[rand(0.2, 0.8), rand(0.2, 0.8)],
fill:#fff, stroke:none, blend:difference) rotate(dur:rand(3, 9), loop:0)
stars that cut each other out as they turn
gui(s1, fill:#05d9e8, blend:difference) a sound's box: overlapping boxes mix
text(pick:["NOW"], dur:4, blend:difference, style:"font-size:300px; color:white")
black where it crosses something white
difference flips what it crosses -- white over white is black, over black
it is white -- which suits black-and-white work: shapes, boxes and words
cut holes in each other. screen adds light, multiply adds ink; the
others are CSS's mix-blend-mode (exclusion, overlay, hue ...).
blend:normal puts it back. A shape, a cluster (gui(sh1, blend:...)), a
sound's box and a text each take it.
scatter(...) -- throwing paths about
s > scatter(0.4, dur:2) s's paths thrown about, over 2 beats
scatter(0.4) with no target: the whole stage's
s > scatter(0) back where they were
s > scatter(follow(s.level, 0, 0.6))
Each path (polygon, line, text ...) is moved and turned at random, by up to
the amount -- a fraction of the target's size (for a cluster, the stretch of
stage its members cover); scatter(0) brings them back. A shape goes whole,
each of a cluster's its own way, so a rotate() on it still turns it about
its own centre where it lands. Nothing is thrown off the screen: what would
go past an edge comes back off it, so a bigger amount (scatter(2)) spreads
them further without losing any. dur: is how long the throw takes, not how far.
Run again, it throws them anew; a followed amount keeps the directions and
only goes further or nearer. Running the shape's own line again (or the whole
buffer, Ctrl+Shift+Enter) puts it back where it was. A sound's box scatters what its scope draws:
with poly:, each voice's trace flies off its own way (fz > scatter(0.8)).
The cost
kaleido, mirror and tile draw the stage N times -- live copies of
it, the scopes included -- light on a live-coding stage, heavy on a long
written score. While they are on, the original is hidden underneath (it
still takes the pointer). blur, pixel, warp and split are filters
on the whole stage, worked out every frame -- on a laptop's own GPU
(Radeon 680M, 1920 x 1080) warp and split each cost about what blur:3
does, and kept 60 fps; on a machine drawing without a GPU they are heavy. Keep resources()
showing while you try them: the fps tells you.
Tip: use ← → or ↑ ↓ to navigate the docs