What the report's columns mean
preset bass mid treb onset drive anim rate cover level rise fall Shatter 0.099 0.064 0.013 0.106 0.103 0.092 0.0357+ 0.997 0.0316 11+ 4+ Stipple 0.050 0.013 0.001 0.011 0.057 0.010 0.0132+ 0.222 0.5313 7+ 26+| column | question it answers |
|---|---|
bass mid treb onset | how far the frame moves when that stimulus alone comes up, against silence — “does this preset respond to bass at all” |
drive | how far the frame moves under the combined stimulus — silence against everything up at once, same depth and same size (the two motion readings) |
anim | how far the frame moves between two capture depths under silence — does it have a life of its own |
rate | how far the frame moves frame to frame — the only column that walks consecutive frames, and the only one measured at 96x96 (the two motion readings) |
cover | fraction of the frame that differs from the corner background — a low value is often correct |
level | how much light the picture carries: mean linear light over the pixels cover counts as lit (what the level column measures) |
rise fall | the transient probe (below) — frames to settle after a step up, and after the matching step down; a + suffix means the value is a lower bound, not a measurement (below); read them as evidence, not a verdict |
What the level column measures
Linear light, over the lit set (ADR-0150). Two halves, and both matter:
- Linear, because the stored pixels are sRGB-encoded and that curve is
concave: a mean over the bytes under-reports a brightness change by roughly
half. That factor of two is the property, and it is what the statistic is for.
The move you see is not the trim you made, and expecting it to be will
make a working column look broken. Trimming
star_rosewindow’sbrightnessby 30 % moves this column from0.0532to0.0408— 23 %, measured 2026-09-01 on the DX12 software adapter. Two things eat the rest, and both are the pipeline rather than the statistic: the tonemap is the identity only below its knee and compressive above it, so bright content moves less than its source; and the lit set itself shrinks as dimmed pixels fall undercover’s threshold, which lifts the mean of what stays. - Over the lit set, the same pixels
covercounts, so an authored background does not dilute the reading. The two columns read one picture from two directions:coveris how much of the frame is lit,levelis how bright that lit part is. In the sample aboveShatterfills the frame at a low level andStipplelights a fifth of it, brightly.
It is a comparison number and never a threshold. There is no level a preset ought to hit, and one row’s cell says nothing on its own. What it is for is a before/after on the same preset — did this change make it brighter, and by how much — or an ordering across a family. The lit predicate is a threshold on the stored bytes, so the statistic is linear light over a set chosen in code space; ADR-0150 records why that seam is accepted rather than solved.
The blind spot, by construction: a preset that goes wrong by changing its
background is invisible here. That is the price of not being a background
detector, and cover is the column that sees it.
The name column is fourteen characters wide, and a longer name is elided in
the middle, not at the tail: Tiled Rosette Mono prints as Tiled R~e Mono.
The tail is what distinguishes a name in this library — Mono, Gallery,
Bordered, Walk — and a tail truncation threw it away, which is how two
presets came to print as one row label in all three tables (design-backlog 0131).
A ~ in a label means characters were dropped there.
Two extra labeled blocks print under the table (the table itself stays un-widened,
so every historical number keeps its place): the realistic-levels reading
(reactivity_low — the same bands at the levels real music reaches, ADR-0042) and,
since Plan 0077, the footprint reading (reactivity_footprint) — the same
differentials divided by the union of lit pixels instead of the whole frame
(metrics::footprint_diff, ADR-0091). Read the footprint block when a mean band
column shows ~0.000: reactivity concentrated in a small footprint — a bloom_amount
halo, a sparse figure — is diluted by the whole-frame mean, and before this reading
existed the house workaround was binding a flash lever just so the report had
something to see. On a backdrop-heavy preset the union mask approaches the whole
frame and the reading degrades toward the mean column — it never sits meaningfully
below it, so it fails toward the old behaviour rather than inventing reactivity.
Every one of those but the last two is a settled measurement: the capture
holds one stimulus for every frame it renders, so each smoother has converged
long before the pixels are read. That is the right question for “does it
respond”, and it is exactly why those columns are identical for any
[smoothing] constant.
The two motion readings
drive and rate were added by ADR-0134,
which exists because the report could not see either axis. Every other statistic
on this page is a settled differential — two frames captured a fixed count
apart — so it cannot see rate at all, and the reactivity columns drive one band
at a time, so they cannot see combined drive either. A preset needed four live
passes with a person watching before the lane found its defect, and the harness
stayed green and unchanged through all four.
drive is the silent 48-frame capture differenced against the fully-driven
one: same frame count, same scene time, same REPORT_SIZE. It is the question
the listener asks — does this change when the music does — and it is not the
same question as the four band columns beside it, which drive one stimulus each.
A preset reading several bands together can measure low on every one of them and
still be strongly driven; one can read plausibly on all four and be driven by
none of them together. A preset with no audio binding at all reads 0.000.
rate is the mean difference between consecutive frames over the settled
tail of the transient probe’s loud plateau. It separates frozen from boiling,
which is the axis the report has never had.
Both ride captures the report already takes — no extra render pass, no extra readback, no resize — so the full-library wall clock is unchanged.
Two things to know before reading either number:
rateis measured atPROBE_SIZE(96x96), notREPORT_SIZE(192x192) like every column printed beside it. It rides the probe’s frames, and the probe runs small for the reasons in the transient columns.frame_diffis a normalized mean so the two are broadly comparable, but “broadly” is not a property: never compare arateagainst a number measured at another size.--jsoncarriesmeasured_at_pxbeside the value for exactly this reason.- A
ratecell carries the same+mark the transient cells do, taken from the probe’s ownrise_settled. An unsettled rise means the frames it averaged were still travelling toward the plateau, so the number describes the transient rather than the steady motion. The mark is common, not exceptional — see the transient section on why a great many presets never settle inside 48 frames.
Neither number is a gate, and rate does not sort the library. This is the
finding the ADR turns on rather than a caveat bolted to it. Motion inside a
static repeating structure reads as calm — the eye reads a pattern updating in
place — while the same amount of motion in an unanchored world moves the whole
sheet bodily and has to be far quieter to watch. Measured: fragment_tiledmono
and fragment_drostemono both sit higher than a draft that was rejected for
shaking, and both are comfortable. No pixel statistic in this harness models
anchoring, so any threshold consistent with the rejected draft would fail two
shipped presets. Read both columns against family neighbours, the way geom
is read (ADR-0083),
and never sort on them.
The transient columns
rise and fall are the one pair that is not settled. The probe drives a
step — silence, a held stimulus, silence again — reads back every frame, and
counts how many it takes for the frame to reach 90 % of its total change each
way (ADR-0039).
That makes ADR-0035’s
{ attack, release } pair visible: a scalar [smoothing] entry selects the same
constant both ways, so its two numbers match; a pair snaps up and glides down, so
fall runs well past rise.
Reading them:
1and1— no easing on whatever the stimulus drives. The frame is fully there the frame after the step.- equal, both large — a scalar
[smoothing]entry, or an asymmetric one whose two constants are close. fallmuch larger thanrise— an{ attack, release }pair doing its job.0and0— the step did not move the frame at all. Check the reactivity columns: this usually means the preset does not respond to the stimulus, not that its easing is instant.
What the transient columns cannot see
The probe measures the frame, not the parameter. It reads pixels, so it can only see easing through whatever curve the scene puts between a bound value and its output — and for most scenes that curve is neither linear nor even monotone. Two consequences, both real and neither fixable by measuring harder:
- A saturating response reads flat.
rose_trailsis the worked example: its 1.25 spin against a max-decay feedback drives the frame to the same place whateverthicknesssays, which is why the content lane once rendered five values from 1.10 to 2.30 — including the untouched original — and could not tell them apart. A preset like that will report a transient that has nothing to do with its[smoothing]table, and no column here will warn you. - The scene’s own motion is measured too. A fragment field’s fold, a feedback
trail and a particle cloud all keep changing while the parameter settles, and
the probe cannot separate that from the response. Over the shipped library this
shows up as presets reporting
fallbelowrise, which is backwards for any easing.
Measured over the shipped set on 2026-07-27 — a snapshot of what the probe sees,
not a figure anyone maintains — presets carrying at least one
{ attack, release } entry had a median fall / rise of 1.02, with
fall > rise in about half of them; presets with only scalar entries sat at
0.60, with barely any. So the columns separate the two populations
directionally and lose the magnitude almost entirely:
Smooth Pulse, the worked asymmetric example with a 0.60 s release,
reads 26 / 31 where a purpose-built near-linear fixture at a 0.5 s release reads
3 / 61.
Those numbers were taken before the
+marker existed, and every one of them would carry it today (Plan 0038 Phase 8). They were produced by exactly the defect this section goes on to describe: atPROBE_WINDOW= 48 a 0.60 s release is 1.33 τ, leaving ~26 % of the travel undone, and the fixture’s3 / 61is now known to be3 / 69when measured to settlement. Read the snapshot for its shape — two populations, separated directionally, magnitude lost — and not for its magnitudes, which is the same warning the rest of this section gives, now with the arithmetic behind it. It has not been re-taken: re-snapshotting is not what makes the columns trustworthy, marking them is.
Read the columns as evidence, not as a verdict. A wide fall / rise gap is
good evidence the easing is working. A narrow one is not evidence it is broken.
The place easing is proven is core/tests/easing.rs, against fixtures built to
have a near-linear response precisely so the measurement is of the easing and not
of a scene; everything else is a preset-shaped approximation of that.
One smaller limit, and it is sharper than it was first written: the probe’s window
is 48 frames (0.8 s) each way, so a release constant longer than about 0.35 s
does not fully settle inside it. This page used to say such a response “reads
clamped” — it does not, and that word was the trap. frames_to_settle
normalizes against the segment’s own last frame, so when that frame is still
travelling the measured total is short and every threshold is crossed early. The
number that comes back is not pinned at the window length and is not obviously
wrong; it is a plausible, smaller frame count. Worse, the bias is uneven — the
0.9 threshold is pulled in harder than the 0.5 one — so a truncated fall also
reads as a more even fall than it is.
That is not hypothetical. Plan 0038 Phase 3 measured two easing orderings, one of which had an effective time constant of 1.0 s against a 1.6 s window, and read the truncation as a difference in the shape of the two falls. Measured to settlement the two shapes are identical and differ only in speed — 73 frames against 145, where the truncated run said 61 against 78. See ADR-0040’s Outcome.
The rule, and the function that enforces it. frames_to_settle cannot detect
this about itself: normalizing against the last frame guarantees the threshold
is crossed inside the segment, so frames_to_settle(seg, f) < seg.len() is a
tautology rather than a check. Before trusting a frame count, gate it on
metrics::segment_settled(segment, tol), which extrapolates the geometric tail
from three points spread across the segment and answers whether the last frame is
within tol of the asymptote. Sample widely rather than from the end: captures
are 8-bit, and a response slow enough to outrun its window moves by less than one
code value per frame near the end, so adjacent frames decode as identical and
read as settled exactly when they are not.
So --report marks rather than pretends (Plan 0038 Phase 8). A transient cell
carries a + when segment_settled cannot certify the response arrived —
61+ means at least 61 frames, never 61. Each family’s table then names how
many of its presets marked. --json carries the same fact as rise_settled and
fall_settled booleans, so a consumer reading only the counts cannot mistake a
truncated response for a settled one.
Expect most of the shipped set to mark, and for two different reasons the
suffix does not separate. One is the window, above. The other is far more
common here and is not a defect in the probe at all: a scene whose own motion
never stops — a fragment field’s fold, a feedback trail, a particle cloud — has
no asymptote to settle to, so segment_settled correctly declines to certify
one. That is the same limitation this page already describes as “the scene’s own
motion is measured too”; the mark just moves it from a caveat you have to remember
into the cell itself. A preset whose cells are unmarked is the interesting case:
it means the number is a measurement.
Widening the --report window does not fix that table’s separation problem,
which is a different thing — measured at 96 frames the scalar-only median got
worse (0.60 → 0.92) for double the wall clock. Scene saturation is what hides
the magnitude there. Window length is what corrupts a slow response’s shape, and
the two are not the same defect.
A low cover is not a defect
cover counts pixels differing from the corner-sampled background by more than a
threshold, on any channel — a symmetric difference, so dark-on-light and
light-on-dark are measured identically. An ink-remapped look is not penalised by
construction, and a low reading is not evidence of one.
What a low cover means is that the frame is sparse, and sparse is often the
intent. reaction_coral_bloom reports 0.128, about as low as the shipped set
goes, and is healthy — it is the family’s ink-on-paper variant, a pale print whose
chaotic-branching regime genuinely covers an eighth of the frame. The number is
truthful; what it cannot tell you is “sparse on purpose” from “dead”.
So the column names suspects rather than convicting them. A low cover
alongside a dead anim and flat reactivity columns is worth investigating; a low
cover on a preset that is deliberately a thin figure on a wide ground is the
report working.
The second reading: the same columns at realistic levels
Under each family’s table is a second block (ADR-0042):
at realistic levels (bass 0.661 mid 0.575 treb 0.281 onset 0.145) — read the *gap* ... preset bass mid treb onset gates ceils occ Aurora 0.183 0.165 0.212 0.025 0 1 0 Ember 0.078 0.053 0.032 0.035 0 0 0The realistic levels changed meaning in Plan 0048. They are now fractions of each signal’s own recent peak (ADR-0049), not magnitudes, which is why they read
0.661where they used to read0.04. Any--reportnumber quoted in an older commit message, ADR Outcome or backlog entry was measured on the raw scale.
The four columns are measured exactly as the ones above, from stimuli set to
what real material produces instead of
to 1.0. The gap between the two rows is the reading, not either number
alone, and it has a direction:
| what you see | what it means |
|---|---|
| both healthy | the preset responds at levels it will actually meet |
| full scale lively, realistic ~0 | gained or gated against a magnitude music never reaches. This is the defect that hid six presets for months |
| realistic close to full scale | a compressive curve/smoothstep doing its job, or a binding already saturating at low input |
| realistic > full scale | something inverted or saturating — a parameter past its useful range at full scale, reading back down |
The last row is why the full-scale columns stayed. They also keep every number quoted in an older commit, ADR or backlog entry meaning what it said.
Two things this pair cannot see. beat is an event, not a magnitude, so it
is true in both readings — a beat-latched binding holds across the gap while an
onset-scaled one falls away, which is a distinction, not a fault. And the
band array is on its own scale: the low stimulus lights the spectrum slice
to the same level as the scalar, while real material’s per-band mean is 0.020
with peaks to 0.338, so a bin()-reading preset reads lower here than the
scalar gap suggests.
Reachability: gates the probe never drove both ways
The gates and ceils counts are not measured from pixels at all. They come
from walking each preset’s expression trees while evaluating them over 12 s
of dynamic:110 through the real analyzer, recording which way every
comparison and every select() condition went, and how close every clamp()
came to its upper bound. A frame differential structurally cannot answer this —
select(c, 6, 8) and select(c, 6, 6) diff identically, and neither names
which gate.
Four kinds of finding come out of that walk. Three are named one per line underneath the table; the ceilings are summarized:
GATE— aselect()whose condition never went both ways. One branch of the preset has never rendered. Named with its source text, so the threshold to re-gain is in front of you.COMP— a comparison (> < >= <= == !=) that only ever took one value, so it read as a constant0or1(ADR-0043). This catches two shapes aGATEline cannot. One is the bare comparison as a whole binding —reseed = "onset > 0.55", the idiomatic boolean-param form, which holds noselect()at all. The other is one half of a composite condition: inselect(min(tempo > 124, bass + treb > 0.38), 4, 1)theGATEline names the wholemin(...), and since atempogate is legitimately one-sided here (below), a reader would dismiss it — so each half is also reported on its own, and the excusable one can no longer launder the other.SAT— aclamp()whose inner value sat at its upper bound for 90 % or more of the probe (ADR-0062). The binding is a gain that has stopped being a function of the audio: it reads as the constant its ceiling is, for anything above a whisper. Named one per line with its occupancy, because unlike a decorative ceiling this is a HARD failure — see the saturation gate.- clamp ceilings — a
clamp()upper bound the value never approached. The bound is decorative and the parameter’s real range is narrower than it reads. These are not printed one per line: a single summary line per family gives the count and names the furthest three. All of them are in--json.
A comparison that is the direct condition of a select() reports once, as
the GATE line only — that line already names it and says which branch never
ran, which a COMP line cannot. So GATE and COMP never double-report the
same finding.
The gates column counts GATE + COMP together: both say a branch of the
preset’s behavior has never happened. ceils counts the ceilings, and occ
counts the saturated clamps.
ceils and occ are opposite ends of one measurement, taken on the same
traversal from the same two numbers — a clamp()’s inner value and its upper
bound. ceils asks how close the value ever came (its peak, as a fraction of
the bound) and fires when the answer is “never near”: the ceiling is decorative.
occ asks how long the value stayed there (the fraction of hops at or above
the bound) and fires when the answer is “always”: the ceiling never released. A
clamp can trip at most one of them, and the healthy state is neither — a bound
reached on peaks and released in between. occ is by far the more serious of
the two, which is why it is the one that is gated.
A flag is a suspect, not a conviction. It says this stimulus never drove
the gate both ways, which is a fact about the probe as much as about the preset.
The standing false positive is tempo: the probe runs at one BPM, so
select(tempo > 132, ...) is correctly one-sided and will flag forever. Check
those two by hand with --set tempo=90 / --set tempo=160 (see
Examples); a gate on a band is the one worth acting on.
The probe runs 12 s rather than the 4 s a --signal filmstrip synthesizes,
because the tempo tracker needs about 4 s to lock. Under a short clip tempo
reads a flat 0 and every tempo comparison flags for the wrong reason.
The GATE and COMP half of this is advisory output. It is not a CI gate,
and deliberately so — and as of Plan 0048 both of the reasons are live again.
(The SAT half is gated, for reasons that do not apply to it — see
below.)
The instrument is one of them, and that has not changed: the tempo single-BPM
false positive above accounts for 17 of the 26 flags the shipped set currently
produces, so a naive “fail if flags > 0” would fail CI permanently and a threshold
would be tuned to noise. The precondition remains a multi-BPM probe or an explicit
tempo exemption
(ADR-0043).
The library is the other, and it regressed on purpose before being put back.
Plan 0042’s re-audit measured 0 genuinely dead gates, and that held until
ADR-0049 changed what a band level means: nine bindings written against raw
levels then compared against normalized ones and never went false, so their
else branches were dead. That was the priced cost of the one-time retune
ADR-0049 chose, and Plan 0048 Phase 7 cleared it — the library is back to
zero genuinely dead gates, with the residual flags all tempo one-sidedness.
The walk used to be unable to see a gain, and now it can. A comparison is a
fork it can watch; clamp(bass * 16, 0, 0.3) is not — it has no select(), no
comparison, nothing two-valued, and it is simply an arithmetic expression that
has quietly become a constant. Plan 0048 Phase 7 measured 263 of 332 clamped
band terms pinned at their ceiling, and 14 presets with no live audio term at
all, none of it visible to any instrument this project had. The occ column and
the SAT lines are that instrument
(ADR-0062), and
unlike the rest of the reachability block they are backed by a gate.
Saturation: a HARD gate on clamp occupancy
core/tests/saturation.rs runs the same walk over the embedded set and fails
the build on any clamp() whose occupancy reaches the threshold. It is the one
part of the reachability block that is not advisory, and the reason is the one
Plan 0048 Phase 7 supplies: for the whole window between ADR-0049 landing and the
retune, every automated signal was green and nobody had cause to run a report. An
instrument that requires suspicion to fire does not address the failure that
there was nothing to be suspicious of.
The threshold is a measured constant (SATURATED_OCCUPANCY), taken from the
retuned library’s own distribution rather than reasoned to, and it therefore has
a shelf life: re-measure it whenever the library changes materially.
A clamp() that is supposed to pin — a safety rail whose job is to bind at
peak — declares itself in the preset:
[occupancy]exempt = ["fade"] # this clamp is a rail, not a gain: pinning is the designAn exemption silences the gate, not the diagnostic: the binding still shows
up as a SAT line and in the occ count, so it stays visible in review. That is
deliberate — an exemption is a place to hide, and the mitigation is that it is
explicit, in the file, and still reported.
Built from a8ce055 at version 0.115.0. This site tracks main and is not versioned per release.