Every shot stands on something, and the frame has two ends

A section used to be one scene, and two thirds of the library is composable —
sparse by design, elements ON something. Cast as backgrounds anyway, they left
9 of 40 sampled frames under 20% painted, the darkest at 0.3%: a minute and a
half of a few bright things on black, invisible to every gate because every
gate on the stack was a limit rather than a floor.

Every section now stands on a GROUND: a canvas that fills the frame, cast per
section kind so a shot cut changes the shot and not the world. When the shot
fills the frame itself it IS the ground — two canvases stacked is two pictures
fighting. Above that, a coverage BUDGET: director appetite times the section's
energy times where the story is, capped at two frames' worth of material.

The measured facts move into the repo. scenes/metadata.json is generated from
the gallery — coverage as a shot, coverage as a bed, variety, the structural
profile — tracked in git, stamped with a fingerprint of the scenes and the
metric definitions, and refreshed from gallery.html. `surface` is derived from
it rather than declared; nine scenes claimed `canvas` while painting under a
third of the frame, and declaring it is now a lint error. The generator weights
every layering choice by measured structural distance, because family labels
and the render disagree: two `geometric` scenes can be 0.31 apart and a `flow`
and an `organic` scene 0.04.

The gallery's 0.1 red line is gone. It was right when a section was one scene
and wrong now — nineteen scenes were failing a bar for being consistent, which
is a virtue in an ingredient.

Chasing the numbers turned up four real faults:

  * A scene that reads prev() cannot be a ground. It returns the whole
    composited frame including the layers above it, so a datamosh under a shot
    is eating it: the render stopped reproducing from a seek and two WebGL
    contexts diverged by 91/255 against a tolerance of 4.
  * Screen was the wrong operator for a shot over a bed. It lightens, so a
    median quarter of every frame clipped to paper and whole sections rendered
    100% white. Replaced by a lumakey — the shot's brightness is its alpha.
  * Feedback was an accumulator: a still image settled at 2.3x its own
    brightness. Fine over black, fatal over a filled ground. Normalised at 0.6,
    plus a highlight shoulder so the top rolls off instead of clipping.
  * useTrack never prewarmed, so a fresh Show's first frame differed from every
    later render of it — the export-breaking hazard Compositor.prime documents.

Blazing is a decision now, not a side effect: directors declare an appetite for
it, a section must be loud and late in the story to earn one, and quiet kinds
never do. The ceiling gate matches that — a hard cap per section, and no more
than a fifth of them hot at all.

Rendered across twelve videos, middle of every section:

    painted        51% mean, darkest 0.3%  ->  87% mean, darkest 43%
    clipped white  24% median, worst 100%  ->   1% mean, worst 30%
    separation     0.10                    ->  0.44

