Repository navigation
feat: playable voicings and Rhythm Lab - #2
Merged
Merged
Conversation
Pure TypeScript engine under engine/ (no React, Tone.js, DOM or runtime
dependencies), deterministic and seeded:
- pitch, tuning presets, mulberry32 PRNG, mm fret geometry
- one shapeToMidi conversion for tabs and audio, plus RiffForge and
Chord Explorer adapters (string 0 = lowest string everywhere)
- personal hand profile with calibration, reach interpolation and pair
limits (comfort guidance, not a safety assessment)
- exhaustive finger assignment with barre detection, side-by-side finger
spacing, reach and pair limits, physical cost breakdown
- voicing families (power, sus/add, shells, quartal, clusters, phrygian,
triads, drones), legacy recipes from library notes, relaxation ladder
- honest naming: degree strings from sounding notes, confident symbols only
- playability cost (physical, right hand, musical) and novelty axis
- windowed voicing generator with diversification and named empty results
- transition cost and exact minimax Viterbi over chord candidates
Includes a property test over 12 roots x 20 families x {E Standard, Drop D}
(100 percent coverage, every voicing barre-free, inside reach, audio equals
tab) and a brute-force completeness check.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
engine/rhythm/: 480 PPQ grid math, nine original style presets (chug engine, gallop, reverse gallop, displaced threes, halftime stomp, tremolo wall, syncopated stabs, 7/8 as 2+2+3, five over four), a seeded generator with accent grouping, polymeter cycles, rests, ties, palm mute, accents and harmony changes only on accents, picking guidance (alternate on the 16th grid, downstrokes mode, fast runs on their own subdivision), validation with a tooFastForProfile warning, mutate and lock-aware regenerate, grid editing helpers, and pure playback and MIDI note events. Built alongside the M1 engine core; the Rhythm Lab UI follows separately. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Library cards no longer transpose the curated fretboard strings. Each card's
JSON notes become an interval recipe (fromLegacyNotes); the engine generates a
no-barre shape for the selected root, tuning and hand profile, and both the
tab and the played notes come from that one shape. The card picks the
generated shape closest to the curated idea (bass register, note count, the
curated tab moved rigidly) and shows string names, finger numbers, degrees
per string and an honest "sounds as" name. Unplayable recipes show the
binding constraint instead of a broken tab; relaxed recipes say what changed.
Removed transposeTabs and its tests. They codified two bugs: Drop mode always
added 2 frets to the lowest string even though the drop JSON is authored in
Drop D (wrong bass, E instead of D), and per-string +12 wrapping turned open
strings into hidden barres (Em(add9) to G became 3 5 7 3 3 3). Name and
subtext transposition stay, now without gluing interval words to the root
("E b2", not "Eb2"), without mistaking "Dim/b2" for a D root, and replacing
flat roots completely ("Bb(VI)" -> "E(VI)").
Also:
- Chord Explorer deep links send the case-sensitive "Major" type id (it
was rejected as "major") and the card shape as gv in E Standard.
- Stored v1 riffs with flat spellings such as Bb2 are respelled on load so
MIDI export no longer throws.
- UPGRADE.md item 4 is marked as superseded by engine/.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Hand profile store persisted at riffforge:hand-profile:v1 with full
validation and safe fallback, export/import as JSON for Chord Explorer.
- Calibration drawer (modal, focus trapped, Escape closes): scale length
presets or custom mm, two pinky-reach checks, no-barre, stretches, open
strings, highest fret and comfortable 16th tempo, live derived mm values
and the comfort disclaimer ("Stop if you feel pain or tension.").
- Voicing Finder view: family chips grouped by sound, Easiest / Most unusual
sort, string-group and pedal-compatible filters, result cards with tab,
finger numbers, degrees, honest name, cost explanation, play, add to riff
and open in Chord Explorer; empty results name the binding constraint and
offer one-click profile hints that never suggest barres.
- Library cards follow the calibrated profile.
- Workbench view switch persisted as ?view=finder; finder and drawer are
lazy-loaded chunks.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Rhythm store persisted at riffforge:rhythm:v1 (params, seed, pattern,
up to 4 chord slots, BPM 60-240, locked bars, metronome) with validation
and safe fallback.
- Rhythm Lab view: nine style presets, density and syncopation, 16th or
16th-triplet grid, 1/2/4 bars, alternate or downstroke picking, New idea,
Mutate, per-bar Lock, play/stop with metronome and playhead, MIDI export.
- Editable grid with a lane per target (pedal, chord slots, dead), bar
lines, accent groups, counting, accent/PM/tie/pick glyphs with text, one
tab stop with roving focus (arrows, Space, A, P, Delete) and spoken cell
labels; scrolls inside its container on phones with 44 px cells.
- Chord slots from the Voicing Finder ("Slot") or the RiffBar ("Use RiffBar
chords as slots"); Optimize fingerings runs minimax optimizeSequence with
the time before each chord change and shows the hardest transition.
- Audio: playRhythm/stopRhythm on Tone.Part through the existing chain;
palm mute shortens and softens notes, accents are louder, dead notes are
short scratches. The RiffBar and the lab stop each other; RiffBar
behaviour is otherwise unchanged.
- MIDI: encodeMidiNotes and rhythmToMidi (velocities, palm-mute durations,
time signature); the existing writer API and tests are unchanged.
- Tempo note when a pattern is faster than the profile's comfortable 16th
tempo (a warning, never a block).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Chord Explorer link parsed the curated card name, so every "Drop ..." card (and "Diminished Phrygian") linked to root D. Resolved voicings now carry an explorer target: the selected root, and a type from the matched engine family or a full chord formula present without a contradicting third. Power chords, dyads, the tritone, fourths, quartal stacks and clusters send no type instead of a wrong one. The shape still travels as gv only in E Standard. parseRoot is kept as a fallback for unplayable cards and now rejects words that merely start with a note letter and reads flat roots as sharps. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The load effect now cancels on cleanup, like the favorites effect, so an older getChords response cannot overwrite cards for a newer root, tuning or vibe. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Card titles came from curated labels that never moved with the root: every
"Drop X" name stayed put ("DROP BB MAJ7" while sounding Gmaj7 at G), and
key-bound subtexts such as "Bb(VI)" described a different chord. The title
is now the engine's honest name for the sounded shape, shown in its own case
(uppercase turned "b9" into "B9"). The curated nickname, with "Drop "
removed and its root moved to the selected root, plus any descriptive
subtext ("m2 Clash") form the secondary line; Roman numerals and stored
chord symbols are dropped. The RiffBar stores the same honest name.
Data: the "Am Add9" and "Drop Gm Add9" rows and their related copies had no
minor third, so they sounded sus2; they now include the b3. "Drop Fm
Mediant" had a fourth where its minor third belongs. Ids and baseRoot are
unchanged, so favorites survive.
A test resolves every library entry at all 12 roots in its tuning and checks
that the title, the sounded name and any nickname root agree with the
selected root, with no Roman-numeral subtext left.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Framer Motion makes any element with whileTap focusable (tabindex=0), so the card wrapper and the lock wrapper were empty tab stops. Both now use CSS active styles instead, the lock keeps a single handler on its button with a fixed label and aria-pressed, and the dead "Lock as main chord" selector is gone. The Play button stays the keyboard path for playing a card. The Clean/Distortion channel buttons get aria-pressed, type="button" and a visible focus ring in the channel color. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The hand profile is editable now, so an empty card names the profile rather than the player's hand. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The finder kept its own table that sent power chords as Major, the tritone as dim and fourths or quartal stacks as sus4. It now uses the same rule as library cards: a type only where Chord Explorer has one, none for power chords, dyads, the tritone, fourths, quartal stacks and clusters. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Row labels, string names and degrees were 9-10px; they are now 11px. Muted "x" cells use neutral-400 so they reach 4.5:1 on the card background. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Card titles are now sounded chord symbols, and CSS uppercase turned "Gm(add9)" into "GM(ADD9)" (read as G major) in the RiffBar chips and the Related Variations header. Only the chord name keeps its case; the RiffBar layout and the rest of its styling are unchanged. The decorative letters behind each card are hidden from screen readers. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Verification
npm test: 32 files, 813 tests passednpx tsc --noEmit: passednpm run build: passedgit diff --check: passed