Collects every manual testing entry point into one library (src/debug/battery.js) rendered from the live registries: all 17 bank songs with app/+venue/strip links, all 70 scenes grouped by family with gate/wide links, all 3 venues, 9 curated param and scenario combos, and the graduated lab pages with their headless runners. debug.html renders it; new songs/scenes/venues join free.
379 lines
17 KiB
HTML
379 lines
17 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<title>flow-state · debug</title>
|
||
<style>
|
||
:root { color-scheme: dark; }
|
||
body {
|
||
margin: 0; padding: 24px 24px 80px;
|
||
background: #0b0d12; color: #d6dae3;
|
||
font: 13px/1.6 ui-monospace, SFMono-Regular, Menlo, monospace;
|
||
}
|
||
h1 { font-size: 15px; letter-spacing: .12em; text-transform: uppercase; color: #7d8698; margin: 0 0 6px; }
|
||
h2 { font-size: 12px; letter-spacing: .1em; text-transform: uppercase; color: #6b7280; margin: 26px 0 10px; font-weight: 600; }
|
||
p.lede { color: #8b94a7; max-width: 80ch; margin: 0 0 8px; }
|
||
a { color: #7dd3fc; text-decoration: none; }
|
||
a:hover { text-decoration: underline; }
|
||
.grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(330px, 1fr)); gap: 10px; }
|
||
.card {
|
||
background: #11141b; border: 1px solid #1c2030; border-radius: 4px;
|
||
padding: 12px 14px; border-left: 3px solid #2a3040;
|
||
}
|
||
.card.slow { border-left-color: #eab308; }
|
||
.card.fast { border-left-color: #22c55e; }
|
||
.card.cli { border-left-color: #6366f1; }
|
||
.card h3 { margin: 0 0 4px; font-size: 13px; }
|
||
.card p { margin: 0 0 8px; color: #8b94a7; font-size: 12px; }
|
||
.card .meta { color: #565f70; font-size: 11px; }
|
||
code, pre {
|
||
background: #080a0f; border: 1px solid #1a1e28; border-radius: 3px;
|
||
padding: 1px 5px; color: #b8c0d0; font-size: 12px;
|
||
}
|
||
pre { padding: 8px 10px; overflow-x: auto; margin: 6px 0 0; }
|
||
.note { color: #6b7280; font-size: 12px; max-width: 80ch; }
|
||
.warn { color: #eab308; }
|
||
.chips { display: flex; flex-wrap: wrap; gap: 6px; margin: 8px 0 2px; }
|
||
.chips a {
|
||
background: #080a0f; border: 1px solid #1a1e28; border-radius: 3px;
|
||
padding: 1px 7px; font-size: 12px; white-space: nowrap;
|
||
}
|
||
.chips a:hover { border-color: #7dd3fc; text-decoration: none; }
|
||
.chips .fam { width: 100%; color: #565f70; font-size: 11px; letter-spacing: .08em;
|
||
text-transform: uppercase; margin-top: 6px; }
|
||
.chips .fam:first-child { margin-top: 0; }
|
||
.links { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
|
||
.links a {
|
||
background: #080a0f; border: 1px solid #1a1e28; border-radius: 3px;
|
||
padding: 1px 7px; font-size: 12px;
|
||
}
|
||
.links a:hover { border-color: #7dd3fc; text-decoration: none; }
|
||
.links .lbl { color: #565f70; font-size: 12px; align-self: center; }
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<h1>flow-state · debug</h1>
|
||
<p class="lede">
|
||
Everything here answers a question the app itself cannot. The app shows you
|
||
one video; these show you whether the generator is doing its job across
|
||
many.
|
||
</p>
|
||
|
||
<h2>Vis testing battery</h2>
|
||
<p class="lede">
|
||
The testing library: every bank song, every scene, every venue, the
|
||
param/scenario combos worth re-running, and the lab pages. Rendered
|
||
from the live registries (<code>src/debug/battery.js</code>), so a new
|
||
song, scene or venue joins the battery for free.
|
||
</p>
|
||
<div class="grid">
|
||
<div class="card fast">
|
||
<h3>songs — the bank, one click each</h3>
|
||
<p>Each song holds a corner of the feature space (<code>covers</code>).
|
||
App loads the song; +venue adds the room; strip is one frame per
|
||
30 s across the whole song.</p>
|
||
<div id="battery-songs" class="chips"><span class="note">loading…</span></div>
|
||
</div>
|
||
<div class="card fast">
|
||
<h3>venues — every room</h3>
|
||
<p>Pinned gallery first (fastest way to review a venue change), then
|
||
the app and the filmstrip with the room on.</p>
|
||
<div id="battery-venues"><span class="note">loading…</span></div>
|
||
</div>
|
||
</div>
|
||
|
||
<h2>Scenes — every visualizer</h2>
|
||
<p class="note">
|
||
Gate is the per-scene acceptance battery (renders, animates,
|
||
deterministic, distinct, sweep, flash, traits); wide is the scene on
|
||
six songs × six seeds.
|
||
</p>
|
||
<div class="grid">
|
||
<div class="card fast">
|
||
<h3>by family</h3>
|
||
<div id="battery-scenes" class="chips"><span class="note">loading…</span></div>
|
||
</div>
|
||
</div>
|
||
|
||
<h2>Scenarios — params and combos</h2>
|
||
<div class="grid" id="battery-scenarios"></div>
|
||
|
||
<h2>Lab pages + headless runners</h2>
|
||
<div class="grid" id="battery-labs"></div>
|
||
|
||
<h2>Looking at output</h2>
|
||
<div class="grid">
|
||
<div class="card fast">
|
||
<h3><a href="/gallery.html">gallery</a></h3>
|
||
<p>Every visualizer, six times, on six different songs' content — cast,
|
||
ink, lattice, palette and parameters all varying. Sorted least-varied
|
||
first, so the scenes that always look the same come to the top.</p>
|
||
<div class="meta">~3 min · the one to open when a scene feels familiar ·
|
||
<code>HOWTO-variety.md</code> for how to fix what it finds</div>
|
||
</div>
|
||
<div class="card fast">
|
||
<h3><a href="/gallery.html?scene=Effigy&songs=6&seeds=6">one scene, wide</a></h3>
|
||
<p>The gallery for a single visualizer, on a grid of songs (rows) against
|
||
seeds (columns) — up to every song in the bank by ten draws. Six
|
||
thumbnails ranks a library; it is far too few to study one scene.
|
||
Scores the two axes separately, which is the thing the library
|
||
gallery cannot tell you: <strong>across seeds</strong> low means the
|
||
scene ignores the identity, <strong>across songs</strong> low means it
|
||
ignores the music. The closest pair in the grid is outlined.</p>
|
||
<div class="meta">seconds, after the songs are analysed once ·
|
||
click any scene name in the gallery to open it here ·
|
||
the buttons change grid size and walk onto fresh seeds</div>
|
||
</div>
|
||
<div class="card">
|
||
<h3><a href="/filmstrip.html">filmstrip</a></h3>
|
||
<p>Every song in the bank, one frame every 30 seconds, left to right.
|
||
The gallery asks whether a scene looks the same in every song; this
|
||
asks whether a song looks the same as itself four minutes later.
|
||
Scored on <code>direction</code> — whether the distance between two
|
||
frames grows with the time between them — so the videos that are busy
|
||
and going nowhere sort to the top.</p>
|
||
<div class="meta">~6 min, then cached · <a href="/filmstrip.html?every=15">?every=15</a> tighter ·
|
||
<a href="/filmstrip.html?song=centre,ember">?song=</a> just these ·
|
||
<code>EPIC-4.md</code> for the arc it is checking</div>
|
||
</div>
|
||
<div class="card fast">
|
||
<h3><a href="/venue-gallery.html">venue gallery</a></h3>
|
||
<p>Venue × viz explorer — venue (higher-level than viz, picked first via census; hybrid
|
||
projection per archetype) against viz behind it. Try a sad-slow chamber of memories vs. a
|
||
pumping stage field from different angles and with different viz.</p>
|
||
<div class="meta">rows = songs · try <code>?venue=1</code> in gallery/filmstrip/app</div>
|
||
</div>
|
||
<div class="card fast">
|
||
<h3><a href="/">the app</a></h3>
|
||
<p>Drop a track and watch it. <code>test/songs/*.wav</code> holds the
|
||
synthetic bank if you want something with known properties.</p>
|
||
<div class="meta">build the bank with <code>npm run build:songs</code></div>
|
||
</div>
|
||
</div>
|
||
|
||
<h2>Is it correct?</h2>
|
||
<div class="grid">
|
||
<div class="card fast">
|
||
<h3><a href="/checks.html">phase gates</a></h3>
|
||
<p>Every acceptance gate in <code>PLAN.md</code>, run for real. This is
|
||
the one to check before committing.</p>
|
||
<div class="meta">
|
||
<a href="/checks.html?slow=1">?slow=1</a> full suite ·
|
||
<a href="/checks.html?phase=12">?phase=12</a> one phase
|
||
</div>
|
||
</div>
|
||
<div class="card fast">
|
||
<h3>single scene</h3>
|
||
<p>The whole per-scene battery for one visualizer: renders, animates,
|
||
deterministic, distinct, param sweep, flash rate, and every declared
|
||
trait and artifact.</p>
|
||
<div class="meta"><code>checks.html?scene=Metaballs</code> — the loop you
|
||
are in while writing or migrating one</div>
|
||
</div>
|
||
</div>
|
||
|
||
<h2>Is it varied?</h2>
|
||
<p class="note">
|
||
These measure whether two videos differ, which no other gate can see — a
|
||
generator that ignores its input passes determinism and liveness perfectly.
|
||
<span class="warn">Read the caution below before trusting a single run.</span>
|
||
</p>
|
||
<div class="grid">
|
||
<div class="card slow">
|
||
<h3><a href="/checks.html?decompose=1">decompose</a></h3>
|
||
<p>Identity against container, measured apart: hold the scene fixed and
|
||
vary the song's content, then the reverse. <strong>The most
|
||
trustworthy number here</strong> — it compares renders directly, and
|
||
its noise floor is exactly zero.</p>
|
||
<div class="meta">~1 min</div>
|
||
</div>
|
||
<div class="card slow">
|
||
<h3><a href="/checks.html?songs=1&count=12">song variety</a></h3>
|
||
<p>Twelve songs from the bank, each with its own audio-derived seed. Also
|
||
reports coupling — whether songs that <em>sound</em> different come
|
||
out <em>looking</em> different, which is still unsolved.</p>
|
||
<div class="meta">~2 min · <a href="/checks.html?variety=1">?variety=1</a>
|
||
for the seed version, one song and many seeds</div>
|
||
</div>
|
||
<div class="card slow">
|
||
<h3><a href="/checks.html?experiment=1&count=12&repeats=3">arms, with error bars</a></h3>
|
||
<p>Three pools compared over repeated runs. The template for any A/B
|
||
here: it prints the difference against its own noise band and says
|
||
outright when the two are indistinguishable.</p>
|
||
<div class="meta">~5 min</div>
|
||
</div>
|
||
<div class="card slow">
|
||
<h3><a href="/checks.html?sweep=1&count=12&sizes=4,8,16,32&repeats=3">pool sweep</a></h3>
|
||
<p>How many scenes a track should draw on. Swept rather than guessed —
|
||
and the answer turned out to be that it does not matter between 4 and
|
||
32.</p>
|
||
<div class="meta">~5 min</div>
|
||
</div>
|
||
</div>
|
||
|
||
<h2>Command line</h2>
|
||
<div class="grid">
|
||
<div class="card cli">
|
||
<h3>migration status</h3>
|
||
<p>Which scenes draw the song's content, which still draw their own, and
|
||
what each remaining one needs. Recipe in <code>MIGRATION.md</code>.</p>
|
||
<pre>node tools/migration-status.js</pre>
|
||
</div>
|
||
<div class="card cli">
|
||
<h3>cast census</h3>
|
||
<p>Which visualizers actually get cast, across songs and seeds — and for
|
||
the ones that do not, which of the four gates killed them.</p>
|
||
<pre>node tools/cast-census.js 12 40</pre>
|
||
</div>
|
||
<div class="card cli">
|
||
<h3>identity census</h3>
|
||
<p>How far apart the songs' identities are before anything is rendered.
|
||
No GPU. Run this before blaming the visualizers.</p>
|
||
<pre>node tools/identity-census.js 12</pre>
|
||
</div>
|
||
<div class="card cli">
|
||
<h3>song bank</h3>
|
||
<p>Rebuild the test songs and verify they still span every feature the
|
||
generator reads. Fails loudly if an axis has collapsed.</p>
|
||
<pre>npm run build:songs
|
||
npm run check:songs</pre>
|
||
</div>
|
||
<div class="card cli">
|
||
<h3>static gates</h3>
|
||
<p>Determinism grep, schema/shader agreement both ways, and the backtick
|
||
check that guards the shader template literals.</p>
|
||
<pre>npm run lint:scenes
|
||
npm test</pre>
|
||
</div>
|
||
</div>
|
||
|
||
<h2>Reading the variety numbers</h2>
|
||
<p class="note">
|
||
This harness has two classes of measurement and only one of them is safe to
|
||
steer by. <strong>Direct render comparisons</strong> — the gallery score,
|
||
<code>decompose</code>, the per-scene gate — have a noise floor of zero and
|
||
mean what they say. <strong>Aggregate ratios</strong> — chiefly
|
||
<code>separation</code>, which divides one small difference by another —
|
||
swing wildly between runs: three runs of the same measurement gave 0.31,
|
||
0.56 and 0.07. Four conclusions were drawn and withdrawn during this epic
|
||
for exactly that reason. Anything under about 0.01 of spread needs
|
||
<code>&repeats=3</code> before it is believed, and a difference that
|
||
changes sign with the sample is not a difference.
|
||
</p>
|
||
|
||
<h2>If a page is dead</h2>
|
||
<p class="note">
|
||
A module that fails to load takes the whole graph with it: the page renders,
|
||
every control is present, and nothing is wired to anything. The app carries a
|
||
boot guard that says so. The usual causes are a content blocker — a file
|
||
named like tracking will be treated as tracking, which cost this project a
|
||
session over a file called <code>clicktrack.js</code> — or a dev server
|
||
caught mid-restart.
|
||
</p>
|
||
<script type="module">
|
||
import {
|
||
songNames, songCovers, families, scenesIn,
|
||
venueNames, appLinks, sceneLinks, venueLinks,
|
||
SCENARIOS, LABS,
|
||
} from '/src/debug/battery.js';
|
||
|
||
const el = (tag, cls, text) => {
|
||
const e = document.createElement(tag);
|
||
if (cls) e.className = cls;
|
||
if (text !== undefined) e.textContent = text;
|
||
return e;
|
||
};
|
||
const link = (href, text) => {
|
||
const a = document.createElement('a');
|
||
a.href = href;
|
||
a.textContent = text;
|
||
return a;
|
||
};
|
||
|
||
// Songs: one row per bank song — name, corner it holds, app / +venue / strip.
|
||
{
|
||
const host = document.getElementById('battery-songs');
|
||
host.replaceChildren();
|
||
for (const name of songNames()) {
|
||
const covers = songCovers(name);
|
||
const L = appLinks(name);
|
||
const row = el('span', 'fam', `${name} — ${covers} `);
|
||
host.appendChild(row);
|
||
host.appendChild(link(L.app, 'app'));
|
||
host.appendChild(link(L.venue, '+venue'));
|
||
host.appendChild(link(`/filmstrip.html?song=${encodeURIComponent(name)}`, 'strip'));
|
||
}
|
||
}
|
||
|
||
// Venues: pinned gallery + app + filmstrip per room.
|
||
{
|
||
const host = document.getElementById('battery-venues');
|
||
host.replaceChildren();
|
||
for (const name of venueNames()) {
|
||
const L = venueLinks(name);
|
||
const card = el('div', null);
|
||
card.appendChild(el('p', null, name));
|
||
const row = el('div', 'links');
|
||
row.appendChild(link(L.gallery, 'gallery'));
|
||
row.appendChild(link(L.app, 'app +venue'));
|
||
row.appendChild(link(L.filmstrip, 'strip'));
|
||
card.appendChild(row);
|
||
host.appendChild(card);
|
||
}
|
||
}
|
||
|
||
// Scenes: one chip per visualizer (→ gate), ◈ per scene (→ wide gallery).
|
||
{
|
||
const host = document.getElementById('battery-scenes');
|
||
host.replaceChildren();
|
||
for (const fam of families()) {
|
||
host.appendChild(el('span', 'fam', fam));
|
||
for (const name of scenesIn(fam)) {
|
||
const L = sceneLinks(name);
|
||
host.appendChild(link(L.gate, name));
|
||
const w = link(L.wide, '◈');
|
||
w.title = `${name} — wide gallery (6 songs × 6 seeds)`;
|
||
host.appendChild(w);
|
||
}
|
||
}
|
||
}
|
||
|
||
// Scenarios: curated cards from the library.
|
||
{
|
||
const host = document.getElementById('battery-scenarios');
|
||
host.replaceChildren();
|
||
for (const s of SCENARIOS) {
|
||
const card = el('div', 'card');
|
||
card.appendChild(el('h3', null, s.title));
|
||
card.appendChild(el('p', null, s.desc));
|
||
const row = el('div', 'links');
|
||
for (const [label, href] of s.links) row.appendChild(link(href, label));
|
||
card.appendChild(row);
|
||
host.appendChild(card);
|
||
}
|
||
}
|
||
|
||
// Labs: pages + the CLI that drives them.
|
||
{
|
||
const host = document.getElementById('battery-labs');
|
||
host.replaceChildren();
|
||
for (const lab of LABS) {
|
||
const card = el('div', 'card cli');
|
||
card.appendChild(el('h3', null, lab.title));
|
||
card.appendChild(el('p', null, lab.desc));
|
||
if (lab.href) {
|
||
const row = el('div', 'links');
|
||
row.appendChild(link(lab.href, 'open'));
|
||
card.appendChild(row);
|
||
}
|
||
if (lab.cli) {
|
||
const pre = el('pre', null, lab.cli);
|
||
card.appendChild(pre);
|
||
}
|
||
host.appendChild(card);
|
||
}
|
||
}
|
||
</script>
|
||
</body>
|
||
</html>
|