Appearance
Loom DSL Specification
Top Level
ebnf
Song = [ Frontmatter ] { Track } ;
Frontmatter = "---" , newline , yaml_content , "---" , newline ;
Track = TrackHeader , { Line } ;
TrackHeader = "#" , space , name , ":" , space , channel , [ space , "x" ] , newline ;Lines
ebnf
Line = CommentLine | InitLine | PatternLine | SeqLine | EmptyLine | TrackWrap ;
CommentLine = ">" , { character } , newline ;
InitLine = "##" , space , InitCommand , newline ;
EmptyLine = { space } , newline ;
TrackWrap = "---" , newline ;
PatternLine = RowHeader , Bar , Block , { Bar , Block } , Bar , [ space , CommentLine ] ;
SeqLine = "seq" , space , Bar , SeqBlock , { Bar , SeqBlock } , Bar , [ space , CommentLine ] ;Patterns and Tokens
ebnf
RowHeader = NoteList | DrumName | MidiNote ;
NoteList = NoteName , { "," , NoteName } ;
NoteName = ( "a"..."g" | "A"..."G" ) , [ "#" | "b" ] , digit ;
MidiNote = digits ;
DrumName = "bd" | "kick" | "sn" | "snare" | "rs" | "rim" | "cp" | "clap"
| "hh" | "hc" | "hihat" | "oh" | "ho" | "hp"
| "cr" | "crash" | "rd" | "ride" | "splash" | "china"
| "ht" | "mt" | "lt" | "ft" | "cb" | "tamb" ;
Bar = "|" | "|:" | ":|" | ":|:" ;
Block = { Token | space } ;
Token = NoteOn | Rest | Sustain | Group ;
NoteOn = "^" ;
Rest = "." ;
Sustain = "-" ;
Group = "[" , { Token | space } , "]" ;
SeqBlock = { SeqToken | space } ;
SeqToken = SeqNote | Rest | Sustain | SeqGroup ;
SeqGroup = "[" , { SeqToken | space } , "]" ;
SeqNote = NoteName | Chord ;
Chord = NoteName , "," , NoteName , { "," , NoteName } ;Notes:
NoteNameis case-insensitive (for example,c4andC4).MidiNotemust be0..127.DrumNameis case-sensitive.seqis a sugar syntax: per-token notes/chords are written directly in the grid.seqcurrently requires explicit octave in each note (for exampleC4).- For independent sustain per voice, use the standard pattern grid (
^ . -) across separate rows.
Init command forms:
pc <0..127>/sound <0..127>bank <msb>/<lsb>cc <controller 0..127> <value 0..127>pan <0..127>/volume <0..127>/expression <0..127>/mod <0..127>/sustain <0..127>
Whitespace handling note:
- Parsing is whitespace-tolerant around init/header tokens.
- Canonical spacing (for example
# Track: 1,## pc 4) is defined by the formatter, not by the language acceptance rules.
Modifier Lines
A modifier line adjusts per-token properties (for example velocity, pitch) for the immediately preceding pattern line.
ebnf
ModifierLine = ModifierKind , space , Bar , { ModifierEntry | space } , { Bar , { ModifierEntry | space } } , Bar ;
ModifierKind = "v" | "p" ;
ModifierEntry = ModifierValue | ModifierGroup | ModifierEmpty ;
ModifierValue = [ "!" ] , [ "+" | "-" ] , digits ;
ModifierNoteList = [ "+" | "-" ] , digits , "," , [ "+" | "-" ] , digits , { "," , [ "+" | "-" ] , digits } ;
ModifierGroup = "[" , { ModifierEntry | space } , "]" ;
ModifierEmpty = "." ;v(Velocity): Absolute value (0..127). Default100.p(Pitch): Relative semitone offset (+N/-N). Default0.!(Latch): Value persists across later empty slots..(Empty): Explicit empty slot (uses latch or default).- Empty slot: Uses latch value if active, otherwise default.
[...](Group): Aligns sub-values 1:1 with pattern group sub-tokens.- Scalar at group position: Broadcast to all leaves of that group.
100,80note-list value is supported forseqchord tokens (length must match chord size).
Templates
Templates define reusable sequences expanded in tracks.
Template Definition
ebnf
Template = TemplateHeader , { Line } ;
TemplateHeader = "#" , space , "@" , name , newline ;Template Expansion
ebnf
TemplateLine = TemplateExpansion , { TemplateExpansion } , newline ;
TemplateExpansion = "[" , "@" , name , { space , TemplateParam } , "]" , [ "*" , digits ] ;
TemplateParam = Transpose | StructuralRepeat | TimeScale | Macro ;
Transpose = ( "+" | "-" ) , digits ;
StructuralRepeat = "x" , digits ;
TimeScale = "/" , digits ;
Macro = "rev" | "arp" | "strum" | "vel:" , digits | "pan:" , digits ;Rules:
- Multiple expansions on the same line are processed sequentially (
[@a][@b]). - Expansions on different lines in the same section are parallel by line semantics.
+N/-N: pitch transposition.xN: structural repeat within the same grid span./N: time scale (compress duration to1/N).*N: repeat the expanded sequence.- Macros:
revarpstrumvel:Npan:N
Lexical Rules
newline: line ending (\n,\r\n)space: horizontal whitespaceyaml_content: valid YAML stringname: track/template namechannel: MIDI channel (1..16)character: any UTF-8 character except newlinedigit:0..9
Symbol Table (Auto)
Note=>^- Note glyphRest=>.- Rest glyphSustain=>-- Sustain (tie) glyphBarStandard=>|- Standard bar lineBarRepeatStart=>|:- Repeat start bar lineBarRepeatEnd=>:|- Repeat end bar lineBarDouble=>:|:- Double bar line / Section boundaryTrackHeader=>#- Track header start symbolTrackHeaderSeparator=>:- Track header separator (name:channel)TrackHeaderMute=>x- Track header mute flagComment=>>- Comment start symbolTrackWrap=>---- Track wrap / Frontmatter boundaryGroupStart=>[- Group startGroupEnd=>]- Group endModVelocity=>v- Velocity modifier selectorModPitch=>p- Pitch modifier selectorModLatch=>!- Modifier latch flag (!)Positive=>+- Modifier relative positive value (+)Negative=>-- Modifier relative negative value (-)Template=>@- Template prefix symbol (@)
Track Wrapping
--- in a track body works as a continuation marker.
loom
# Track: 1
C3 | ^ - - | ^ . . |
---
C3 | ^ ^ ^ | ^ ^ ^ |Equivalent timeline:
C3 | ^ - - | ^ . . | ^ ^ ^ | ^ ^ ^ |
Global Configuration
See Global Config.