Curl Flow, Signal Decay and Circuit Bloom had no parameter that changed their structure. Each was one process at one scale, statistically identical everywhere and at every moment — which is the technical statement of "it looks the same for five minutes". Every pixel moving, the image never changing. Each got the large-scale structure it was missing, and the slow axis now has something real to walk: - Curl Flow gains a CURRENT: one broad band carries the filaments and the rest runs bare. The axis is the band's strength rather than its position, and that choice was measured — moving the band preserves the frame's total energy, so a ten-second average of it is nearly the same image wherever it sits (0.0028 across a full sweep). Opening and closing it changes how much of the frame is lit at all. The current gates the final image rather than only the vein term, because the feedback trail and the flow tint each fill the frame on their own. - Signal Decay gains a DAMAGE FRONT: lock falls away on one side of a moving boundary, so the stack has a shape instead of every lane being an independent coin flip. Hiss had to be gated by it too — hiss rises as lock falls, so without that a damaged lane simply traded signal for noise and carried the same energy, and the front cancelled itself out. - Circuit Bloom now actually BLOOMS. `reach` was a fixed vignette, so the packets ran, the pads blinked and the board never changed. Its range stops at 1.6 because the frame's far corner is ~1.4 units out and travel spent past that changes nothing — the first attempt wasted most of the axis up there. The gate itself was wrong, and this is the more important half of the commit. It compared the scene at thirty seconds against the scene at two and a half minutes and divided by what it did between those points with frozen parameters. Those are two different places in the song, so the denominator was full of audio reactivity: it was really asking "does the axis move this scene MORE than the music does", which no scene should have to pass. Circuit Bloom failed at 0.92x while its mean luminance moved 5.6x across the axis — a large structural change scored as nothing. The experiment now holds time constant and varies only the axis, against a control that is the same parameters over the next window — the residual churn the ten-second average failed to cancel, which is the noise floor it has to beat. With the axis disabled the two parameter sets are identical and the ratio is 0.00 by construction, so the companion check is a real discriminator rather than a formality. It reads 0.00x on all six scenes. Moiré Grid 28.7x · Gate Corridor 14.3x · Truchet Fold 3.3x · Signal Decay 1.9x · Curl Flow 1.7x · Circuit Bloom 1.4x, against a 1.25x bar. 104/104 checks pass including the slow set. Closes SIDE-QUESTS.md §2 and the open half of EPIC-2.md §3.4. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
245 lines
13 KiB
Markdown
245 lines
13 KiB
Markdown
# Epic 2 — from texture generator to something worth watching
|
||
|
||
PLAN.md took this from nothing to a deterministic engine with a 42-scene library, a
|
||
production-design layer, and an editor. Epic 2 is the answer to a different question, asked
|
||
after watching the result rather than after building it:
|
||
|
||
> Is it varied enough per track? Interesting enough to hold five minutes? Pleasant enough to
|
||
> watch for long? Is there a unifying factor? Are we reusing the same visuals between tracks?
|
||
> Are the visuals active enough?
|
||
|
||
The honest answers were: partly, no, mixed, yes, yes, and no in both directions. This document
|
||
is what those answers turn into.
|
||
|
||
---
|
||
|
||
## 1. What was measured
|
||
|
||
Everything below is a number off the real system, not an impression. The measurements are
|
||
reproducible from the check harness and several have become gates (§4).
|
||
|
||
**Shot rhythm is a metronome.** A five-minute track at 90 BPM gets sixteen shots of 19.3,
|
||
18.7, 18.7, 18.7 … 18.7, 19.3 seconds. At 124 BPM it is 15.5/15.5/13.6 repeating. `planShots`
|
||
picks a bar count *once per section* and then divides the section evenly, so every cut inside
|
||
a section lands on the same pulse. Over five minutes that is the most fatiguing edit rhythm
|
||
available.
|
||
|
||
**The palette is frozen for the whole track.** One `look.palette`, pushed to every layer of
|
||
every section for the entire runtime. Colour is the strongest perceptual variable the system
|
||
has and it never moves.
|
||
|
||
**39% of the library does not develop.** Rendering every scene at generated parameters and
|
||
comparing frames 0.5 s / 5 s / 30 s / 2 min apart, normalised for brightness:
|
||
|
||
| behaviour | count | examples (0.5 s → 2 min) |
|
||
|---|---|---|
|
||
| churn — changes as much in half a second as in two minutes | 11 | Curl Flow 0.81 → 0.75, Moiré Grid 1.42 → 1.40, Circuit Bloom 2.86 → 2.83 |
|
||
| nearly static | 5 | Pylon Grid 0.15 at 2 min, Dust Chamber 0.39, Salt Flat 0.64 |
|
||
| genuinely evolving | ~25 | Flora 0.29 → 3.25, Silk Ribbon 0.26 → 2.68, Slow Orb 0.09 → 1.21 |
|
||
|
||
Churn is the more interesting failure. Those scenes are violently animated and read as
|
||
*static*, because motion without structure is texture: the eye adapts in about two seconds and
|
||
then there is nothing left to find. "Active enough" fails at both ends of the range.
|
||
|
||
**The most-cast scenes are among the least developing.** Across twelve tracks, four scenes
|
||
appeared in ten to twelve of them — Dust Chamber, Slow Orb, Salt Flat, Eclipse Field — and two
|
||
of those are in the nearly-static bucket. Twenty-nine of forty-two scenes appeared in none of
|
||
the twelve.
|
||
|
||
**Every track makes the same genre decisions.** `FAMILY_BY_KIND` is a module constant: an
|
||
intro is always minimal/flow/organic, a drop always geometric/glitch/structural, and intro and
|
||
outro are literally the same list. Cross-track sameness is baked into a table.
|
||
|
||
**A five-minute track shows five or six distinct images.**
|
||
|
||
**The production-design layer works and is not in scope for this epic.** Changing only the
|
||
personality moves the image 0.846; changing only the parameters 0.882; changing only the
|
||
palette 0.467. The unifying mechanism is as strong as the variation mechanism. Leave it alone.
|
||
|
||
---
|
||
|
||
## 2. The diagnosis
|
||
|
||
Two separate problems, and it matters that they are separate:
|
||
|
||
**The system has no arc longer than fifteen seconds.** It has a per-frame arc (reactivity), a
|
||
per-shot arc (cuts), and a per-section arc (drift), and then it stops. Nothing is built to pay
|
||
off at minute four. Held attention over five minutes is a property of *structure over
|
||
minutes*, and there is currently no mechanism that operates on that timescale.
|
||
|
||
**Every scene is the same shot.** A locked-off, full-frame, abstract texture. There is no
|
||
scale, no framing, no composition — no wide against close, no negative space, no subject the
|
||
eye can track across a cut. Music-video attention is held by framing changes at least as much
|
||
as by content changes, and the vocabulary for that does not exist here at all.
|
||
|
||
The first is a set of bugs in disguise. The second is a ceiling.
|
||
|
||
---
|
||
|
||
## 3. The work
|
||
|
||
Five workstreams, ordered by impact per unit of effort. Each is independently shippable and
|
||
independently valuable; nothing here is a prerequisite for anything below it except where
|
||
noted.
|
||
|
||
### 3.1 Break the cut metronome
|
||
|
||
Shot lengths must vary *within* a section. The target is a phrase shape rather than a pulse: a
|
||
long hold, two quick ones, a long hold. Cuts still land on downbeats and still respect the
|
||
floor and ceiling — this changes where the cuts are, not the rules they obey.
|
||
|
||
The mechanism is a per-section **rhythm pattern**: a small set of bar-length sequences (`[8,
|
||
8, 4, 4, 16]` and friends) chosen by seed and energy, walked in order and repeated as needed,
|
||
instead of one bar count divided evenly. A section then reads as edited rather than as
|
||
metered.
|
||
|
||
Two things to preserve, both load-bearing and both easy to break here: cuts must stay on
|
||
phrase lines, and `MIN_SHOT_SECONDS` / `MAX_SHOT_SECONDS` remain hard.
|
||
|
||
### 3.2 A per-track director
|
||
|
||
`FAMILY_BY_KIND` becomes one of several mappings, chosen per track at look-generation time.
|
||
Each mapping is a coherent point of view about what a song looks like — one that answers a
|
||
drop with geometry, one that answers it with glitch, one that stays organic throughout, one
|
||
that treats the intro as structural rather than minimal.
|
||
|
||
This is a small change that directly attacks the largest source of cross-track sameness, and
|
||
it makes the twenty-nine unused scenes reachable without touching the library or the casting
|
||
weights.
|
||
|
||
### 3.3 Let the palette move
|
||
|
||
Per-section colour movement, driven by the arc that already exists: a hue rotation, a
|
||
saturation shift, or a scheme change into a drop. The track keeps one identity — the movement
|
||
is *from* the track's palette, not a replacement for it — so this does not fight §3.5 of
|
||
PLAN.md or the personality layer.
|
||
|
||
Constraint: the contrast floor the Phase 3 palette gate enforces has to hold at every point in
|
||
the movement, not just at the endpoints.
|
||
|
||
### 3.4 Give the churn scenes a slow axis
|
||
|
||
Each of the eleven needs at least one parameter on a multi-minute ramp, so that the frame at
|
||
three minutes is not the frame at thirty seconds. This is per-scene work and it is mechanical,
|
||
but it is the only thing that fixes the specific failure of "animated and yet static".
|
||
|
||
The five nearly-static scenes get the opposite treatment, and Phase 7's movement gate already
|
||
describes what "enough" means.
|
||
|
||
**Status after the first pass: mechanism delivered, most of the scene work still open.**
|
||
|
||
The mechanism exists and is proven. `ArcDriver._slowAxisFor` walks a declared param across the
|
||
whole track, monotonically — the existing drift is a 20-70 second LFO, and an LFO returns,
|
||
which is precisely why ten cycles of it over five minutes reads as static.
|
||
|
||
Two things were learned the hard way and are worth not relearning:
|
||
|
||
*Which param you move decides everything.* The first version chose at random from everything
|
||
eligible and measured as doing **nothing whatsoever** — identical structural change with the
|
||
axis applied and with it disabled. Sweeping Moiré Grid's `width` moves its time-averaged
|
||
structure by 0.110 and its `offset` by 0.002; a random draw finds the second kind almost every
|
||
time. Hence `slowAxis: true` as a declaration rather than a heuristic.
|
||
|
||
*Single-frame distance cannot measure this.* A churning scene's consecutive frames are already
|
||
~0.6 apart, so every pair of its frames scores the same whether the structure moved or not.
|
||
The gate measures **ten-second time-averaged** frames, which cancels the churn and leaves the
|
||
structure. The window was measured: at one second, Moiré Grid's frozen-parameter control still
|
||
reads 0.024; at ten it reads 0.0095 while the axis-driven signal stays at 0.037.
|
||
|
||
Measured across ten candidate scenes (ratio of axis-driven structural change to what the scene
|
||
does on its own):
|
||
|
||
| works | Moiré Grid 3.93× · Gate Corridor 2.88× · Truchet Fold 1.40× |
|
||
|---|---|
|
||
| **no parameter helps** | Curl Flow 1.31× · Signal Decay 1.21× · Circuit Bloom 1.10× |
|
||
| **already develops; never the problem** | Firefly Drift · Vortex Drift · Kaleido Tunnel · Plasma Bloom |
|
||
|
||
The middle row is the remaining work, and it is **not** mechanical: those scenes have no
|
||
parameter that changes their structure, so they need shader changes that introduce one.
|
||
Parameter automation cannot substitute for structure a scene does not have.
|
||
|
||
**Closed by side quest 2** (SIDE-QUESTS.md §2). All three got the structure they were missing,
|
||
and the measurement was corrected in the process — the ratios above were produced by an
|
||
experiment that compared two different points in the SONG, so its denominator was full of audio
|
||
reactivity and it was really asking "does the axis move this scene more than the music does".
|
||
Circuit Bloom failed at 0.92× while its mean luminance moved 5.6× across the axis. The gate now
|
||
holds time constant and varies only the axis, against the metric's own noise floor. Current
|
||
figures: Moiré Grid 28.7× · Gate Corridor 14.3× · Truchet Fold 3.3× · Signal Decay 1.9× ·
|
||
Curl Flow 1.7× · Circuit Bloom 1.4×, against a 1.25× bar, with the axis disabled reading 0.00×.
|
||
|
||
### 3.5 A framing layer
|
||
|
||
The one that raises the ceiling rather than the floor. A shared zoom / crop / scale envelope
|
||
sitting above the scenes, so the same image can be played as a wide and as a close, and so a
|
||
cut can change the framing without changing the subject.
|
||
|
||
This is deliberately last: it is the largest change, it interacts with resolution
|
||
independence (§1 of PLAN.md) and with the feedback buffer, and the four items above will have
|
||
changed what it needs to do.
|
||
|
||
**Status after the first pass: shipped, deliberately simple.**
|
||
|
||
A shot now carries a size and a recentre, applied inside `sigCamera` in scene coordinates —
|
||
rendered close rather than magnified, which is what keeps it resolution-independent and free at
|
||
4K. `look/framing.js` owns the vocabulary: three shot sizes with measured headroom (a close-up
|
||
past ~2.2 is a blurry wide, a wide past ~0.55 is a speck), and a per-track style (`locked` /
|
||
`gentle` / `edited`) that decides how far sizes travel and how often a cut also changes the shot.
|
||
The arc driver plans one framing per cue, walking the cuts in order so the whole video gets a
|
||
consistent hand and a seek finds the same framing as playback. The gates hold it honest: cuts
|
||
actually change size, sizes stay in headroom, and two drivers over one look agree on every shot.
|
||
|
||
What this pass does not do, and the reason it is "simple": the framing is constant within a shot.
|
||
A zoom that moves during a shot is a separate device and would fight the drift LFO and the slow
|
||
axis, both of which already own continuous motion. Scenes built outside the shader contract (the
|
||
single 3D layer) are not framed yet. Both are the obvious next steps and neither is required for
|
||
a working project.
|
||
|
||
---
|
||
|
||
## 4. Validation
|
||
|
||
The rule from PLAN.md §11 stands: variety is a property of a population, not of one render, so
|
||
every gate here samples many tracks and asks about a spread.
|
||
|
||
| workstream | gate |
|
||
|---|---|
|
||
| 3.1 rhythm | Within one section, shot lengths must have real spread (no section where every shot is within a few percent of the mean). Cuts still land on downbeats; floor and ceiling still hold. |
|
||
| 3.2 director | Across a population, the same section kind must draw from more than one family set. Every scene in the library must be reachable by some director. No director may leave a kind with too few scenes to build a roster from. |
|
||
| 3.3 palette | Contrast floor holds at every sampled point of the movement, not just the endpoints. Colour actually moves across a track — measurably, not decoratively. |
|
||
| 3.4 slow axis | Every scene's two-minute frame distance must exceed its half-second distance by a real margin. This is the churn number from §1, promoted to a gate. |
|
||
| 3.5 framing | Resolution independence survives (the existing dual-resolution diff). Framing changes are visible. Determinism holds. |
|
||
|
||
Two failure modes are worth naming in advance, because both have already happened once in this
|
||
project's history:
|
||
|
||
- **A gate that measures the wrong thing.** The Phase 3 look-space check was propped up by
|
||
grain until grain was removed. Any new gate here should be sanity-checked by deliberately
|
||
breaking the thing it is supposed to catch.
|
||
- **A change that a gate rewards and a viewer does not.** Cut variety that is *random* rather
|
||
than *phrased* would pass 3.1's gate and look worse. The gates are necessary and not
|
||
sufficient; the end-to-end watch stays the real test.
|
||
|
||
---
|
||
|
||
## 5. Sequencing
|
||
|
||
1. **3.1 rhythm** — biggest felt change, smallest diff, no dependencies.
|
||
2. **3.2 director** — small, orthogonal, attacks cross-track sameness directly.
|
||
3. **3.3 palette** — medium, touches the arc driver.
|
||
4. **3.4 slow axis** — large but mechanical, eleven-plus scenes, one at a time.
|
||
5. **3.5 framing** — largest, and best attempted once the other four have landed.
|
||
|
||
Each ships as its own commit with its gate green.
|
||
|
||
---
|
||
|
||
## 6. What this epic does not do
|
||
|
||
- **It does not touch the personality layer.** Measured, it works.
|
||
- **It does not grow the library.** Twenty-nine scenes are currently unreachable; making them
|
||
reachable (3.2) is worth more than adding a forty-third.
|
||
- **It does not add audio analysis.** Every mechanism here is driven by features that already
|
||
exist in the FeatureTrack.
|
||
- **It does not relax determinism.** Every rule in PLAN.md §1 still applies, and the seek /
|
||
export / repeat gates are non-negotiable.
|