Designing SongScript: Parsing Chords with Zero Overhead

If you search for chord charts online, you will find a complete mess. Some sites place chord names on their own line directly above the lyrics. Others write them inline inside square brackets, like [Am]. Some use slash chords like C/E, and others include random tab markers, instructions, and tuning guides that have nothing to do with the actual song structure.

I wanted Bandroom to accept any of these formats. You copy the text, paste it into a box, click a button, and the song gets generated.

To make this work reliably on the client side, I built the SongScript Parser.

The Intermediate Format: SongScript

The biggest architectural decision was separating the visual layout from the playback schedule.

The user needs to see chords aligned with lyrics on the screen. The audio engine, however, only cares about the timeline: what chord is played at what bar, at what beat, and by which instrument.

To bridge this gap, I designed a custom JSON schema called SongScript (SST).

SongScript acts as our single source of truth. It stores metadata (BPM, key, time signature), the roster of band members, the text sections (Verse, Chorus), and a timeline score. The score maps chords to specific lyric segments and character offsets.

The SST Parser Pipeline

The parser is a pure TypeScript module that processes the pasted text line by line. It follows a simple, rule-based pipeline:

  1. Section Detection: It identifies lines matching /^\[.+\]$/ as section headers. Standard headers like "Verse 1" or "Chorus" are normalized.
  2. Chord Line Classification: It checks if a line consists entirely of valid chord tokens separated by whitespace. Using a regular expression, it identifies standard chord structures, including extensions, alterations, and slash chords (like Am/G).
  3. Lyric Line Association: If a chord line is immediately followed by a text line, the parser associates them. It calculates the character position of each chord in the chord line and maps it to the corresponding word offset in the lyric line.
  4. Inline Expansion: If it encounters inline chords like [Am], it splits the text, extracts the chords, and computes the alignment programmatically, converting the inline format to our standard mapped format.

Client-Side Transposition

By storing the chords as structured strings in SongScript rather than baked audio patterns, we get transposition for free.

When a user clicks the transpose button in Performance Mode, the engine does not perform pitch-shifting. Instead, a helper utility recomputes the chord root notes (adjusting for guitar capo configurations) and feeds the updated note array to the sampler instantly.

The parser works beautifully. It handles about 95 percent of chord charts pasted from major websites without any errors. For the remaining 5 percent (where text and chords are mixed in confusing ways), it flags the ambiguous lines and lets the user manually classify them before saving.

With the parser and Tone.js engine working locally, the final piece of the lab phase was figuring out how to handle saving and syncing.