music-video-gen/flow-state/_exporttest.html
Dejvino 17a583a87f Side quest 1: give four scenes art direction other than grain
Classic Wave, Silk Ribbon, Kaleido Tunnel and Slow Orb all declared the
`style` trait and honoured it with `col += sigGrain(uv)` and nothing else. A
declared trait is a contract — the disqualification rule in Personality.js is
the only thing keeping off-design scenes out of a track — so honouring it
with dirt meant these four could not take the `texture: 0` opt-out the grain
work introduced. Phase 9 measured their style response at exactly 0. They sat
at texture: 0.35 as a stopgap, which kept speckle on the library's cleanest
scenes purely to keep a gate green.

Each already had the knob; it just was not wired to the track:

- Classic Wave contrasts its wave through a bare smoothstep(0.2, 0.8). The
  transition width now comes from u_sigSoft and u_sigLine, centred on 0.5 so
  changing the hand does not change the exposure. Crest concentration is
  driven separately by u_sigLine, because a track is free to sample the
  scene's own u_softness at zero and the art direction must still show.
- Silk Ribbon's strand width is u_sigLine and its falloff exponent u_sigSoft:
  a sharp track gets a filament with a defined edge, a soft one a haze.
- Kaleido Tunnel drew its grid against a bare 0.42 — a line weight with no
  name. It is now a weight and a feather, both from the track.
- Slow Orb's body edge multiplies the scene's softness by the video's, and
  gains a sigEdge rim so a sharp-handed track gets a defined limb.

All four are now texture: 0. Style response measured against a maximally
soft-handed versus maximally sharp-handed personality: Classic Wave 111,
Silk Ribbon 216, Kaleido Tunnel 206, Slow Orb 102, out of 255 — previously 0.

104/104 checks pass including the slow set. See SIDE-QUESTS.md §1.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-06 19:35:50 +02:00

44 lines
1.6 KiB
HTML

<!DOCTYPE html>
<html>
<head><title>export test</title></head>
<body>
<canvas id="c" width="1" height="1"></canvas>
<div id="out">running…</div>
<script type="module">
import { Show } from '/src/Show.js';
import { FeatureTrack } from '/src/audio/FeatureTrack.js';
import { synthesizeSectioned } from '/src/audio/synth.js';
import { generateLook } from '/src/look/LookGenerator.js';
import { Exporter, PRESETS } from '/src/export/Exporter.js';
const out = document.getElementById('out');
const log = (m) => { out.textContent += m + '\n'; };
const fps = 60;
const show = new Show({ canvas: document.getElementById('c'), width: 640, height: 360, fps });
const buffer = synthesizeSectioned({ bpm: 120, duration: 8, changeAt: 4 });
const track = FeatureTrack.fromAudioBuffer(buffer, { fps });
show.track = track;
show.engine.timeline.setDuration(track.duration);
show.look = generateLook(track, { seed: 7 });
show.audioBuffer = buffer;show.arc = new (await import('/src/look/ArcDriver.js')).ArcDriver(show.look, track);
log(`frameCount ${show.frameCount} · fps ${fps} · duration ${track.duration.toFixed(2)}s`);
// Override size so we don't pay 720p cost in swiftshader.
const ex = new Exporter(show);
try {
const start = performance.now();
const blob = await ex.export({ preset: '720p', frameRange: [0, show.frameCount], onProgress: (p) => {} });
const ms = performance.now() - start;
log(`export done in ${(ms/1000).toFixed(1)}s, ${(blob.size/1e6).toFixed(2)} MB`);
window.__EXPORT_BLOB__ = blob;
} catch (e) {
log('EXPORT THREW: ' + e.stack);
}
log('DONE');
window.__EXPORT_DONE__ = true;
</script>
</body>
</html>