spectrum — the frequency-axis readout
A line system like the three above, but its figure is not a generator’s geometry:
it is the engine’s log-spaced band array, divided into elements by the
[spectrum] table. Element 0 is the bottom of the
spectrum and the last is the top, so nothing in [params] maps audio to
position — that mapping is the scene. What the params say is how the elements
look and how far they reach.
base— the length every element has before any audio, in world units (the frame is 2 units tall). Deliberately not zero by default: at zero the readout vanishes completely in a silence, which reads as a broken preset. Bind it totimefor a resting breath.scale— multiplier on the element’s own band level. The bands read small on real music, the same caveat asbass/mid/treb, so useful values are well above 1.curve— the level-shaping exponent, default1.0(exactly linear, the map this scene had before it existed).level^curve, applied to the element’s raw level before the easing;0.5is a square root and lower values compress harder, which is how you get a dB-like readout where the quiet elements are legible instead of stubbed. Per element, so it can be walked withindex. The level is floored at0and the exponent clamped to[0.05, 4.0], so no expression can produce aNaNlength.radius—radial_ringonly: the inner circle the spokes stand on. No effect onbarsorpolyline.span— the figure’s half-width in world units, default1.0.barsandpolylineonly (the ring is sized byradius).baseline— the world y the elements stand on, default-0.85, so bars grow upward from near the bottom edge.barsandpolylineonly.
curve and scale are not independent, and the factor is large. Measured
typical band levels are ~0.02–0.05. At curve = 0.5 a level of 0.03 becomes
0.173 — a 5.8x boost — so a preset adopting a curve has to bring scale
down by roughly that factor or the readout leaves the top of the frame. That is
why the default is exactly 1.0: a curve is opt-in, and opting in means retuning
scale in the same edit. Say so in the preset’s header comment, with the factor.
What curve does not disturb is the timing. The easing runs on the curved
value, which is the value you see, so a fall’s time constant is exactly the
release you wrote — at curve = 1.0, at 0.5, at 0.25. The two knobs are
independent in time; what a curve changes is amplitude. (Curving after the
easing instead would have made the effective release release / curve, silently
doubling every fall at curve = 0.5. That ordering was measured and rejected —
ADR-0040.)
Neither ordering produces an even fall: a one-pole is exponential, so it covers
the first half of its travel in about 30 % of its settling time at any curve.
How wide the readout is, and why span is not a “fill the frame” switch. The
line renderer divides world x by the target’s aspect — the same rule every line
scene follows, and the reason a radial_ring comes out a circle rather than an
ellipse. span is therefore a world quantity, not a fraction of the frame:
the default 1.0 makes bars and polyline span the frame’s height in
pixels, which is about 56 % of its width at 16:9. span ≈ 1.78 fills a 16:9
frame edge to edge — and leaves an ultrawide short, because 1.78 is still 1.78
there. That is correct behaviour for a world quantity and the reason there is
deliberately no fit/auto mode: a scene that sized itself from its render
target’s aspect is exactly the trap
ADR-0037 exists
to forbid, and it has shipped twice in this codebase. Pick the span that suits
the frames you care about, or bind it and accept that the number means world
units. Note zoom is still not a substitute for either lever — it scales y with
x, so widening the comb also lifts it.
baseline = 0 is how you get a centre-mirrored readout. With the default
-0.85, mirror_reflect produces two combs pinned against the top and bottom
edges growing toward each other — which reads as a bug and is not. The geometry
mirror reflects across the x-axis on every line scene, so a figure standing at
-0.85 reflects to one hanging from +0.85. Move the feet onto the axis and the
same mirror gives the symmetric “landscape and its reflection”: bars up, their
copy down, sharing one foot line on the frame centre. pan_y cannot substitute —
the mirror runs on world coordinates in update() while the view transform is
applied later in the shader, so panning moves the mirrored pair together.
rotation— turns the whole figure about the frame centre, in radians, on every layout. On the ring it is the natural motion; on bars and the polyline it tilts the readout, which is what makes those two worth folding withmirror_*.hue_spread— walks the palette across the elements:0(the default) colours the whole figure one hue,1spans the full palette from the lowest element to the highest, so you can see where a peak is without counting positions. Onradial_ringa spread of exactly1makes the wrap continuous in colour as well as in position.
Per-element bindings. This is the one system where a
single [params] expression can say something different about each element. A
binding whose text names index — the element’s normalized 0..1 position along
the frequency axis — is evaluated once per element instead of once per frame:
[params]thickness = "0.01 + bin(index) * 0.05" # thick where that element's band is loudbase = "0.16 + index * 0.12" # a longer rest toward the quiet top endhue = "index * 0.3 + time * 0.02" # colour walked by hand instead of hue_spreadThe params that genuinely vary per element are the ones describing a single
element — base, scale, curve, thickness, brightness, hue. The rest
describe the whole figure (span, baseline, radius, rotation, glow,
hue_spread, palette_mix, saturation, the view transform, the mirror), so a
series aimed at one of those takes its index = 0 value rather than being
dropped. index reads 0 on every other system, and a [smoothing] entry naming
a per-element binding is a surfaced warning — ease the levels with
[spectrum] smoothing instead. Full semantics in
Expression language.
What this system honors, since a silent no-op would be worse than an absence:
the shared view transform, the geometry mirror, the palette surface
([palette]/[palette_b]/palette_mix/saturation, sampled per element — this
is the only line system that reads [palette]; the other three still colour
from the built-in cosine), and all the engine stages (bg_*, trails,
kaleido_*, bloom_*, exposure, ink_*). On radial_ring the geometry
mirror is close to a no-op
for the same reason it is on star_pattern — the figure is already rotationally
symmetric about the frame centre, so the copies land on the original. On bars
and polyline it is genuinely transformative.
Built from a8ce055 at version 0.115.0. This site tracks main and is not versioned per release.