feat(mockups): slot 16 voyage readable; record 14 vs 15 feedback

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Lucas WintherandClaude Opus 5.5 committed 2026-10-30 02:37:19 +01:00
1 parent 7e3fe798a3
commit 27d1cbfa8e
12 files changed
+131 -4

No files matched your search

+2 -2
View File
@@ -1,7 +1,7 @@
# EPIC: The Musical Timeline
Unofficial fan site: an interactive timeline of *EPIC: The Musical* songs. Currently 7 design-direction
mockups. Bun 1.3.14 (server, bundler, test runner), TypeScript, three.js (mockups 06/07/13/14/15 only), Docker.
mockups. Bun 1.3.14 (server, bundler, test runner), TypeScript, three.js (mockups 06/07/13–16 only), Docker.
`AGENTS.md` is the single source of truth for agents here. `CLAUDE.md` and `GEMINI.md` only point here.
Deeper context on demand: `docs/ARCHITECTURE.md`, `docs/DECISIONS.md`, `docs/TODO.md`.
@@ -10,7 +10,7 @@ Deeper context on demand: `docs/ARCHITECTURE.md`, `docs/DECISIONS.md`, `docs/TOD
Install: `bun install --frozen-lockfile`
Dev: `bun run dev` (`PORT` env, default 3000)
Test: `bun test` | Single file: `bun test tests/data.test.ts` | Single case: `bun test -t "registry has 15"`
Test: `bun test` | Single file: `bun test tests/data.test.ts` | Single case: `bun test -t "registry has 16"`
Typecheck: `bun run typecheck`
Build: `bun run build` | Run the build: `bun run start`
Docker: `docker build -t epic-timeline .` then `docker run --rm -p 3000:3000 epic-timeline`
+4
View File
@@ -62,3 +62,7 @@ persistent side panel. No new dependency.
## 2026-10-30 — three.js allowed in mockup 15
**Decision:** mockup 15 (Voyage, look 2) may use three.js; it iterates on 14. No new dependency.
## 2026-10-30 — three.js allowed in mockup 16
**Decision:** mockup 16 (Voyage, readable) may use three.js; it iterates on 15. No new dependency.
+1 -1
View File
@@ -22,7 +22,7 @@ or `AGENTS.md` Commands.
| `bun install --frozen-lockfile` | worked (no changes) |
| `bun test` | worked — 34 pass, 0 fail, 3 files |
| `bun test tests/data.test.ts` | worked — 10 pass |
| `bun test -t "registry has 15"` | worked — 1 pass |
| `bun test -t "registry has 16"` | worked — 1 pass |
| `bun run typecheck` | worked (`tsc --noEmit`, exit 0) |
| `bun run build` | worked — `dist/server.js` + hashed page assets |
| `bun run dev` (`PORT=3113`) | worked — `/` and all mockup routes 200, no-slash form 302 |
+23
View File
@@ -259,6 +259,29 @@ mockup of the transition.
- User asked to compare with 14: "can you open mockup 14 again so i can compare?" (14 is still served
unchanged at /mockups/14-voyage-panel/.)
### 16. 14 vs 15 compared
> "Thanks. First off, 15's font is very hard to read. 15 Accents with the freisa patterns on all the
> menus are very nice though, and the colors on the "between sagas a year at sea" looks nice. The hard
> question is paths A, B and C, cause they all have their strengths. In terms of menu, listen should be
> easier to do, and perhaps closer tied to the lyrics tab."
- **Fix:** 15's typography is "very hard to read" (the small, wide-tracked Cormorant capitals used for
labels, and small SVG text). Go back to readable labels (14's style) at comfortable sizes.
- **Keep:** the frieze-pattern accents on all menus/panels (15).
- **Keep:** the colours of the "Between sagas · a year at sea" beat (15's beat 2).
- **Paths A/B/C:** undecided, "they all have their strengths".
- **Listen:** should be easier, and tied more closely to the Lyrics tab.
- *(Claude's proposal)* Mockup **16** from 15:
- readable type: Inter for all labels/UI (no wide-tracked small caps), Cormorant only for large titles;
nothing under 13px;
- **Listen + Lyrics as one tab**: a player at the top of the tab (play, scrub bar with part markers),
lyrics by part below; clicking a part jumps there, the current part is highlighted as it plays;
plus a play button always visible in the panel header;
- **paths combined rather than chosen**: C's timeline is the default home and can be collapsed to a
slim bar; when collapsed, A's strip appears in the panel; B's ribbons in the sea are an on/off
switch ("Threads on the sea").
## Open questions (in priority order)
1. ~~Which sea-chart approach (08–11) reads best, or which mix?~~ 11's navigation + 10's style (§9).
@@ -0,0 +1,74 @@
# 16 Voyage, readable — iteration brief
## Why
The user compared 14 and 15 (verbatim):
> "Thanks. First off, 15's font is very hard to read. 15 Accents with the freisa patterns on all the
> menus are very nice though, and the colors on the "between sagas a year at sea" looks nice. The hard
> question is paths A, B and C, cause they all have their strengths. In terms of menu, listen should be
> easier to do, and perhaps closer tied to the lyrics tab."
16 starts from a **copy of 15** (`src/mockups/15-voyage-look2/`). Keep: look 2, the five beats and
transitions, stylised toon/ink 3D, the frieze-pattern accents on every menu/panel/HUD, the colours of
beat 2 ("Between sagas · a year at sea"), placeholders, URL params.
## 1. Readable type (the top complaint)
- Stop using small, wide-tracked Cormorant capitals for labels (`.lbl`, tab names, step-dot labels,
"PATHS", panel eyebrows, data-row labels, saga names, frieze-thread labels, timeline labels).
- **Inter** for all UI text and labels: sentence case or modest caps with ≤0.06em tracking, weight
500–600. **Cormorant** only for large display titles (caption title, panel song title, frieze
inscriptions at ≥19px).
- **Nothing under 13px on screen**, body text in the panel ≥15px, timeline song titles ≥12.5px with
room. Check contrast (≥4.5:1 for text) on parchment.
- Compare against 14's panel, which the user found readable.
## 2. Listen + Lyrics, together
- Merge the Lyrics tab and the Listen section into **one tab, "Listen & lyrics"** (About keeps facts,
Odyssey, parts and singers, and Watch).
- At the top of that tab, a **player**: big play/pause button, elapsed / total time, a scrub bar with
**part markers** (Verse 1, Chorus…) you can click. Below it the lyrics, by part with singer labels and
motif chips. Clicking a part's heading jumps the player there. While playing, the **current part is
highlighted** and scrolled into view.
- There are no real audio files. Simulate playback with a timer over a made-up duration per song
(spread part start times across it; use `part.startSec` if present), and say so in a small note
("Preview player: no audio file yet"). If `song.audio` exists, use a real `<audio>` element instead.
Do not change `src/shared/audio-slot.ts` — build this player in your folder.
- **Play button always visible in the panel header** next to the song title: one click starts the
current song and switches to the Listen & lyrics tab.
## 3. Paths: combine A, B and C instead of choosing
Remove the A/B/C toggle. Instead:
- **C is the default home**: the bottom timeline with all songs, saga dividers, "you are here", and
threads underneath. It gets a **collapse** control (chevron) that shrinks it to a slim bar (≈56px:
just the song stops and you-are-here, no threads).
- **When the timeline is collapsed, A's strip appears** pinned in the panel above the tabs, so threads
are never hidden. When expanded, the panel strip hides (no duplication).
- **B's ribbons are an on/off switch** in the controls: "Threads on the sea" (default off). Fix 15's
B issue: labels stacking over the cave mouth — offset/stack them so they never overlap each other or
the cave.
- Clicking a motif anywhere still highlights it everywhere (timeline, strip, ribbons, lyrics chips).
- URL: `?timeline=open|closed`, `?ribbons=1`, `?motif=`, `?tab=about|listen|paths|fan`.
- Make sure the expanded timeline doesn't eat the stage: keep it ≤190px tall and frame the 3D camera
so the ship and island sit above it.
## Rules
- Edit ONLY `src/mockups/16-voyage-readable/` (stub files exist; index.html links credits; main.ts calls
`mountCredits()`; replace the stub style.css with unique content). three.js allowed. Copy from 15
(and 14 for its readable panel type); don't import across mockup folders.
- Credits footer fully visible in every state.
- Build to `/tmp/claude-1000/-home-user-epic-the-musical-timeline/fb650f6d-47a0-47e4-8f6b-197d02417f3b/scratchpad/r3-16`,
serve with `cd <outdir> && PORT=4116 bun server.js`. Kill only your own server.
- Screenshots 1440×900 with `--use-angle=swiftshader --use-gl=angle --enable-unsafe-swiftshader
--virtual-time-budget=6000`: `screenshots/16-voyage-readable-beat{1..5}.png`, `-listen.png`
(beat 3, Listen & lyrics tab, mid-playback if you can freeze it e.g. `?t_play=40`), `-collapsed.png`
(timeline closed, strip in panel), `-ribbons.png` (beat 3 with ribbons on, `?motif=polyphemus`),
`-fan.png`. Look at every one, read every label at 100% zoom, and fix anything small or cramped.
- `bun test` and `bun run typecheck` must pass. Don't commit. Don't spawn subagents.
- If you run low on budget, stop and write a pause report (done / checklist / next step) instead.
- Report to `docs/design/002-sea-chart-spikes/16-voyage-readable-report.md`: what you built, what works,
what doesn't, screenshots.
+1
View File
@@ -28,6 +28,7 @@
<li class="card"><a href="/mockups/13-curtain-call/"><span class="card__num">13</span><h2>Curtain call</h2><p>The frieze curtain that carries you from the 2D story into the 3D sea and down into a landing.</p><p class="card__try">Try this: Press Play, or step through: frieze, sea, Cyclops' Island, frieze, cave. Switch the look (1/2/3).</p></a></li>
<li class="card"><a href="/mockups/14-voyage-panel/"><span class="card__num">14</span><h2>Voyage with side panel</h2><p>The curtain-call voyage with stylised models and a side panel that stays open: about, paths, lyrics, fan works.</p><p class="card__try">Try this: Play the voyage and watch the panel follow; open Lyrics and Fan works; switch the look (2/3).</p></a></li>
<li class="card"><a href="/mockups/15-voyage-look2/"><span class="card__num">15</span><h2>Voyage, look 2</h2><p>14 in look 2, with the frieze, panel and world tied together, and the motif paths out in the open.</p><p class="card__try">Try this: Step through the voyage and switch where the paths live (A strip, B in the sea, C timeline).</p></a></li>
<li class="card"><a href="/mockups/16-voyage-readable/"><span class="card__num">16</span><h2>Voyage, readable</h2><p>15 with readable type, listening folded into the lyrics, and all three motif paths working together.</p><p class="card__try">Try this: Press play in the panel header, follow the lyrics, collapse the timeline, and turn on threads on the sea.</p></a></li>
</ul>
</main>
<footer data-fan-credits></footer>
+15
View File
@@ -0,0 +1,15 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Voyage, readable — EPIC Timeline mockup</title>
<link rel="stylesheet" href="../../shared/credits.css" />
<link rel="stylesheet" href="./style.css" />
</head>
<body>
<main id="app"></main>
<footer data-fan-credits></footer>
<script type="module" src="./main.ts"></script>
</body>
</html>
+3
View File
@@ -0,0 +1,3 @@
import { mountCredits } from "../../shared/credits";
mountCredits();
+1
View File
@@ -0,0 +1 @@
body { margin: 0; --stub-16: 1; }
+3
View File
@@ -46,4 +46,7 @@ export const mockups: MockupInfo[] = [
{ slug: "15-voyage-look2", number: 15, name: "Voyage, look 2",
pitch: "14 in look 2, with the frieze, panel and world tied together, and the motif paths out in the open.",
tryThis: "Step through the voyage and switch where the paths live (A strip, B in the sea, C timeline)." },
{ slug: "16-voyage-readable", number: 16, name: "Voyage, readable",
pitch: "15 with readable type, listening folded into the lyrics, and all three motif paths working together.",
tryThis: "Press play in the panel header, follow the lyrics, collapse the timeline, and turn on threads on the sea." },
];
+3
View File
@@ -15,6 +15,7 @@ import m11 from "./mockups/12-vellum-atlas/index.html";
import m12 from "./mockups/13-curtain-call/index.html";
import m13 from "./mockups/14-voyage-panel/index.html";
import m14 from "./mockups/15-voyage-look2/index.html";
import m15 from "./mockups/16-voyage-readable/index.html";
export function startServer(port: number): Server<undefined> {
return Bun.serve({
@@ -51,6 +52,8 @@ export function startServer(port: number): Server<undefined> {
"/mockups/14-voyage-panel": Response.redirect("/mockups/14-voyage-panel/", 302),
"/mockups/15-voyage-look2/": m14,
"/mockups/15-voyage-look2": Response.redirect("/mockups/15-voyage-look2/", 302),
"/mockups/16-voyage-readable/": m15,
"/mockups/16-voyage-readable": Response.redirect("/mockups/16-voyage-readable/", 302),
},
development: process.env.NODE_ENV !== "production",
fetch() {
+1 -1
View File
@@ -8,7 +8,7 @@ let base: string;
beforeAll(() => { server = startServer(0); base = `http://localhost:${server.port}`; });
afterAll(() => server.stop(true));
test("registry has 15 mockups", () => expect(mockups).toHaveLength(15));
test("registry has 16 mockups", () => expect(mockups).toHaveLength(16));
test("index links every mockup", async () => {
const res = await fetch(base + "/");