Skip to content

shape_collage — flat opaque elements on their own paper

This is the one system in the engine where one object is in front of another. Every other scene emits additive light, and additive light has no ordering: a black bar over a red one simply adds. shape_collage paints instead. Each pixel starts at the paper colour and walks an array of flat elements in array order, compositing each with over, so the array index is the depth (ADR-0123). There is no glow param, no bloom, and no soft edge — that is the vocabulary, not an omission.

Where the elements come from is layout’s answer: either the scene’s own authored suprematist canvas of fourteen elements, or one of three seeded layout grammars. What the music then does to them is the second table below.

paramwhat it does
counthow many elements of the canvas are live, in painter order. Quantized to a whole element CPU-side — an eased binding does not half-draw one — and bounded by the tier’s element cap. A non-finite value falls back to the whole authored canvas rather than to a blank frame.
layoutwhich composition builds the element list. 0 the authored canvas (a fixed fourteen-element composition; ignores seed), 1 anchor-and-satellites (one or two dominant planes with the rest clustered around them — the picture has a subject), 2 diagonal-axis (a dominant angle with elements along it and narrowly across it — the picture has a direction), 3 size-hierarchy (power-law sizes with position independent of size — a range but no centre). Quantized; anything off the roster falls back to 0.
seedwhich canvas a grammar composes. Whole numbers, 0 to 16777216 — the ceiling is where an f32 stops telling two seeds apart, not an arbitrary limit. Inert on layout = 0.
rosterwhich element kinds the canvas draws from. 0 suprematistquad, circle, triangle, and the default. 1 Kandinsky — those three plus bar (a rounded-cap stroke), ring (an annulus), segment (a circular sector), arc (an annular sector) and checker (a checkerboard patch), and about one element in four drawn translucent so crossings show both forms. Quantized; anything else falls back to 0. Inert on layout = 0.
size_hierarchy0..1, how steeply generated sizes fall from the largest element to the smallest. 0 is a nearly uniform canvas; 1 is a few dominant forms over many small ones. Inert on layout = 0.
angle_biasthe canvas’s dominant angle, in degrees. Each grammar scatters around it differently — tightly on 1 and 2, loosely on 3. Wraps rather than clamping, so a drifting binding keeps turning. Inert on layout = 0.
scalethe canvas’s size in the frame. 1.0 is the composition as composed.
paperthe ground’s palette coordinate. A raw coordinate: color_span and palette_shift move the elements and deliberately do not drag the ground with them, so a colour drift does not take the paper along.
opacitya global multiplier on every element’s own alpha. 1.0 — fully opaque — is the look; below it the whole canvas turns translucent over the paper.
edge_softnessextra pixels of coverage ramp past the one-pixel analytic edge. 0 is the hard edge and is the default. This is an escape from the look, not a quality knob — antialiasing is already exact at 0.
color_span, palette_shiftscale and offset applied to every element’s stored palette coordinate, so a whole canvas can be recoloured or drifted through the gradient at once. Both are the identity by default.
saturation, palette_mixthe shared palette controls, unchanged.

The four reactivity levers. Every one of them defaults to the identity, so a preset that binds none of them draws a still canvas.

paramwhat it does
recomposerising past 0.5 recomposes the canvas once. Edge-triggered, like reseed on the particle scenes and for the same reason: a sustained gate must not re-run the generator every frame. Each recomposition advances the generator’s recomposition index, so successive canvases differ without the preset changing seed.
recompose_blendseconds the recomposition crossfades over. 0 — the default — is a hard cut. Above it, both canvases are on screen for that long and the outgoing one dissolves through the paper as the new one arrives. Note the cost: a blend has two whole canvases in the per-pixel loop, so it is the one moment a preset pays double.
densitywhat fraction of count is live, 0..1. Elements fade in and out over about half a second rather than popping. Birth order is stable: raising density only ever adds — an element already on the canvas never moves, changes colour, or loses its place in the painter’s order.
drift, spinmultipliers on each element’s own velocity and angular velocity, drawn from the seed at generation. 1 is a slow travel — an element crosses the canvas in about a minute — and higher is legal. The travel is measured in real seconds, so the canvas covers the same ground per second of music at any refresh rate. An element reaching the canvas edge wraps to the other side. Both integrate a phase (ADR-0132), so both are safe to bind to audio: a binding that moves steers the canvas from that instant and does not rescale the ground it has already covered, and the accumulation restarts whenever the canvas is laid out again. The price is the same fact in reverse — a rate has to persist to be seen at all, so bind them to a band envelope rather than to an onset, whose spike is over before it has moved anything, and judge the result over a passage rather than over one hit.
pump_size, pump_alphadepths of a per-element swell in size and in opacity. Phase-offset per element, so the canvas does not breathe in unison — a field of oscillators sharing a phase reads as one sheet flashing. The rate is the engine’s and is not a parameter; drive the depth from the music.

beat_index counts onsets, not beats, and no fixed multiplier converts (ADR-0109). It runs at 1.2x-2.3x detections per musical beat depending on the material and wanders between 1x, 2x and 4x inside one track, so a recompose hung off it has no period at all — see the trap in Expression language. Do not compensate for it inside a preset, because there is nothing stable to compensate against. What beat_index is good for here is exactly what the shipped canvas uses it for: hash(beat_index) re-rolls the arrangement on activity without claiming a beat count.

Keep the palette under linear 0.6 - sRGB byte 0xcb in the hex you write - and the look is free. This is the one authoring fact this system has, and it comes off the tonemap rather than off any parameter — see Linear light and exposure and the paragraph on the knee in Colour and palettes. Below the knee the tonemap curve is exactly the identity, so the fill survives the post chain unshaded — the value the element wrote is the value that leaves the tonemap — and bloom’s threshold sits above that same knee, so the edges stay hard with no halo. One constraint, both properties, no parameter. Reach for a brighter palette and you lose the flat fill and the hard edge together, and nothing will tell you why.

Unshaded also means the hex you typed. A stop is sRGB and is decoded once at load, so the decode and the display encode are inverses and the identity curve between them changes nothing: in collage_suprematist, #494949 renders #494949 and #c24f63 renders #c24f63. The only residual is the LUT’s own 8-bit linear storage, up to two levels on a very dark channel. Every element is shifted by the same curve and none of them is shaded or haloed, which is the property the look rests on.

The paper is the deliberate exception: f(1.0) = 0.800 makes pure white unreachable, so an off-white ground is the affordable one. Both of the reference canvases this system was built from are off-white anyway.

Author the palette as plateaus, not as a gradient. An element takes one colour off the LUT at its coordinate, so a smooth ramp shades every element by where it happens to sit. A pair of stops a ten-thousandth apart is a hard transition; presets/collage_suprematist.toml is eight flat bands built that way, and its header carries the arithmetic.

Eight is not a coincidence. A layout grammar draws each element’s colour from eight evenly spaced coordinates at band centres (k/8 + 1/16), and reserves the last for the paper so a generated element never draws the ground’s own colour and vanishes. An eight-plateau palette therefore resolves every generated element exactly; a palette with a different number of bands still works, but its elements land wherever those eight coordinates happen to fall.

Built from a8ce055 at version 0.115.0. This site tracks main and is not versioned per release.