song2html
v2026.8.7
Published
Convert chord-lyric source text-files to formatted HTML for rendering song charts
Maintainers
Readme
song2html
Song charts are translated into HTML so they can be displayed and stylized in a web browser.
Example Input Format
Songs need to be specifically formatted as follows:
Title
author: John Doe, Jane Doe
key: C
tempo: 128
time: 4/4
owner: Example Music LLC
license: CC BY-SA 4.0
verses: A B C D E F
chorus: D E F A B C
bridge: G A B C D E
Sections:
Verse 1:
^Some lyrics ^are here
^carets are ^where chords ^go
^oh
Chorus:
^If the ^section name starts with the ^same word as the section in the ^chord chart ^(after the title)
it will use those chords in the ^section
Verse 2:
^For example, all ^verses will all
^use the same chords, and chord-naming is ^pretty flexible.
^You can even use ^Nashville numbering.
Bridge:
^Don't forget the colons, ^they're important.
And if you have more carets than chords, it will just start at the beginning of the chord chart and keep going.
Arrangements:
Live:
Verse 1
Chorus
Verse 2
Chorus
Bridge
Studio:
Verse 1
Verse 2
Chorus
Verse 1
Chorus
Bridge
ChorusFormat Specification
- There will always be a default arrangement which displays the sections in the order they are given.
- Parsing rules:
- Blank lines are ignored
- Windows line-endings (\r\n) and Unix line-endings (\n) are both supported.
- The very first line of the file is the song title.
- Everything indented beneath the title is metadata about the song.
- The following are some internally recognised song vars:
keytempotimeauthorowner(rendered as a copyright notice in every page footer)license(rendered in every page footer)- Other keys are presumed to be section names for chord charts.
- Any chords after the section-name
<song-section-name>:are assumed to be chords for that section- even if they span multiple lines. - Groups of chords in parentheses can be repeated by a following xN, where N is the number of times to repeat.
- e.g. (C G Am F)x2 would expand to C G Am F C G Am F
- Any chords after the section-name
- Segments
- Segments are defined by a name followed by a colon.
- Segments indented under the segment name belong to that segment.
- 2-space indentation is used to define segments & metadata.
- 4-space indentation is used to define content within song-sections and arrangements.
- Segment names are not case-sensitive; i.e. 'Arrangements' is the same as 'arrangements'.
- Segments names cannot be:
keytempotimeauthor
- The following are reserved segment names:
- Arrangements
- Sections
- Segements beneath 'Sections' are song sections.
- These segments can be named anything.
- If a section name starts with the same word as a section in the chord chart (after the title), it will use those chords in that section.
- e.g. 'Chorus' in the chord chart will be used in any section named 'Chorus', 'Chorus 1', 'Chorus A', etc.
- Carets within the song sections indicate where chords go.
- Chords are mapped from the corresponding chord chart metadata.
- If there are more carets than chords in a line of lyrics, it will loop back to the beginning of the chord chart and continue assigning chords.
- Segments beneath 'Arrangements' are arrangements.
- These segments can be named anything.
- Each line in an arrangement is the name of a song-section to include in that arrangement in the given order.
- If no arrangements are defined, the default arrangement is used (which is the sections in the order written.)
- Output is paginated
- First "page" is the title block, metdata, and chord chart
- Subsequent pages contain song sections
- Pages are separated after a certain number of lines (configurable in code)
Example Output
This is output of the example above
<article class="s2h-song">
<section class="s2h-page" data-page="1">
<section class="s2h-meta">
<h2 class="s2h-meta-title">Title</h2>
<p class="s2h-meta-key"><strong>Key:</strong> C</p>
<p class="s2h-meta-tempo"><strong>Tempo:</strong> 128</p>
<p class="s2h-meta-time"><strong>Time:</strong> 4/4</p>
<p class="s2h-meta-authors"><strong>Authors:</strong> John Doe, Jane Doe</p>
</section>
<section class="s2h-chords"><h3 class="s2h-chords-title">Chords</h3>
<p class="s2h-chord-line"><span class="s2h-chord-section-label">Chorus</span> <span class="s2h-chord">D</span> <span class="s2h-chord">E</span> <span class="s2h-chord">F</span> <span class="s2h-chord">A</span> <span class="s2h-chord">B</span> <span class="s2h-chord">C</span></p>
<p class="s2h-chord-line"><span class="s2h-chord-section-label">Chorus</span> <span class="s2h-chord">D</span> <span class="s2h-chord">E</span> <span class="s2h-chord">F</span> <span class="s2h-chord">A</span> <span class="s2h-chord">B</span> <span class="s2h-chord">C</span></p>
<p class="s2h-chord-line"><span class="s2h-chord-section-label">Bridge</span> <span class="s2h-chord">G</span> <span class="s2h-chord">A</span> <span class="s2h-chord">B</span> <span class="s2h-chord">C</span> <span class="s2h-chord">D</span> <span class="s2h-chord">E</span></p>
</section>
<section class="s2h-section s2h-section-verse-1">
<h3 class="s2h-section-title">Verse 1</h3>
<p class="s2h-lyric-line"><sup class="s2h-chord"></sup>Some lyrics <sup class="s2h-chord"></sup>are here</p>
<p class="s2h-lyric-line"><sup class="s2h-chord"></sup>carets are <sup class="s2h-chord"></sup>where chords <sup class="s2h-chord"></sup>go</p>
<p class="s2h-lyric-line"><sup class="s2h-chord"></sup>oh</p>
</section>
<section class="s2h-section s2h-section-chorus">
<h3 class="s2h-section-title">Chorus</h3>
<p class="s2h-lyric-line"><sup class="s2h-chord">D</sup>If the <sup class="s2h-chord">E</sup>section name starts with the <sup class="s2h-chord">F</sup>same word as the section in the <sup class="s2h-chord">A</sup>chord chart <sup class="s2h-chord">B</sup>(after the title)</p>
<p class="s2h-lyric-line">it will use those chords in the <sup class="s2h-chord">C</sup>section</p>
</section>
<section class="s2h-section s2h-section-verse-2">
<h3 class="s2h-section-title">Verse 2</h3>
<p class="s2h-lyric-line"><sup class="s2h-chord"></sup>For example, all <sup class="s2h-chord"></sup>verses will all</p>
<p class="s2h-lyric-line"><sup class="s2h-chord"></sup>use the same chords, and chord-naming is <sup class="s2h-chord"></sup>pretty flexible.</p>
<p class="s2h-lyric-line"><sup class="s2h-chord"></sup>You can even use <sup class="s2h-chord"></sup>Nashville numbering.</p>
</section>
<section class="s2h-section s2h-section-chorus">
<h3 class="s2h-section-title">Chorus</h3>
</section>
</section>
<section class="s2h-page" data-page="2">
<section class="s2h-section s2h-section-chorus">
<p class="s2h-lyric-line"><sup class="s2h-chord">D</sup>If the <sup class="s2h-chord">E</sup>section name starts with the <sup class="s2h-chord">F</sup>same word as the section in the <sup class="s2h-chord">A</sup>chord chart <sup class="s2h-chord">B</sup>(after the title)</p>
<p class="s2h-lyric-line">it will use those chords in the <sup class="s2h-chord">C</sup>section</p>
</section>
<section class="s2h-section s2h-section-bridge">
<h3 class="s2h-section-title">Bridge</h3>
<p class="s2h-lyric-line"><sup class="s2h-chord">G</sup>Don't forget the colons, <sup class="s2h-chord">A</sup>they're important.</p>
<p class="s2h-lyric-line">And if you have more carets than chords, it will just start at the beginning of the chord chart and keep going.</p>
</section>
</section>
</article>Chord Notation
- Notation
N- C or 1 - Major
- in this case 1 would be the root chord of the key
- Dm or 2 - Minor
- whether its minor depends on the key (2,3,6 are minor)
- C or 1 - Major
/- F/A or F/5 or 4/5 - bass note
- indicates a chord inversion with the A or 5 as the bass note
- numeric bass notes are scale degrees relative to the song key and render as notes without chord qualities
- F/A or F/5 or 4/5 - bass note
\- F\D or F\3 or 4\3 - treble note
- indicates a chord inversion with the D or 3 as the treble note
- in slash chords the numbers are relative to the chord root and rendered thusly
- F\D or F\3 or 4\3 - treble note
-- G-BCD or G-bcd or G-671 - chord melody
- the part after the dash is the melody notes
- melody notes are not translated/transposed- just displayed as-is
- G-BCD or G-bcd or G-671 - chord melody
|N#- A7 or 67 - position indicator
- a number after a chord indicates the position of the chord on the fretboard, or octave on piano
- A7 or 67 - position indicator
MCP tools and filesystem policy
Run the stdio MCP server with npm run start:mcp. Its parse/read tools support an optional include list so callers can omit generated HTML or source and conserve model context. render_html supports print, stage, compact, large-type, and dark themes.
The library now exposes a canonical Song AST for structured keys, chords, lyric/chord events, stable section identities, arrangements, source locations, and diagnostics. Parsing, validation, creation, transposition, rendering normalization, and MCP operations share this model. Deterministic importers support ChordPro, inline bracket notation, OpenSong XML, chords-over-lyrics text, and song2html itself. See AST.md for the schema, API, round-trip guarantees, importer behavior, and examples.
The MCP server also exposes detect_format and import_song. Importers preserve observed chord order and placement, report ambiguity instead of silently guessing, and retain source provenance. The legacy default songToHtml() API remains compatible; AST operations are named exports.
The file tools are intended for a trusted local MCP process. Set SONG2HTML_LIBRARY_ROOT=/absolute/path/to/library to confine all read, write, and list operations to one directory. Writes are atomic, refuse existing destinations unless overwrite: true, and refuse error-severity diagnostics unless allowInvalid: true; dryRun: true validates without writing.
npm test is read-only. Regenerate checked-in fixture HTML explicitly with npm run fixtures:update.
