Skip to content

feat(release): № 014, an AI composer you train and tune; workers survive the service worker - #17

Merged
PSheon merged 7 commits into
mainfrom
dev
Sep 23, 2026
Merged

PSheon merged 7 commits into
mainfrom
dev

Conversation

@PSheon

@PSheon PSheon commented Sep 23, 2026

Copy link
Copy Markdown
Owner

№ 014 — 三分鐘訓練你的 AI 作曲家

A 64,704-parameter Transformer (lib/ml's own, the one № 004 builds) learns Bach's four-part chorales in the reader's browser in about three minutes, and a synthesiser written sample by sample plays what it writes.

Three figures

  • Train and listen. It improvises at six moments on the way, so the reader hears it stop noodling. Numbers beside each piece: in key, voice crossings, held notes, parallel fifths, and the longest stretch copied from the training set.
  • You be the judge. Two pieces, one pick, one DPO step. About twenty picks move held notes from 48 % to 64 % without the model forgetting Bach — and, asked for fewer parallel fifths, it learns to stop moving instead. Reward hacking, in a figure.
  • Blind test. Bach, the model and five lines of maths, shuffled; the piano rolls and the answers wait until you have guessed.

Honest by construction: every figure's numbers are proxies for "like a chorale", never for "good", and the article says so; the copy detector is there to catch the model remembering rather than composing (5 to 10 eighths at the end of training, once 32); the reward-hacking contrast that only reproduces under a harder push is reported as exactly that.

Data: Craig Sapp's edition of Bach's 370 chorales, CC BY-NC-SA 4.0. The tokens and the trained model in public/posts/music-ai/ carry the same licence, with attribution in the README, the article and the handoff. scripts/music/ rebuilds both files byte for byte.

A fix that matters beyond this article

fix(pwa): Turbopack hands a worker its chunk list in the script URL's fragment, which a Request does not carry, so the service worker's asset cache was answering worker scripts without it. In a production build with the service worker in control, every worker on the site died on its first line — the diffusion model, the head camera's training, the path tracer's scene builder. Worker scripts now bypass the cache.

Checked

  • 324 unit tests (2 skipped), 234 E2E runs (4 skipped) against a production build, axe clean on both languages and both themes.
  • Looked at in the browser at 1440 and 390, dark and light; console read.
  • Measured twice in node with the page's own recipe; the article's table is the average (docs/research/music-ai/RESULTS.md).

🤖 Generated with Claude Code

PSheon and others added 7 commits September 23, 2026 10:47
Turbopack hands a worker its chunk list in the script URL's fragment
(`/_next/static/chunks/turbopack-worker-….js#params=…`). A fragment is not part of a Request, so
answering one from the service worker's asset cache loses it and the worker dies on the first line
with "Missing worker bootstrap config". In a production build with the service worker in control,
that killed every worker on the site: the diffusion model, the head camera's training, the path
tracer's scene builder. Worker scripts now go straight to the network.

Found while adding article 014, whose figures are three workers; `e2e/music-ai.spec.ts` covers it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…tes them

The model is lib/ml's Transformer again (the one № 004 builds), 64,704 parameters: 56 words, a
16-step window, two layers. A chorale becomes one token per voice per eighth note — a pitch, HOLD or
REST — so writing music is writing the next word. Beside it: what a piece is measured by (in key,
voice crossings, parallel fifths, held notes, chord variety), a copy detector against the training
set, the five-line rules composer, and DPO for the preference figure.

Data: Craig Sapp's edition of Bach's 370 four-part chorales, CC BY-NC-SA 4.0, moved to C major or
A minor, 333 to train on and 37 kept back. scripts/music rebuilds both .bin files byte for byte from
the corpus clone and a checkpoint; the corpus and the runs live outside git (docs/research/music-ai).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Every sample is computed here, with none of the browser's own instruments or effects: three soft
voices (a warm pad, an electric piano by FM, a flute with breath), a damped stereo reverb laid out
like Freeverb, and a low-pass over the mix. The first organ was sharp — 6 to 16 dB more in the
1–4 kHz band the ear is most sensitive to (docs/research/music-ai, "Timbre").

The player owns the page's single AudioContext and the synthesiser worker: playing something stops
whatever was playing, changing the instrument keeps the position and never leaves a gap, a piece
counts as finished when the music ends rather than when its reverb tail does, and the last figure to
leave stops the sound and closes the context.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
One worker file serves all of it. Training keeps a worker to itself for the three minutes it holds
the thread; figures 02 and 03 share a second one, so the trained model and the chorales are fetched
and parsed once instead of twice; the player has a third for synthesis, so a note never waits behind
a training step. Every message carries which figure asked, because a shared worker's replies reach
both and loading one figure used to reset the other.

Workers are made in one place (worker-factory.ts): three `new Worker(new URL(…))` call sites for the
same file left the production bundle unable to boot any of them.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… wrote it

Fig. 01 trains a composer in about three minutes and improvises at six moments on the way, so the
reader hears it stop noodling; the snapshot they picked is never taken from under them by the next
one, and the numbers underneath say what changed.

Fig. 02 is the reader as the judge: two pieces, one pick, one DPO step against a frozen copy of the
model. About twenty picks are audible; at the page's learning rate that moves held notes from 48 %
to 64 % without the model forgetting Bach (docs/research/music-ai).

Fig. 03 is the blind test: Bach, the model and five lines of maths, shuffled, with the piano rolls
and the answers held back until the reader has guessed — a chorale and a block-chord machine look
nothing alike.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…self

A small Transformer learns Bach's chorales in the reader's browser in three minutes, and they hear
it learn. Then the honest half: the copy detector shows it composing rather than remembering (5 to
10 eighths at the end, once 32), the judge figure shows it gaming the score — asked for fewer
parallel fifths it learns to stop moving — and five lines of maths get a blind test of their own.

Numbers are the page's own recipe, measured twice in node and averaged (docs/research/music-ai).
Sound is checked by hand: CI cannot hear, so e2e/music-ai.spec.ts covers training, a pick, the blind
test's secrecy, and that nothing heavy is fetched before it is asked for. The smoke test's
single-article tag moved to `multi-task`: `llm` now has two.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
RESULTS.md carries every number the article quotes and the ones it does not: the licence trail for
the data, both training recipes, the preference-tuning tables at the research push and at the page's
gentler one (where the reward-hacking contrast does not reproduce, which the article now says), the
timbre work, and two ways of measuring harshness that were wrong.

DESIGN.md §4 gains "Instruments that make sound": one player per page, sound that stops when the
figures go, a piece that ends with its music and not its reverb, and the fact that CI cannot hear.
HANDOFF.md gets article 014, and COMMITS.md two scopes that were already in the log.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented Sep 23, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
paul-notebook Ready Ready Preview Sep 23, 2026 2:48am UTC

Request Review

@PSheon
PSheon merged commit 1c80d64 into main Sep 23, 2026
6 checks passed

This branch was successfully deployed

1 active deployment
Preview — 83d5efdc Deployed Sep 23, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant