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
+38 -8
View File
@@ -28,16 +28,23 @@ checks.html?scene=X the per-scene gate, including `consumes:` lines
```
The gallery score is the mean structural distance between a scene's own six
frames, on the same descriptor the variety harness uses. **Minimum bar: 0.1**
(`MIN_VARIETY` in `checks/gallery.js`), drawn as a red line. Below it a scene is
the same picture wherever it appears, and because it is cast into many songs
that sameness leaks between them — the viewer recognises the shot rather than
the song.
frames, on the same descriptor the variety harness uses. High means the song
changes this scene a lot; low means it looks like itself wherever it is cast.
Most of the library does not clear it. That is deliberate: a bar set where the
work already is measures nothing.
**There is no bar.** There used to be one — 0.1, drawn across the gallery as a
red line — and it was removed when composition arrived. A section is no longer
one scene: it is a ground, a shot standing on it, and sometimes a pass over
that, so what a viewer sees is a COMBINATION. A scene that is reliably itself is
a perfectly good ingredient in one, and the old line failed it for being
consistent.
Some reference points, measured:
What replaced it is a level up. The interesting quantity is how unalike the
scenes in one stack are, and the generator now weights its choices by exactly
that — see `structuralDistance` in `scenes/surface.js`. Your scene does not have
to be varied on its own. It has to be unlike the things it will be stacked with,
and that is measured for you.
Some reference points, measured across six songs:
```
0.324 Droste Feedback varies a lot
@@ -47,6 +54,29 @@ Some reference points, measured:
0.047 Moiré Grid after a subject; 0.031 before
```
### The measured metadata
`src/scenes/metadata.json` is generated, tracked in git, and holds every
measured fact about every scene: coverage, variety, per-block scores and the
structural profile the distance above is computed from. Nothing in it is
hand-written, and two decisions read it:
* **`surface`** — canvas at 50% coverage or more, composable below. It used to
be declared per scene and it drifted; nine scenes claimed `canvas` while
painting under a third of the frame. Declaring `surface:` in a scene file is
now an error.
* **which scenes may be a GROUND** — a canvas that does not call `prev()`, fills the frame
when sampled as a bed, and does not vanish under any of the six identities. Coverage is
measured twice, once as a shot and once as a bed, because a scene paints wildly different
amounts at different parameters: Metaballs is 69% as a drop's shot and 6% as an intro's
ground.
Refresh it from **gallery.html → refresh metadata** after changing a shader or a
metric. The file carries a fingerprint of the scenes, the shader contract, the
identities and palettes, and the descriptor definitions; when that stops
matching, phase 12 fails and tells you to re-measure. The write goes through a
dev-only endpoint in `vite.config.js`, so it only works under `npm run dev`.
---
## What the descriptor can and cannot see