Skip to content
אצלי
Go back

Music notation in Standard Notes: ABC, ChordPro and alphaTex

Stave editor inside Standard Notes, with the ABC source of a tune on the left and, on the right, the rendered score with melody, bass, chord symbols and lyrics

Also available in Portuguese: Notação musical no Standard Notes: ABC, ChordPro e alphaTex.

Standard Notes is a free, open source, end-to-end encrypted notes app that I have used since April 2024. Besides the default editor, it accepts custom editors, and I have been writing a few:

All of them share the same base: React 18 and TypeScript, built with Vite, the Standard Notes stylekit for the themes, Vitest and fast-check for unit tests and Playwright for end-to-end tests.

The fourth is Stave, a music notation editor. You write the music as text, and it draws the score, tablature or chord sheet next to it, plays it and exports it (SVG, PNG, MIDI, PDF, Guitar Pro). The rule is the same as in the other plugins: the note keeps only the text, so it stays readable without the plugin and is encrypted like the rest of your data.

Stave reads three text formats: ABC, ChordPro and alphaTex. They look like they do the same job, but each was made for a different kind of musician, and that is what this post is about.

Table of contents

Open Table of contents

Installing Stave

  1. In Standard Notes (web or desktop), open Preferences, then Plugins.

  2. At the bottom of the page, paste the URL below into Install Custom Plugin:

    https://esli.cafe/StandardNotes-Stave/ext.json
  3. Create a note, open the Editor menu and pick Stave.

You do not have to pick the format: Stave detects it from the text. If the first line starts with X:, it is ABC; lines with tags such as \title mean alphaTex; directives such as {title:} or lyrics with [chords] mean ChordPro. An empty note offers a template for each, and the Help menu has a cheat sheet for all three.

ABC

Stave with a tune in ABC. On the left, the source with the X, T, M, L, Q and K header, two voices, Melody and Bass, and a line of lyrics. On the right, the score on two staves, treble and bass clef, with chord symbols above, dynamics, a trill, repeats with first and second endings and the lyrics under the melody

Origin

ABC was created by Chris Walshaw in the early 1990s to write down traditional tunes, mostly Irish and Scottish, in a way that would fit in an email or a Usenet post. The idea was that a musician could read the text without any software at all. It worked: tens of thousands of folk tunes are published in ABC today, and the current version of the standard is 2.1. In Stave, drawing and playback are done by abcjs, by Paul Rosen.

Syntax

An ABC file has a header of one-letter fields followed by a colon, then the body with the notes:

In the body:

Example

X:1
T:Morning on the Stave
M:4/4
L:1/8
K:G
|: "G"G2 BG dGBG | "C"c2 ec "G"B2 dB | "Am"A2 cA "D"F2 AF | "G"G4 z4 :|

A tune in G major, 4/4, four bars with a repeat and a chord on each strong beat. You can read it and hum it without rendering anything, which is the whole point.

The same ABC tune in the dark theme. The source on the left has the header fields, voices and decorations highlighted in color, and the score on the right stays on a light background, with melody, bass, chord symbols and lyrics

ChordPro

Stave with a song in ChordPro. On the left, the title, subtitle, artist, key, capo, tempo and time directives, followed by verses and a chorus with chords in brackets inside the lyrics. On the right, the song sheet with the title, the song facts and each chord in blue above the syllable it falls on

Origin

ChordPro comes from the chord program, written by Martin Leclerc and Mario Dorion in the early 1990s to print songbooks from text files. The format spread through chord and stage apps and became a de facto standard. Today it is maintained by Johan Vromans, who rewrote the reference tool and publishes the specification at chordpro.org.

Its focus is different: there are no notes and no rhythm, only lyrics and chords. It is the format of someone playing guitar in a circle of friends, not of someone reading a score. That is why Stave draws and transposes ChordPro but does not play it: a chord sheet does not say how long anything lasts.

Syntax

There are only two elements:

The most common directives:

Lines starting with # are file comments and are not shown.

Example

{title: Morning on the Stave}
{key: G}
{capo: 2}

{start_of_verse: Verse 1}
[G]Morning comes on the [C]stave, [G]light on every [Am]line
{end_of_verse}

{start_of_chorus}
And the [C]tune goes [G]round a-[Am]gain, [D7]
{end_of_chorus}

Without the plugin, the text is still a readable chord sheet. With it, the chords move above the lyrics, and transposing changes every chord and the {key} at once, bass notes of chords such as D/F# included.

The same song in ChordPro in the dark theme. Directives in blue and chords in yellow in the source on the left; on the right, the sheet with the title, the song facts, Verse 1, the chorus marked by a bar on the left, Verse 2 and the bridge with an italic comment

alphaTex

Stave with the same tune in alphaTex, dark theme. On the left, the title, tempo, ts and ks tags, the Melody track with instrument, staff and lyrics, and the notes in fret.string.duration form with effects in braces. On the right, score and tablature for three tracks: melody, rhythm guitar with chords and a 4-string bass

Origin

alphaTex is the text format of alphaTab, an open source library created by Daniel Kuschny to draw and play scores and tablature in the browser. alphaTab was made to open Guitar Pro files, and alphaTex is the way to write the same thing by hand. That is why it thinks like a guitarist: instead of naming the note, you say which fret and which string it is on.