Seven scenes can ground a section — five geometric, two organic — so every
quiet section of every video stands on one of two beds. That is the library's
largest hole and it is scene work: there is no minimal or flow canvas that
fills half the frame without reading prev().

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Dejvino
2026-08-20 09:02:01 +02:00
co-authored by Claude Opus 5
parent 7d151be6e1
commit 89e05459c0
68 changed files with 9896 additions and 293 deletions
+188
View File
@@ -394,3 +394,191 @@ do it once the numbers were understood, and they now are.
written — direction clears the arcless reference by 0.26, which is hard to explain without
a story — but it remains the only thing that would attribute the floor rise (0.1697 →
0.2130) to this work rather than to everything else that changed alongside it.
---
## 14. Composition — the frame has a floor and a ceiling
Not part of the original plan. It comes out of the same place §12 did: the layer stack was
being asked to carry the video, and half of what it was stacking was nothing.
**The floor.** Two thirds of the library is `composable` — sparse by design, elements ON
something. `canBackground` let almost all of it anchor a section anyway, so a section's
bottom layer was regularly a scene painting 2% of the frame with black behind it. Measured
across six videos, sampling the middle of every section: **9 of 40 frames were under 20%
painted, the darkest at 0.3%**. That is a minute and a half of a few bright things on black,
and no gate could see it, because every gate on the stack was a limit rather than a floor.
Every section now stands on a GROUND: a canvas painting at least half the frame, cast per
section kind so a section's shot cuts change the shot and not the world. When the shot is
itself a full canvas it *is* the ground — two canvases stacked is two pictures fighting. The
same measurement now reads **mean 92% painted, darkest 63%, nothing under 30%**, and phase 12
gates it in the image, not in the spec.
**The ceiling.** Layering had a rate but no budget, so how full a frame got was a die roll
that knew nothing about the song. A stack now has a coverage budget — director appetite
(`crowd`, 1.05 brutalist to 1.7 corrupt), times the section's energy and density, times where
the story is — capped at **200%**, two frames' worth of material. The ground and the shot are
paid for first; the budget governs what may be stacked on top, and the odds of an overlay
fall off as the headroom does rather than only at the wall.
**What it cost, and what it bought.** Same instrument, same six seeds:
```
baseline with grounds
separation 0.10 0.44
direction 0.06 0.21 (arcless reference 0.02 → 0.08)
library 80% 97% of what the library can express
scale 42% 56%
motion 68% 96%
texture 74% 90%
```
The ceiling *fell* (0.2334 → 0.2031) while observed rose, so part of that separation gain is
the reference arm coming down: videos that share a ground vocabulary are less unalike even
when they share no shots. The honest reading is that the floor rose 0.009, direction tripled,
and the ceiling moved toward the floor — the numbers are up, and not all of the rise is
signal.
**A scene that reads the previous frame cannot be a ground.** `prev()` returns the whole
composited frame, *including whatever is layered on top of this scene*, so a datamosh under a
shot is not grounding it, it is eating it. Measured the moment one became a bed: the render
stopped reproducing from a seek, and two WebGL contexts diverged by 91/255 against a ceiling
of 4 — phase 4 caught both. `readsHistory` is derived from the shader source rather than
declared, and costs the ground pool six of twenty scenes, five of them glitch.
`scenes/coverage.js` is the measured coverage of every scene, pasted back out of the phase 12
gate, which re-measures and fails on drift over a tenth of a frame. A new scene has no entry
and therefore cannot ground anything until someone renders it.
### 14.1 The measurements moved into the repo
Everything in §14 depends on a number — how much of the frame a scene paints — and the
first version of it was a table pasted into a source file by hand. That is the same mistake
`surface` already was: a measured fact written down by a person, correct on the day and
wrong after the next shader edit.
**`src/scenes/metadata.json`** is now generated, tracked in git, and holds every measured
fact about every visualizer: coverage as a shot, coverage as a bed, the variety score, the
per-block scores, and the mean structural profile. Nothing in it is typed by a human.
`surface` is derived from it (canvas at 50%, composable below) and declaring `surface:` in a
scene file is now a lint error.
It is refreshed from **gallery.html → refresh metadata**, which re-renders the library and
writes the file through a dev-only endpoint in `vite.config.js`. The file carries a
fingerprint of everything that can move a number in it — the scenes, the shader contract,
the identities and palettes they are handed, the descriptor definitions — and phase 12 fails
when that stops matching. A stale metadata file does not produce a stale report, it produces
wrong videos, so it has to be detectable.
**Measuring at the wrong bias measures nothing.** Coverage is mostly a function of a scene's
parameters, and the first measurement took one number per scene at the busiest section of
six songs. Metaballs measures 69% there and painted **6%** as an intro's ground. Three
things came out of chasing that, in order of how much each was worth:
* A bed is drawn with a **moderate hand**. `extremity` pushes parameters toward the ends of
their ranges, and half of what decides coverage is a parameter with no `bias` key at all —
Metaballs' `threshold` — so extremity is the only thing moving it and one end is an empty
frame. Grounds sample at `extremity * 0.2`. The extremes belong to the shot.
* Energy and density are **floors** for a ground, not reductions. Calming the bed by
lowering energy is how to empty it: scenes bias their fill against `energy` as often as
against `density`.
* Coverage is measured **twice**, once as a shot and once as a bed, at the same bias and
temperament the generator will use. `GROUND_BIAS` and `groundTemperamentFrom` live in
`scenes/surface.js` and are imported by both the generator and the measuring pass, so the
two cannot drift apart.
Rendered result across six videos, sampling the middle of every section: **mean 87% painted,
darkest 30%**, against 51%/0.3% before any of this.
**Eleven scenes can ground a section** — five organic, five geometric, one structural. There
is no `minimal`, `flow` or `glitch` canvas in the library that paints half the frame without
reading `prev()`, so every quiet section in every video stands on one of five organic beds.
That is the largest remaining hole and it is library work, not generator work: the fix is
writing dense, self-contained canvases in the three thin families.
### 14.2 The red line is gone
`MIN_VARIETY` — 0.1, drawn across the gallery, "below this a scene is the same picture
wherever it appears" — was right when a section was one scene and is wrong now. A section is
a ground, a shot on it and sometimes a pass over that, so what a viewer sees is a
combination, and a scene that is reliably itself is a good ingredient in one. Nineteen
scenes were failing a bar for being consistent.
What replaced it is a level up, and it is the reason the profiles are in the metadata: the
generator weights every layering choice by **structural distance** — how unalike two scenes
measured, on the same descriptor the variety harness compares videos with. Family labels and
the render disagree often enough to matter; two `geometric` scenes can be 0.31 apart and a
`flow` and an `organic` scene 0.04, and stacking the second pair is one picture at double
density. Measured over 436 stacked pairs: **mean distance 0.268, zero near-twins**.
### 14.3 The other end of the frame
The floor had a ceiling missing. Measured across twelve videos, sampling the middle of every
section: **a median 24% of every frame was clipped to pure white, and whole sections
rendered at 100%.** The videos were washed out, and no gate could see it because every guard
on the frame asked whether there was enough in it.
Three causes, all introduced or exposed by §14, in the order they were found:
**The shot was screened over its ground.** Screen is a lightening operator — correct for a
few bright elements over a bed, and over a filled canvas it drives everything toward white.
Replaced with a **lumakey** blend (`passes.js`): the shot's own brightness is its alpha, so
it replaces the ground where it paints and leaves it where it does not, keeping its own
colour instead of adding it to the bed's. Median clipping 24% → 11%.
**The feedback loop was an accumulator.** `cur + hist * decay * amount` settles a still
image at `1/(1 - decay*amount)` times its drawn brightness — **2.3x** at the settings the
generator hands out. Survivable when a frame was a few bright things on black; fatal the
moment every section stood on a filled ground. Turning feedback off took a blown frame from
100% to 34% mean luminance, which is the whole diagnosis in one number. It now divides by
the same gain, at 0.6 rather than 1.0: full normalisation took the lift out along with the
blowout — the median frame fell from 95% painted to 71% and fourteen sections dropped
through the black-frame floor.
**Nothing rolled off.** The grade clipped. A **highlight shoulder** now compresses everything
above 0.75 toward but never to 1. Clipped white is not brightness, it is missing information:
every difference inside it has been deleted.
Rendered, after all three — 74 sections, twelve videos:
```
before after
painted 51% mean 88% mean, darkest 43% (was darkest 0.3%)
clipped white 24% median 0% median, worst 21% (was worst 100%)
luminance 77% median 42% median, worst 80%
```
The gate is now two-ended: `composition · a rendered section is neither black nor blown out`.
**A ground has to survive the song's identity.** Chasing the dark end turned up the same
class of error as §14.1: a ground measured at 61% painted **2%** in one particular video,
because that song's ink treatment is `hollow` — outlines, no fill. Two changes came out of
it. The world is now drawn solid and the song's hand is kept for the subject
(`groundPersonalityFrom`), and eligibility takes the **worst** identity into account as well
as the mean: a ground fills the frame on average and never vanishes. Holding the worst case
to the full 50% would leave *four* castable beds in the library, which is a worse video than
an occasionally dim intro.
**Seven scenes can ground a section** — five geometric, two organic. Every quiet section of
every video stands on one of two beds. Restated from §14.1 because it got worse, not better:
this is the library's largest hole.
### 14.4 Blazing on purpose
Screening the shot over its ground and letting the two brightnesses sum is a real effect — a
drop that goes to paper for eight bars reads as the song peaking. The mistake in §14.3 was
not the effect, it was that the effect was the **default**: every section did it, so a median
quarter of every frame in every video was clipped, and nothing about that said "peak".
It is a decision now. Each director declares a `blaze` appetite — brutalist 0.05 (mass does
not glow), corrupt 0.5 (overload is the subject) — and a section still has to earn one: loud,
and late in the story. Quiet kinds never blaze, because a breakdown that goes white is not a
choice, it is a bug with a rationale. The default composite is the lumakey, which keeps the
shot's own colour.
The ceiling gate matches the intent rather than banning brightness. Per section, only a hard
cap — past about half the frame at full white there is no picture left to read. Across the
population, **no more than a fifth of sections may be hot at all**, which is the number that
actually distinguishes a director choosing to peak from a pipeline with no headroom.
Measured now: **1 of 74 sections blazing**, worst 30% clipped.