Tabfier
Transcribes guitar and bass recordings into playable tablature.
Transcribes a guitar or bass recording into playable, editable tablature, entirely in the browser — nothing is uploaded.
The problem
Transcribing your own guitar or bass playing into tab is normally manual, ear-by-ear work, or it means uploading a recording and waiting. Tabfier does it in the browser: record or import audio, get an editable tab out, nothing leaves the device.
The hard part isn’t detecting notes — it’s turning a detected pitch into a specific string and fret. The same note usually has several fingerings, and picking the wrong one produces a tab that’s technically correct and unplayable the way it’s written.
Approach and methodology
Built measured rather than tuned by ear: the project keeps a mechanism doc, a ranked list of known accuracy problems, and a results doc with real F1 scores against real guitar recordings from GuitarSet — not just synthetic test audio, which overstates accuracy considerably (see Results).
Fret assignment is treated as an optimisation problem: the constants weighing “prefer a lower hand position” against “prefer fewer position shifts” were fit and cross-validated against real players’ actual fingering choices from that same dataset. Negative results are kept in the docs too — two attempts to close a gap in open-string usage both measurably hurt accuracy and were reverted, recorded rather than deleted.
Built with Claude Code as the coding agent. Every UI element carries a test id specifically so a change can be verified against the real, running app in a browser rather than trusted on faith.
Architecture
Nothing leaves the browser: transcription runs entirely client-side, and audio, notes, and edits are all stored locally. The only network request the app makes at all is fetching the model’s own weights from a CDN the first time it runs. Tab is drawn as hand-rolled SVG — no third-party notation engraver — and exports to a hand-written MIDI encoder and MusicXML.
The editor exposes real controls into that pipeline, not just a finished tab to look at: Simplify collapses a chord down to its lowest note; Fold octaves corrects octave-detection errors; Positional turns the hand-position-aware fret search on or off; Hand span sets how many frets that search will stretch across; Confidence and Min length filter out low-confidence or too-short detections before they ever reach the tab. Each one is a direct dial on a step of the pipeline above, not a separate feature bolted on afterward.
The fret-assignment search itself, from src/core/positional.ts — the state cost that turns
“prefer a lower hand position” and “prefer fewer shifts” into numbers a solver can compare:
const stateCost = (noteIndex: number, s: State): number => {
const pos = playable[noteIndex]!.cands[s.candidate]!;
const reach = pos.fret === 0 ? 0 : pos.fret - s.anchor;
let cost = o.heightCost * s.anchor + o.reachCost * reach;
if (pos.fret === 0) cost -= o.openBonus;
return cost;
};The comment a few lines below it states the model in one sentence: “only the hand moving costs
anything; staying in position is free.” heightCost prices the hand’s anchor position,
reachCost prices how far a note sits from it, and shifting between anchors elsewhere in the
search is what actually costs — not the fingering above.
Results
The open question going in was how far @spotify/basic-pitch —
a general-purpose, off-the-shelf pitch model, not built for guitar specifically — could get on
an instrument where the same note usually has more than one right answer for where to play it.
| Metric | Value |
|---|---|
| Fret-assignment accuracy | 54.5% → 68.5% after two fixes |
| Bass F1 (synthetic) | ~0.39 — not yet measured on real bass recordings |
A specific, counterintuitive result is documented rather than glossed over: sustained single notes — bends, vibrato — fragment worse than fast chord strums, the opposite of what “more notes at once should be harder” would predict.
Limitations
- Explicitly an experimental beta, with known accuracy problems.
- Bass transcription is the weakest measured area and hasn’t been verified on real bass recordings at all — an open question, not just a rough edge.
- The recording path has real, un-fixed accuracy costs: no input-level normalisation before the model’s fixed thresholds, and mic recording goes through a lossy codec then back.
- Manual tempo entry is the default. Automatic detection exists and works from the notes alone, but can’t always tell a tempo from its double or half.
basic-pitchis general-purpose and wasn’t built for guitar — weakest at exactly what guitar does a lot of: the same pitch on more than one string, bends, distortion.
Work in progress
Real-audio bass accuracy — currently a blind spot, not just a weak spot.
Raw audio capture (skipping the lossy codec round-trip) and input-level normalisation are both reasoned through, not yet built. Tap-tempo and audio-based tempo detection are on the list.
A guitar-specific model, or a simpler single-note pitch tracker for solo lines, is being considered as a way past the current model’s ceiling — not decided yet.
The tab renderer itself — drawing and editing a fretted-instrument staff from a plain list of notes — doesn’t depend on anything Tabfier-specific. Worth its own small library if a second use for it ever comes up.