It is the most complete of the three for string instruments: several tracks, their own tunings, techniques such as bends, slides, hammer-ons and palm muting. In Stave, it also imports MusicXML and Guitar Pro 3 to 7 files and exports to Guitar Pro 7.

Syntax

Metadata are backslash tags:

Notes follow the fret.string.duration form:

Example

\title "Morning Riff"
\tempo 96
\track "Guitar"
\staff {score tabs}
0.6.8 0.6 3.6 0.6 5.6.4 3.6.8 5.6 | (0.6 2.5 2.4).4{ch "E5"} 7.3{b (0 4)}.4 r.2
\track "Bass"
\tuning (G2 D2 A1 E1)
\staff {tabs}
0.4.4 0.4 3.4 0.4 | 0.4.2 r.2

Two tracks: a guitar with a riff on the low string, an E power chord and a whole-step bend, and a 4-string bass along with it. It is less readable than ABC for someone who does not play, but for anyone who already reads tablature the translation is direct.

The same alphaTex tune in the light theme. On the right, the melody in score and tablature, the rhythm guitar with strummed chords and arrows for the strum direction, and the bass in bass clef with its tablature, followed by the second line with a slide, a bend and the first and second endings

Which one to use

All three store music as text, but each answers a different question:

The full example of each format, in Stave’s File menu, is the same tune written in all three, which makes comparing them much easier.

How Stave was built

Stave is a single page written in TypeScript with React 18, built with Vite. Standard Notes loads it in an iframe, and the page talks to the app through the Standard Notes component relay: it receives the note, saves the text back (with a short delay that merges fast edits into one save) and keeps the layout preferences in the plugin’s own data, never in the note. The interface reuses the patterns of the Milkdown plugin, and every color comes from the Standard Notes theme variables, so all themes work without code of their own.

One editor, three engines

The core of the project is a TypeScript interface, Engine, and the UI only talks to the notations through it. Each engine:

A registry lists the three engines, and it is the only file that knows they exist. Each renderer is loaded with a dynamic import(), so Vite puts it in its own chunk: opening the editor loads React, the UI and CodeMirror, and someone who only writes ChordPro never downloads abcjs or alphaTab.

The ABC engine

ABC is drawn and played by abcjs 6, an MIT library written in JavaScript. renderAbc draws SVG, and its click listener gives the character offsets of each note, which is how a click in the score selects the text. Playback uses the abcjs synthesizer with TimingCallbacks to move the cursor, MIDI export uses getMidiFile, and transposition uses strTranspose, which rewrites the source and the key signature. The abcjs synthesizer fetches its samples from a CDN by default, so Stave ships the Fluid R3 GM piano samples in the build and points the synthesizer at them.

Verovio, a WebAssembly engraver with better looking output, was measured and left out: 2.3 MB gzip against 146 KB for abcjs, no support for several voices in its ABC importer, and no link from the drawing back to the source text.

The alphaTex engine

alphaTex runs on alphaTab 1.8, an MPL-2.0 library written in TypeScript. Its AlphaTexImporter parses the text and gives the diagnostics with their position; Stave wraps one step of the importer to note the source range of each beat, which is what makes clicks and the cursor highlight work. The score is drawn as SVG on the main thread with the Bravura music font. MIDI export goes through alphaTab’s MidiFileGenerator, Guitar Pro 7 export through its Gp7Exporter, and the MusicXML and Guitar Pro import goes through ScoreLoader and then AlphaTexExporter, which writes the alphaTex into the note. Transposition is Stave’s own code, since it has to move frets to another string when they leave the neck.

Playback was the hardest part. alphaTab plays through a worker (the synthesizer) and an audio worklet, but Standard Notes loads plugins in an iframe with an opaque origin, where a worker cannot start from the plugin’s URL. Vite builds alphaTab’s player as a single classic script with no imports; on the first Play the page fetches it and starts the worker from a blob of its text, then loads the Sonivox SoundFont.

The ChordPro engine

ChordPro has no library at all. ChordSheetJS, the usual JavaScript one, is GPL-2.0-only and cannot be combined with an AGPL-3.0 plugin, so Stave has its own parser in TypeScript. It reads the text line by line into a list of items (lyrics split into chord and syllable segments, directives, sections, comments, tablature), each with its source offsets, and reports unknown directives and unclosed chords or sections as errors. The renderer builds the sheet as HTML with DOM calls and puts the note’s text in through textContent, never as markup, so nothing in a note can inject code. Transposition shifts every chord, bass notes included, and the {key} directive.

Security, offline and tests

A Vite plugin adds a Content Security Policy to the build: scripts only from the plugin’s own origin, no inline code, no eval, connections only to the plugin’s origin and workers only from blobs. Fonts, SoundFonts and samples ship with the build, so the editor keeps working offline, like the app. Every dependency comes from the npm registry, and the component relay, which had a fix only on GitHub, is vendored in the repository with its license.

The tests use Vitest, Testing Library and fast-check for unit and property tests, and Playwright for end-to-end tests that load the plugin in a mock of the web and mobile apps, play music, check the CSP and run axe-core against WCAG 2.1 AA.

The current version is v1.2.0, AGPL-3.0 licensed. The code is at github.com/Esl1h/StandardNotes-Stave.


Share this post on:

Previous Post
Notação musical no Standard Notes: ABC, ChordPro e alphaTex
Next Post
Um cliente de API dentro do Standard Notes, com sintaxe .http e criptografia ponta a ponta