Skip to content

Colour — the palette surface

The four shader-coloured scenes (fragment_field, swarm, reaction_diffusion, attractor) — and spectrum, which samples the same LUT on the CPU, one colour per element — colour through a shared palette (ADR-0021): a gradient — a built-in name or custom stops — baked into a lookup table the scene samples. An optional top-level [palette] table picks it; a [palette_b] + bindable palette_mix crossfades between two. A custom stop is authored in sRGB — the hex a picker gives you — and the engine decodes it to light when it bakes the LUT (ADR-0151), so a plateau written #c81423 renders #c81423; that is exact below the tonemap knee, and the colour surface has the domain. Colour modulation (saturation, color_span/color_center, hue_spread/hue_center, palette_mix) is normal audio-bindable [params]. All defaults reproduce each scene’s prior look ([palette]-less = the classic spectrum cosine), so a preset that sets none is unchanged. The other three line scenes join them (ADR-0059) — parametric_curve, lsystem and star_pattern all sample the same LUT on the CPU, each walking hue_spread along its own generator’s axis (see the colour axes; the star’s is inert until its interior is redesigned). With no [palette] that LUT is the line scenes’ familiar cosine — the swatch table is what its hue values actually look like.

[palette]
name = "ember" # or: stops = [ {at=0.0, color="#0b0b2a"}, ... ]
[params]
color_span = "0.3" # fragment/RD: low = a cohesive single-family mood
hue_spread = "0.15" # swarm/attractor: low = a coherent-colour cloud
saturation = "0.8 + mid * 2"

color_center (and hue_center) is a CYCLIC coordinate. It slides a window along the gradient, and the coordinate wraps — it does not clamp. So pushing it negative to reach the palette’s dark end lands the field in the palette’s bright stops instead, and the picture gets brighter. That cost the content lane three rendered iterations of chasing exposure and contour density, all downstream of a cause that was neither. To darken, change the palette’s stops or the scene’s own exposure; to move the tonal centre, keep the centre inside 0..1 and know that -0.1 and 0.9 are the same place.

Hard bands — palette_steps and palette_contour

The gradient is a smooth ramp, and palette_steps turns it into hard graphic bands (ADR-0078): the palette coordinate snaps to one of palette_steps band centres just before the LUT read. palette_contour then darkens a hairline where the coordinate crosses a band edge. Both default to 0 (off), and off is the exact identity, so adding them to an existing preset is the only thing that changes it.

ParamDefaultRange that readsAccepted
palette_steps0 (off)4120 = smooth, up to 64
palette_contour0 (none)00.50 = none, up to 1. Draws only where the two bands it separates are different colours (ADR-0133) — so it is silent inside a plateau and draws at each run boundary, which is what makes it usable on a limited-ink palette. Unchanged on a smooth one at any step count

Those two ranges were read off a rendered sweep rather than argued. Outside them nothing breaks — it just stops being the graphic look:

  • palette_steps 16 and up approaches the smooth ramp again. The bands get narrower than the eye separates them at, so the picture converges on the unbanded gradient. That is a legitimate destination if you want almost continuous with a hint of structure; it is not what to bind if you want bands. 412 is where the field reads as flat graphic areas.
  • palette_contour 0.8 is a deliberate topographic look, not a mistake. Past about 0.5 the dark line stops being an edge between two colours and becomes the dominant mark — the field reads as a contour map with colour fill. Bind it on purpose or not at all; do not arrive there by easing.

Three things to know before binding them:

  • palette_steps is stepped. It rounds to a whole number, like kaleido_order: a fractional band count leaves every boundary crawling across the field rather than stepping, which reads as shimmer. An eased or bound palette_steps still eases — it snaps at each half-integer.
  • palette_contour is inert on attractor, swarm, emitter and the line scenes, and nothing warns. A contour needs a gradient across a fragment to sit in, and those scenes take one palette sample per particle or per segment — the attractor’s in the vertex stage, where the derivative the contour is measured against does not exist at all. Banding reaches every scene; contours reach the continuous-field scenes, fragment_field and reaction_diffusion. The parameter is accepted everywhere because it is a known name, so no unknown-param warning fires; this paragraph is the warning.
  • Banding fights bloom. The bright pass blurs exactly the hard edges this creates, so a preset cannot have crisp bands and heavy bloom_amount at full strength. Pick one.

The cyclic hue character of a banded reference image is already reachable without this: the LUT is repeat-addressed, so a color_span above 1 wraps it and repeats the whole gradient across the field. palette_steps adds only the hard edge between one cycle’s colours and the next.

Full reference — built-in names, custom-stops rules, the per-scene colour params, and the A/B crossfade — is in Colour and palettes.

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