Skip to content

Sheet music for teaching: meter, key, pickup, MusicXML, and a sheet-music view with autoplay and a finish notice - #37

Merged
snakajima merged 14 commits into
mainfrom
notation
Oct 23, 2026
Merged

snakajima merged 14 commits into
mainfrom
notation

Conversation

@snakajima

@snakajima snakajima commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

Sheet music for music education. An LLM writes a short piece and shows it to the user either as a waveform or as sheet music. It can also have the piece start playing right away and be told when the user has heard it to the end.

Score format (sound unchanged)

  • New optional top-level fields: meter ("3/4", "6/8"), key ("F", "Am") and pickup (beats before the first full bar).
  • New optional track fields: clef (treble/bass) and notation: false.
  • None of these affect rendering, and every existing golden hash still holds.

Library

  • src/notation.ts turns a score into measures, staves and written note values. It handles ties, dots, triplets in quarter-note beats, accidentals, lyrics with hyphens and extenders, chord symbols, and a grand staff for piano.
  • When a note has to be rounded onto the beat grid or shortened, it produces a warning for the LLM.
  • src/musicxml.ts writes MusicXML 4.0 (partwise).
  • checkScore adds notation warnings when the score sets meter, key or pickup, or when it is called with view: "notation".
  • New renderScore options: view ("waveform" | "notation"), autoplay and notifyWhenDone. They are stored in the result's presentation.
  • If a score itself contains these options, the error says they belong in the call, not in the score.
  • When there is an output directory (the MCP case), <stem>.musicxml is written next to the audio.
  • CLI: jinglescript notation <score> -o x.musicxml and check --notation.
  • The guide has a new section, "Sheet music". README, schema, skill and plugin definition are regenerated.

GUI plugin

  • New dependency: opensheetmusicdisplay (BSD-3-Clause; Verovio was ruled out as LGPL). It is external in the build and lazy-loaded only for the sheet-music view.
  • Waveform / Sheet music toggle, a cursor that follows playback, a seek bar, and a MusicXML download.
  • Hosts mount and remount every card in a chat. So a card autoplays only if it was rendered in the last 2 minutes, and only once. The finish message to the LLM (sendTextMessage) is sent once, on ended.
  • Both facts are saved in the result's viewState through onUpdateResult.
  • The plugin's instructions to the LLM vary with the chosen presentation.

Examples and eval

  • Five new learn-* examples: C major scale, rhythms, Twinkle (grand staff), Happy Birthday (pickup, 3/4) and Row Your Boat (6/8). Each has an audio golden snapshot and a MusicXML snapshot.
  • eval/requests-notation.json has 8 requests in English and Japanese. The eval/check.ts notation spec checks meter, key, pickup, harmonies and that there are no warnings.
  • Default model: 8/8 on the first try.
  • Haiku: 0/8 at first, because it put view into the score. After the guide fix and the new error hint, 8/8.

Verification

  • yarn format, lint and typecheck pass.
  • Root tests: 706 passed, 102 skipped. smoke.sh passes.
  • Plugin: typecheck, 196 tests, build and playground build pass.
  • Headless Chrome, on the playground and the demo:
    • All five examples draw correctly.
    • The cursor moves during playback.
    • The finish message is sent once, and not again on a replay.
  • Not verified: autoplay without a click. Headless Chrome blocks it, and the "Press play to listen" fallback was shown instead.
  • Not done: listening checks on the rendered examples. Audio is unchanged for existing scores.

Codex review (local, codex review --base origin/main)

Twelve rounds raised one or two P2 findings each. All were fixed with a test that fails without the fix. The thirteenth round found nothing.

  • Sheet music that no longer matches the audio, with no warning. Each of these now gives a warning with the note's path:
    • an off-grid len
    • straight notes mixed with triplets inside one quarter
    • triplets in 6/8 or 3/8
    • notes that start together with different lens
    • any pickup that gets rounded
  • Bars whose durations don't add up to their length (pickups of triplet length, a note rounded away, a 0.25 pickup next to triplets):
    • A test now writes 144 generated rhythms over 8 meters and 6 pickups and checks every bar.
    • A throwaway fuzz of 20 000 scores found 1 188 broken bars before the last fix and none after.
  • Smaller fixes:
    • Triplets now also work in 2/2.
    • A note tied over a bar line no longer hides a later note's accidental.
    • A sound effect that takes a pitch (laser) stays off the sheet music.
    • Humanize can no longer swap lyric syllables.
    • A repeated note warns once.
  • Player view:
    • A draw still loading is cancelled on unmount.
    • A player reused for another result starts over with that result's view and memory. Checked in the playground: before the fix, the second render kept the first one's view.
  • The 16 scores from the earlier eval runs still give no notation warnings.

🤖 Generated with Claude Code

work in jinglescript

…sic view with autoplay and a finish notice

