Fretdown Specification v0.1
Fretdown is a plaintext notation format for fretted instruments. It is to guitar and bass tabs what Markdown is to formatted prose: human-readable, diff-friendly, and unambiguous to parse.
This specification is v0.1 (draft). It is intentionally small and opinionated.
Design principles
- Text-first. A Fretdown document is readable and writable by hand in any text editor. No required tooling, no binary blobs, no XML.
- Deterministic parse. Every valid document has exactly one interpretation. The grammar is unambiguous; there are no heuristics in the parser.
- Fretted-instrument-agnostic. Nothing in the core format assumes six strings or guitar tuning. Strings and frets are abstract; instruments supply defaults.
- Validator-friendly. The format is designed so a validator can give precise, located diagnostics (line/column/length) — fret out of range, beats that don't fill a measure, references to sections that don't exist.
Document at a glance
# A line comment
@title "Sunshine Riff"
@artist "Fretdown Demo"
@tempo 120
@time 4/4
@key Em
@arrange intro verse
@track Guitar
@instrument guitar
@tuning E2 A2 D3 G3 B3 E4
intro:
|: s6f0:8 s6f0 s5f2 s6f0 s4f2 s6f0 s5f2h3 s5f2 :|x2
verse:
| (s4f2 s3f2 s2f2):4 _:4 s2f3.pm:8 s2f1:8 s1f0/3:4 |
| s3f5b7:4 s3f5:8 s3x:8 (s4f0 s3f0):2 |Spec contents
- File structure & metadata
- Instruments & tuning
- Measures & beats
- Notes & rhythm
- Techniques
- Song structure
- Worked example
- Formal grammar (EBNF)
File extensions
.fd (short) and .fretdown (long). Both are identical in content.
Comments
A # begins a comment that runs to the end of the line. Comments may appear on their own
line or trailing any line.
File structure & metadata
A Fretdown document has two regions:
- A header of song-level directives (metadata + arrangement).
- One or more track blocks, each introduced by
@track.
Directives begin with @ and occupy a single line. Order within the header is free, but
all header directives must appear before the first @track.
Metadata directives
| Directive | Argument | Example | Notes |
|---|---|---|---|
@title | string | @title "Smoke on the Water" | Song title. |
@artist | string | @artist "Deep Purple" | Performer/composer. |
@album | string | @album "Machine Head" | Optional. |
@tempo | integer | @tempo 112 | Beats per minute. |
@time | fraction | @time 4/4 | Time signature. Default 4/4. |
@key | key | @key Em | Tonal key, e.g. C, Em, F#, Bbm. |
@capo | integer | @capo 2 | Song-level capo fret. Optional, default 0. |
Strings are double-quoted. A quoted string may contain any character except an unescaped
double quote; use \" for a literal quote.
Arrangement directive
@arrange lists section labels in performance order. It is optional; if present, every
label it names must exist as a section in at least one track (validated).
@arrange intro verse chorus verse chorus outro
Track directive
@track <name> opens a track block. The name is an identifier (letters, digits,
underscores, hyphens) or a quoted string. Everything that follows — until the next
@track or end of file — belongs to that track:
- the track's
@instrument,@tuning,@frets,@capodirectives, then - one or more labeled sections.
@track Guitar
@instrument guitar
@tuning E2 A2 D3 G3 B3 E4
intro:
| s6f0:4 s5f2 s4f2 s6f0 |A track must declare a tuning (directly via @tuning, or indirectly via a known
@instrument that supplies a default tuning). See
Instruments & tuning.
Instruments & tuning
String numbering
Strings are numbered so that string 1 is the highest-pitched string and the highest
number is the lowest-pitched. For standard guitar, s1 is the high E and s6 is the low
E. This matches conventional tablature, where the top line of the staff is string 1.
@tuning
@tuning lists one pitch per string, ordered from the highest-numbered string (lowest
pitch) to string 1 (highest pitch) — i.e. low to high in pitch, which is how players
say tunings aloud.
@tuning E2 A2 D3 G3 B3 E4
# s6 s5 s4 s3 s2 s1
The number of pitches determines the track's string count. Pitches use scientific pitch
notation: a letter A–G, an optional accidental (# or b), and an octave number.
E2 A#2 Db3 G3 B3 E4
@instrument
@instrument <id> selects a known instrument, which supplies a default tuning and fret
count. If @tuning is also present it overrides the default tuning (the string count then
follows the explicit tuning).
| id | strings | default tuning (s_n → s1) | frets |
|---|---|---|---|
guitar | 6 | E2 A2 D3 G3 B3 E4 | 24 |
guitar7 | 7 | B1 E2 A2 D3 G3 B3 E4 | 24 |
bass | 4 | E1 A1 D2 G2 | 24 |
bass5 | 5 | B0 E1 A1 D2 G2 | 24 |
ukulele | 4 | G4 C4 E4 A4 | 18 |
If @instrument is omitted, @tuning is required and the fret count defaults to 24.
@frets
@frets <n> overrides the highest playable fret for the track (default 24). Fret numbers
outside 0..n are a validation error.
@capo
@capo <n> on a track shifts open-string pitches up by n frets for rendering/pitch
purposes. It does not change how frets are written (a capo'd 2nd-fret note is still f2).
Measures & beats
Sections
Within a track, music is organized into sections. A section begins with a label — an identifier followed by a colon — on its own line:
intro:
| s6f0:4 s5f2 s4f2 s6f0 |
verse:
| s4f2:8 s4f2 s3f2 s3f2 |
Section labels are identifiers (letters, digits, underscores, hyphens). A label is unique
within a track. The same label may recur across tracks (e.g. both Guitar and Bass have an
intro), which aligns those sections by name.
Measures
A measure is delimited by the bar character |. Beats live between bars, separated by
whitespace. A line may contain one or more measures and may wrap across lines; bars are the
only structural delimiter.
| s6f0:4 s5f2 s4f2 s6f0 | s6f0:4 s5f2 s4f2 s6f0 |
The sum of beat durations in a measure must equal the active time signature (validated).
For 4/4 the durations must total one whole note; for 6/8, six eighths; and so on.
Beats
A beat is one rhythmic event. It is one of:
- a note —
s6f3(see Notes & rhythm) - a chord — several notes sounded together:
(s4f2 s3f2 s2f2) - a rest —
_
Each beat may carry a duration suffix (:4, :8, :2.). When omitted, a beat inherits
the duration of the previous beat in the track; the first beat of a track defaults to a
quarter note (:4).
| s6f0:8 s6f0 s6f0 s6f0 s5f2 s5f2 s5f2 s5f2 | # eight eighth notes
Repeats
|: opens a repeated span and :| closes it. An optional xN after the close sets the
total number of times the span is played (default 2).
|: s6f0:8 s6f0 s5f2 s6f0 s4f2 s6f0 s5f2 s5f2 :|x4
Voltas (alternate endings)
A measure may be prefixed with a volta bracket [N] indicating it is played only on pass
N of the enclosing repeat. Multiple passes list multiple numbers: [1,2].
|: s6f0:4 s5f2 s4f2 s6f0 |
[1] s6f0:4 s5f2 s4f2 s4f0 :|
[2] s6f0:4 s5f2 s4f2 s6f2 |
The volta bracket attaches to the measure whose beats follow it (here, the alternate endings), up to the next barline.
Notes & rhythm
A single note
A note names a string and a fret:
s6f3
│ │ └─ fret 3
│ └─── (literal 'f')
└───── string 6
s<N>— string number,N ≥ 1and≤the track's string count.f<N>— fret number,0 ≤ N ≤the track's fret count (24 by default).f0is the open string.
A dead/muted note replaces the fret with x:
s6x # string 6, muted
Chords
Parentheses group notes that sound simultaneously as one beat:
(s4f2 s3f2 s2f2)
A chord's duration suffix follows the closing parenthesis: (s4f2 s3f2 s2f2):4.
Rests
A rest is _, with an optional duration: _:4 is a quarter rest.
Durations
A duration suffix is : followed by a note-value denominator:
| Suffix | Value |
|---|---|
:1 | whole |
:2 | half |
:4 | quarter |
:8 | eighth |
:16 | sixteenth |
:32 | thirty-second |
A trailing . dots the value (adds half its duration): :4. is a dotted quarter
(= quarter + eighth).
Durations carry over: if a beat omits its suffix it reuses the previous beat's duration.
The first beat of a track defaults to :4.
Tuplets
tN( … ) groups beats into a tuplet of N notes occupying the time normally taken by the
largest power of two less than N. The common case t3 is a triplet (3 in the time of 2):
t3( s6f0:8 s6f2:8 s6f3:8 ) # eighth-note triplet, total time = one quarter
t5 is a quintuplet (5 in the time of 4), t6 a sextuplet (6 in the time of 4), and so
on. Beats inside the group are written with their face-value durations; the validator
accounts for the tuplet ratio when checking that a measure is full.
Techniques
Techniques are written two ways:
- Connectors join fret events on the same string inside one note token.
- Articulation flags are dot-prefixed keywords appended to a note.
Connectors (same-string transitions)
A note token may chain several fret events on one string using a connector between fret numbers. The whole chain is a single beat.
| Connector | Technique | Example | Meaning |
|---|---|---|---|
h | hammer-on | s3f5h7 | fret 5, hammer on to 7 |
p | pull-off | s3f7p5 | fret 7, pull off to 5 |
/ | slide up | s3f5/7 | slide from 5 up to 7 |
\ | slide down | s3f7\5 | slide from 7 down to 5 |
b | bend | s3f7b9 | bend fret 7 up to the pitch of fret 9 |
r | release | s3f7b9r7 | bend up to 9, release back to 7 |
Chains compose left to right: s3f5h7p5 is hammer 5→7 then pull-off 7→5.
Bend semantics. A bend's target is written as the fret whose pitch the bend reaches (
b9= "bend up to the pitch you'd get at fret 9"), not a semitone count. The semitone interval is derivable (target − origin). SeeDECISIONS.md.
Articulation flags
Dot-prefixed keywords attach to a note, after the fret chain and before any duration suffix. Multiple flags may stack.
| Flag | Technique |
|---|---|
.pm | palm mute |
.vib | vibrato |
.harm | natural harmonic |
.ghost | ghost note |
.slap | slap (bass) |
.pop | pop (bass) |
.tap | tapped note |
.let | let ring |
.stac | staccato |
s2f3.pm:8 # palm-muted eighth on string 2, fret 3
s4f5.slap:4 # slapped quarter
s3f12.harm:2 # natural harmonic, half note
s5f7h9.vib:4 # hammer 7→9 with vibrato
Flag order is not significant. Unknown flags are a validation error.
Song structure
Fretdown separates sections (named blocks of music inside a track) from the arrangement (the order in which sections are performed).
Sections
Every block of music lives under a label (see Measures & beats).
Typical labels: intro, verse, chorus, bridge, solo, outro. Labels are free
identifiers; these names are conventions, not keywords.
Arrangement
The song-level @arrange directive lists section labels in performance order. It lets a
short document describe a full song without repeating the music.
@arrange intro verse chorus verse chorus bridge chorus outro
Validation: every label in @arrange must exist as a section in at least one track.
Navigation markers
Within a section, these standalone directives mark navigation points used by players and renderers. They are informational in v0.1 (they do not expand the arrangement):
| Directive | Meaning |
|---|---|
@segno | segno sign |
@coda | coda sign |
@fine | end point for D.C./D.S. al Fine |
chorus:
@segno
| s6f0:4 s5f2 s4f2 s6f0 |
@coda
| s6f0:4 s5f2 s4f2 s6f0 |
Repeats and alternate endings (voltas) are covered in Measures & beats.
Worked example
A complete two-track song — guitar and bass — exercising metadata, arrangement, repeats,
chords, rests, hammer-ons, slides, bends, palm mutes, dead notes, and mixed rhythms. This
is the canonical corpus fixture at fixtures/sunshine-riff.fd.
# Sunshine Riff — canonical Fretdown worked example
@title "Sunshine Riff"
@artist "Fretdown Demo"
@tempo 120
@time 4/4
@key Em
@arrange intro verse
@track Guitar
@instrument guitar
@tuning E2 A2 D3 G3 B3 E4
intro:
|: s6f0:8 s6f0 s5f2 s6f0 s4f2 s6f0 s5f2h3 s5f2 :|x2
verse:
| (s4f2 s3f2 s2f2):4 _:4 s2f3.pm:8 s2f1:8 s1f0/3:4 |
| s3f5b7:4 s3f5:8 s3x:8 (s4f0 s3f0):2 |
@track Bass
@instrument bass
@tuning E1 A1 D2 G2
intro:
|: s4f0:8 s4f0 s4f0 s3f2 s4f0 s4f0 s3f2 s3f0 :|x2
verse:
| s4f0:4 s4f0:8 s4f0:8 s3f2:4 s3f0:4 |
| s4f3:4 s4f3:8 s4f2:8 s4f0:2 |How each measure fills 4/4
Guitar intro — eight eighth notes. The first beat sets :8; the rest inherit it.
8 × 1/8 = 1 whole note. The s5f2h3 beat is a single eighth that hammers fret 2 → 3.
Guitar verse, measure 1 — 1/4 + 1/4 + 1/8 + 1/8 + 1/4 = 1. Opens with a three-note
chord, then a quarter rest, two palm-muted eighths, and a quarter that slides f0 → f3.
Guitar verse, measure 2 — 1/4 + 1/8 + 1/8 + 1/2 = 1. A quarter that bends f5 up
to the pitch of f7, an eighth, a dead-note eighth, then a half-note dyad.
Bass intro — eight eighths, 8 × 1/8 = 1.
Bass verse, measure 1 — 1/4 + 1/8 + 1/8 + 1/4 + 1/4 = 1.
Bass verse, measure 2 — 1/4 + 1/8 + 1/8 + 1/2 = 1.
Every string reference is within range (guitar s1–s6, bass s1–s4), every fret is
≤ 24, and both @arrange labels (intro, verse) exist in both tracks.
Formal grammar (EBNF)
This is the reference grammar for Fretdown v0.1. It is descriptive; the canonical
implementation is @fretdown/core (a Chevrotain lexer + parser). Whitespace within a line
and #-to-end-of-line comments are insignificant. Newlines terminate directive lines and
section labels; elsewhere they are insignificant.
document = header , { track } ;
header = { metaDirective | arrangeDirective } ;
metaDirective = "@title" string
| "@artist" string
| "@album" string
| "@tempo" integer
| "@time" fraction
| "@key" word
| "@capo" integer ;
arrangeDirective = "@arrange" , label , { label } ;
track = "@track" , ( word | string ) ,
{ trackDirective } ,
{ section } ;
trackDirective = "@instrument" word
| "@tuning" , pitch , { pitch }
| "@frets" integer
| "@capo" integer ;
section = label , ":" , { sectionItem } ;
sectionItem = navMarker | measureGroup ;
navMarker = "@segno" | "@coda" | "@fine" ;
measureGroup = [ "|:" ] , measure , { measure } , [ "|" | repeatClose ] ;
repeatClose = ":|" , [ "x" integer ] ;
measure = [ volta ] , "|" , { beat } ;
volta = "[" , integer , { "," , integer } , "]" ;
beat = ( note | chord | rest ) , [ duration ] ;
chord = "(" , note , { note } , ")" ;
rest = "_" ;
tuplet = "t" integer , "(" , beat , { beat } , ")" ;
note = stringRef , fretChain , { articulation } ;
stringRef = "s" , integer ;
fretChain = ( fret | "x" ) , { connector , integer } ;
fret = "f" , integer ;
connector = "h" | "p" | "/" | "\" | "b" | "r" ;
articulation = "." , artKeyword ;
artKeyword = "pm" | "vib" | "harm" | "ghost" | "slap"
| "pop" | "tap" | "let" | "stac" ;
duration = ":" , noteValue , [ "." ] ;
noteValue = "1" | "2" | "4" | "8" | "16" | "32" ;
label = letter , { letter | digit | "_" | "-" } ;
word = letter , { letter | digit | "_" | "-" | "#" } ;
pitch = ( "A" … "G" ) , [ "#" | "b" ] , integer ;
fraction = integer , "/" , integer ;
string = '"' , { character } , '"' ;
integer = digit , { digit } ;
Lexical notes
A note atom (stringRef fretChain articulation*) is lexed as a single token and then
decoded by a dedicated, separately tested routine; this keeps the document grammar small
and free of single-letter token ambiguities. See DECISIONS.md.
Token disambiguation relies on a note atom always beginning with s followed by a digit,
so identifiers (section labels, track names, instrument ids, keys, pitches) never collide
with notes.