A reproducible migration, and the first eighteen scenes through it
The library is sixty-one scenes, which is too many to convert from memory or from taste, so the migration is a queue with a gate rather than a judgement call per file. Three pieces. A classifier reads each shader and assigns a tier from evidence in the source — drawn, figure, field, treatment — so two passes over the library reach the same answer and the work has an order. MIGRATION.md is the recipe per tier, written to be followed mechanically. And a gate makes the result verifiable: `consumes` is now a schema field, the lint enforces it in both directions, and the per-scene battery renders each scene under two deliberately distant identities and requires the picture to change. That gate is the part that matters. Without it `consumes` is a comment, and the whole inversion becomes unverifiable at exactly the point where it stops being checkable by eye. With it, a scene that declares the cast and ignores it fails. Eighteen scenes migrated. Four by hand at the drawn tier — Firefly Drift, Metaballs, Floating Geometry, Prism Bloom — and ten at the field tier by script, which is one declaration and one wrapped return. All eighteen pass. The field tier is honestly marginal and the gate says so: every one of the ten moves by 37 to 39 of 255, against 173 to 255 for the drawn tier, and the uniformity across ten unrelated scenes is the tell. That is one global posterisation applying, not ten scenes expressing anything. Cheap, real, shallow. The decomposition moved from identity being worth 54% of the container to 158%, but the stage set changed underneath the measurement and part of that is Metaballs expressing a cast better than Constellation did. What survives the caveat is the useful finding: a migrated library scene carries the identity better than a stage written from scratch to carry it. The four bespoke stages were the wrong shape of effort. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -10,6 +10,7 @@ export const fireflyDrift = {
|
||||
family: 'flow',
|
||||
kind: 'fragment',
|
||||
// Personality: see look/Personality.js.
|
||||
consumes: ['cast', 'ink', 'staging'],
|
||||
traits: ['camera', 'style'],
|
||||
|
||||
params: {
|
||||
@@ -38,25 +39,29 @@ vec4 scene(vec2 uv, vec2 p) {
|
||||
if (float(i) >= u_count) break;
|
||||
float fi = float(i);
|
||||
|
||||
// Seed the mote to a stable place in the frame (deterministic per i).
|
||||
vec2 grid = vec2(hash12(vec2(fi, 1.7)), hash12(vec2(fi, 9.1)));
|
||||
vec2 base = (grid - 0.5) * 2.0 * u_spread * vec2(1.0, 0.7);
|
||||
// The mote's home is the song's lattice; the drift is this scene's own.
|
||||
vec3 node = stageNode(fi, u_count);
|
||||
vec2 base = node.xy * u_spread * vec2(1.0, 0.7);
|
||||
|
||||
// Advect along the curl field plus a bounded wander term on top.
|
||||
vec2 flow = curl(grid * 3.0 + vec2(0.0, t * 0.15), t * 0.5);
|
||||
vec2 flow = curl(node.xy * 3.0 + vec2(0.0, t * 0.15), t * 0.5);
|
||||
vec2 pos = base + flow * 1.3
|
||||
+ vec2(sin(t * (0.4 + fract(fi * 0.13))),
|
||||
cos(t * (0.5 + fract(fi * 0.29)))) * u_jitter;
|
||||
|
||||
// A mote is one of the chorus, drawn small — the glow is the scene's
|
||||
// contribution, the form is the song's.
|
||||
float size = 0.045 * node.z;
|
||||
float sd = castChorus((p - pos) / max(size, 1e-3)) * size;
|
||||
float d = length(p - pos);
|
||||
|
||||
vec3 hc = pal(int(mod(fi, 4.0)));
|
||||
col = mix(col, hc, inkMask(sd, uv));
|
||||
col += hc * (exp(-d * d / (0.015 + u_glow * 0.04)) * (1.0 + u_glow * 0.6));
|
||||
col += hc * exp(-d * d * 60.0);
|
||||
}
|
||||
|
||||
col += sigGrain(uv);
|
||||
return vec4(col, 1.0);
|
||||
return vec4(inkValue(col), 1.0);
|
||||
}
|
||||
`,
|
||||
};
|
||||
Reference in New Issue
Block a user