The score gains meter, key, pickup and per-track clef / notation (none changes the audio).
scoreToNotation writes bars, ties, triplets, rests, accidentals, lyrics, chord symbols and a
grand staff, warning (with the note's path) about every rounding or shortening; scoreToMusicXml
writes MusicXML 4.0. CLI: notation, check --notation. renderScore takes view, autoplay and
notifyWhenDone; the plugin's view draws the sheet music with OpenSheetMusicDisplay, a cursor
following playback, autoplays only a fresh render once and tells the LLM once when it has been
heard, both remembered in viewState. Five learn-* examples; eval/requests-notation.json.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@socket-security

socket-security Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addedopensheetmusicdisplay@​2.2.19910010095100

View full report

snakajima and others added 13 commits October 23, 2026 05:45
…ie over the bar line

From the Codex review of #37. A len the sheet music rounds (0.29 written as a 16th) was rounded
silently; it is now reported at the note's len, and the threshold is half a grid step for times
and lengths alike (a triplet's 0.333 stays far inside it). A note tied over from the bar before
no longer counts as stating its accidental, so a later F# in the new bar keeps its sharp.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…time the grid moves

From the second Codex review of #37. Triplets are now grouped per quarter note, so 2/2 writes
them too; in 6/8 and 3/8 (and anywhere else they cannot be written) triplet times go onto the
straight grid instead of producing a 32nd two divisions long. Any onset or len the bar moves to
fit — a 0.25 inside a triplet quarter, a triplet in 6/8 — is now a warning with the note's path,
so checkScore no longer calls such sheet music clean. The guide says where triplets go.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
From the third Codex review of #37. A pickup bar starts off the straight grid when the pickup is
a triplet's length (0.333), but the bar's start was left out of triplet detection, so its notes
were split into straight values that overfilled it. The test checks that every bar's items add up
to its length, for triplet and straight pickups.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… one warning per repeated note

From the fourth Codex review of #37. Notes that start together on one staff become one chord at
the longest length; a shorter `len` is now reported at its path instead of silently lengthened.
Warnings are also kept once per note and kind, so a repeated note does not repeat its warning.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…, tested broadly

From the fifth Codex review of #37. A note that rounding collapsed after a gap left its rest
written while the position stayed put, so the next rest covered the gap again and overfilled the
bar. Collapsed notes are now skipped before any rest, and a start rounded before the last note's
end is clamped to it. A new test writes 96 generated rhythms (straight, triplet and off-grid
steps; eight meters; with and without pickups) and checks that every bar adds up to its length.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
From the sixth Codex review of #37. A triplet-length pickup (0.333) in a meter without triplets
(6/8) made a pickup bar of 8 steps that straight values cannot fill, overfilling it and, on a
grand staff, misaligning the second staff. It is now rounded to a 32nd and reported at `pickup`.
The broad bar-length test now runs every pickup in every meter instead of picking one at random.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
From the seventh Codex review of #37. A laser or pop given a `pitch` was drawn on a staff, and a
score of such effects got sheet music instead of the waveform. Tracks are now shown only when
their instrument (built in or the score's own, as expandScore resolves it) is pitched.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
From the eighth Codex review of #37. A pickup off the grid (0.29) was rounded to 0.25 without a
word, moving every bar line. pickupSteps now compares the written pickup with the exact one and
reports the difference, whether it came from the grid or from a meter without triplets.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…hed away

From the ninth Codex review of #37. Unmounting now cancels a pending draw, which also checks
after the OSMD import, so switching back to Waveform mid-load no longer creates or renders a
display in a detached container. Toggled five times quickly in the demo: one sheet, no errors.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…lt's view and memory

From the tenth Codex review of #37. A host (or the playground) may hand an existing View a new
selectedResult; its memory, view and autoplay check stayed those of the first result, so a new
sheet-music render could open as a waveform and an old `notified` could swallow the new finish
message. Watching the result's uuid now pauses, resets and runs the autoplay check again once
the audio element has the new source. Checked in the playground: before, a second render kept
the first one's Sheet music view; now it opens in its own.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ables

From the eleventh Codex review of #37. expandScore orders events by humanized playback time, and
lyric joins and chord-symbol changes were read in that order; two close notes swapped by
humanize wrote "hap-" / "py" as begin / single. Notation now sorts a track's events by their
written time first.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…itten straight

From the twelfth Codex review of #37. A pickup of 0.25 next to triplets put the bar's start inside
a triplet quarter but off its grid, and the 32nd fallback overfilled the pickup. Such a quarter is
now written straight (where every bar line falls), with the moved notes reported. The bar-length
test adds 0.25 and 0.667 pickups and sixth-of-a-beat steps. An ad-hoc fuzz of 20 000 scores
(11 meters, pickups, chords, two hands, off-grid lengths) found 1 188 overfilled bars before and
none after.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sheet music for teaching (meter, key, pickup; MusicXML; the sheet-music view with autoplay and a
finish notice). The GUI plugin follows at 0.10.0 and needs jinglescript ^0.10.0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@snakajima
snakajima merged commit f237d95 into main Oct 23, 2026
10 checks passed
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