b69eaa799d
2 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
14d1204e82 |
Make grain a treatment, tempo a governor, and params commit harder
Grain was in every video. It was added twice unconditionally — every scene called sigGrain, and the grade added its own on top — so the only thing that varied between two tracks was how much of it there was. That makes grain the renderer's fingerprint rather than a decision about one video. It is now described rather than dialled (look/grain.js): a mode (off / constant / swell / sections / transient), a cell size in pixels, a refresh rate in frames, a mask (uniform, shadows, highlights, edges, bands) and a chroma amount. Roughly 45% of tracks get none at all. The non-constant modes carry a per-frame envelope computed in Show._postAt from frame and features only, so preview and export still agree. Scene-side grain is gated the same way, and a module can decline it outright with `texture: 0` — crisp line work should stay crisp. The post tab grew a real grain block so any of it can be forced per track. Slow songs got fast scenes. `motion` bias was mostly section energy with tempo as a small correction, so a 70bpm track's drop asked for nearly as much speed as a 150bpm one. Motion is now tempo-dominated, and every `rate: true` param is additionally scaled by a per-track rateScale — measured, 84bpm now samples its rate params at 0.276 of range against 148bpm's 0.571. Parameter sampling also commits harder: extremity starts at 0.45 rather than 0.25 and shapes the draw more aggressively. This was first pushed to 0.82 and backed off to 0.72, because the gates caught the overshoot — seeds began collapsing onto the same range ends and a sparse scene sampled at its low end rendered effectively black. Fallout worth recording: turning the default grade grain off exposed two scenes that were never really animating. Dust Chamber and Eclipse Field passed the Phase 7 movement gate only because per-pixel noise was moving underneath them; both now breathe on their own fixed clock, and Dust Chamber needed a brightness floor as well. Four scenes (Classic Wave, Silk Ribbon, Kaleido Tunnel, Slow Orb) express the style trait ONLY through grain and so cannot opt out yet; they hold a reduced share at 0.35 pending real edge and softness response. Phase 3's look-space check now measures its closest pair relative to image brightness, the same correction Phase 10 already documents for sparse scenes: the absolute number was being propped up by grain rather than by look-space width. Five new Phase 10 checks cover grain distribution, treatment variety, envelope range, the texture opt-out, and tempo. 93/93 pass. Two transport bugs, both stale state surfacing in the UI: - Loading a track replaces audio.src, which stops playback silently, so state.playing stayed true and the play button stayed on pause — the first click after a track change only flipped the flag back. Added stopPlayback(). - The controls row wrapped mid-song because two readouts that change length while playing were sized by their content: the clock crossing ten minutes and the section label whenever a scene name is long or a crossfade appears. The clock is now fixed-width with tabular figures and the section label is the row's only flexible item, laying out at zero width and ellipsising into whatever space is left. The two spacers competed with it for that space and are gone; its own text-align does their job. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
2806ef1386 |
Phase 10: variety, twelve scenes, and tooling to write the next one
Watching several finished tracks side by side turned up the problem neither Phase 8 (too few cuts) nor Phase 9 (no through-line) addressed: the same scene cast in two different videos looked like the same footage twice. Section bias is nearly identical between two tracks' drops, so both sampled their parameters around the same centre, and the library's own averageness did the rest. Three answers, none of them a new scene: Temperament — a per-track hand on every parameter dial: intensity, pace, detail, and an extremity that decides how far toward the ends of a range the track is willing to sample. Bias comes from the section and is shared between tracks; temperament comes from the track and is not. Overlays — sometimes a second full scene composited over the shot, from a different family, in a blend that preserves what is underneath and never above 0.6 opacity. Not always: a stack that always doubled up would read as permanently cluttered rather than as occasionally layered. A wider palette — hue now derives from SPECTRAL TILT, the log ratio of treble to body. The centroid is a number most masters sit in the middle of, and the plain body/(body+treble) fraction is worse: low frequencies carry most of the energy in all music, so it read 0.98-1.00 for everything and four different battery tracks came out within 0.02 of each other. The ratio is multiplicative, so its logarithm is what spreads — the same four measure -9.3, -5.0, -4.1, -3.8. Also both ways round the wheel (violet, magenta and pink were unreachable by construction), four new schemes, and seeded chroma profile and lightness curve. Closest battery pair went from 0.005 to 0.113. Twelve scenes take the library to 36, six per family: Aurora Veil, Vortex Drift, Tide Rings, Ink Bleed, Dust Chamber, Salt Flat, Cargo Belt, Gate Corridor, Circuit Bloom, Truchet Fold, Signal Decay, Storm Rift. Weighted toward the 'space' and 'shape' traits, which were thinnest and so the signatures most likely to run a track out of cast — the Phase 9 casting rule means the pool a track draws from is smaller than the library. Also fixes a real one in shots.js: heavy LRU weighting was not enough to make a section reach its whole roster, and a five-shot section still came out 0,2,0,2,0 about a fifth of the time. An unseen companion now wins outright; which one is still free, so only the coverage is guaranteed. Block Mosh declared the camera trait, assigned sigCamera(p) to a p it then never read, and passed the lint's evidence grep. The Phase 9 render gate measured its response to the camera at exactly zero. --- tooling --- Adding a scene was mostly boilerplate and round-trips, which is expensive in both senses. The irreducible cost is the shader body; everything around it is now mechanical: npm run new:scene -- "Name" --family=... --traits=... writes the module, registers it, and leaves a skeleton that already passes every gate, with name-derived constants so two skeletons are not twins. The lint grew the rules that previously needed a GPU to catch: the dead camera above, prev() with no base image, and large loops with no early break (with a `// lint: fixed-cost` opt-out for a genuinely fixed-cost sampling loop). checks.html?scene=Name runs the per-scene acceptance battery for one scene — ten lines and a verdict instead of rendering the whole library to find out whether one shader is alive. The same procedure is a repo skill under .claude/skills/build-visualizer/. --- checks changed, with the measurements --- P5 determinism compared two WebGL CONTEXTS, which is not what it is for. Measured: one context is bit-exact over 40 frames with feedback at 0.6; two contexts disagree by up to 2/255 whether feedback is on or off. It now asserts generation is byte-identical (hard) and rendering within 2/255, since feedback compounds single-level variance. P10's cross-track comparison measures distance RELATIVE to how much image there is. Most scenes are mostly dark, so two genuinely different renders — 25 bars against 53 — scored under 0.02 absolute purely because the black background agrees with itself. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |