Fretdown

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

  1. Text-first. A Fretdown document is readable and writable by hand in any text editor. No required tooling, no binary blobs, no XML.
  2. Deterministic parse. Every valid document has exactly one interpretation. The grammar is unambiguous; there are no heuristics in the parser.
  3. Fretted-instrument-agnostic. Nothing in the core format assumes six strings or guitar tuning. Strings and frets are abstract; instruments supply defaults.
  4. 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

  1. File structure & metadata
  2. Instruments & tuning
  3. Measures & beats
  4. Notes & rhythm
  5. Techniques
  6. Song structure
  7. Worked example
  8. 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:

  1. A header of song-level directives (metadata + arrangement).
  2. 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

DirectiveArgumentExampleNotes
@titlestring@title "Smoke on the Water"Song title.
@artiststring@artist "Deep Purple"Performer/composer.
@albumstring@album "Machine Head"Optional.
@tempointeger@tempo 112Beats per minute.
@timefraction@time 4/4Time signature. Default 4/4.
@keykey@key EmTonal key, e.g. C, Em, F#, Bbm.
@capointeger@capo 2Song-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, @capo directives, 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 AG, 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).

idstringsdefault tuning (s_n → s1)frets
guitar6E2 A2 D3 G3 B3 E424
guitar77B1 E2 A2 D3 G3 B3 E424
bass4E1 A1 D2 G224
bass55B0 E1 A1 D2 G224
ukulele4G4 C4 E4 A418

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 notes6f3 (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 ≥ 1 and the track's string count.
  • f<N> — fret number, 0 ≤ N ≤ the track's fret count (24 by default). f0 is 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:

SuffixValue
:1whole
:2half
:4quarter
:8eighth
:16sixteenth
:32thirty-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.

ConnectorTechniqueExampleMeaning
hhammer-ons3f5h7fret 5, hammer on to 7
ppull-offs3f7p5fret 7, pull off to 5
/slide ups3f5/7slide from 5 up to 7
\slide downs3f7\5slide from 7 down to 5
bbends3f7b9bend fret 7 up to the pitch of fret 9
rreleases3f7b9r7bend 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). See DECISIONS.md.

Articulation flags

Dot-prefixed keywords attach to a note, after the fret chain and before any duration suffix. Multiple flags may stack.

FlagTechnique
.pmpalm mute
.vibvibrato
.harmnatural harmonic
.ghostghost note
.slapslap (bass)
.poppop (bass)
.taptapped note
.letlet ring
.stacstaccato
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):

DirectiveMeaning
@segnosegno sign
@codacoda sign
@fineend 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 11/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 21/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 11/4 + 1/8 + 1/8 + 1/4 + 1/4 = 1.

Bass verse, measure 21/4 + 1/8 + 1/8 + 1/2 = 1.

Every string reference is within range (guitar s1s6, bass s1s4), 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.