I created song book (well two actually)
2026-01-27
So I wanted to create a song book. Why? Well a group that I'm a part of already had a song book, great! But I wanted to create my own "fork" if you will. With songs that were in previous iterations of this song book but I also wanted to add my own favourite songs.
My issue with it was that it was made in Microsoft Word a there were multiple issues with it. If I put aside the inconsistent typesetting, my main issue was that the chord alignment was bad and adding songs to it would be very labor intensive.
So what I really wanted, was a songbook that would accomplish the following:
- Could be typeset the way I wanted
- Chord alignment was handled automatically without user specifying spacing and whatnot
- Easily add songs without too much hassle
Now at that point I hadn't even though about my requirements. I just wanted my own song book that was better that the one before.
OpTeX
And so it happened that right around that time I came across OpTeX. From the OpTeX website OpTeX is a LuaTeX format (LuaHBTeX format since 2025) based on Plain TeX macros (by Donald Knuth).
It's basically a bunch of macros built on top of TeX the same way that LaTeX is (as far as my understanding goes) but it aims to be much simpler and tries to not obfuscate the ways TeX works from the user.
I'm not going to go into the details of OpTeX or it's advantages or disadvantages over LaTeX as I don't have enough knowledge and experience with either. What's important is that I though that it was cool and wanted to play with it.
At the start I was pretty much lost. Knowing little TeX apart from some basics from my limited LaTeX usage, I was pretty much lost. But to my rescue came csongbook! Marcos for typesetting songs with chords and an example of the whole song book! Now this didn't do exactly what I wanted but it gave me reference which I could work from.
I created my own set of macros (that were sometimes just more readable rewrites of the ones provided) and with them I created my first song book!
It was... fine. I could typset chord nicely and
adding new songs wasn't too bad.
One cool feature I managed to add thanks to OpTeX being
base on LuaTeX was customizable transposition but
the developer experience was horrible especially
when I had to handle hashes (#) for chords was very frustrating.
And to be honest it took me quite a while to port all of the song from the old song book over to the new one. And while it probably wasn't as frustrating as doing it in Microsoft Word, it wasn't exactly painless either. Every chord had to be a macro call and the chords woudn't be even automatically spaced, you had to specify the spacing manually!
This was an improvement but not as good as I had hoped.
Returning to the requirements (which I hadn't formulated yet at that point):
- I had great control over the typesetting even if the typesetting itself wasn't exactly easy.
- Chord spacing was still a pain
- Writing songs wasn't as easy as I had hoped
Typst
After I had finished the first version in OpTeX I wanted to add the chord spacing but it turns out that I'm terrible at TeX and was pretty much lost as to where I could start. And same as before, at some point I discovered a new cool thing! This time it was Typst.
Typst is a powerful typesetting system with a pretty good language desing that greatly improves over the status quo (that being TeX/LaTeX). It isn't without it's issues but in a lot of areas it's already miles better than (La)TeX.
So of course I decided to rewrite my song book in Typst! Again without much of a plan :D. I found a couple packages that did something simmilar but not exactly what I wanted. So I took inspiration from the one that worked mostly like I wanted and made my own version. This was much easier that my frist try with OpTeX. Partly because I knew what I was going for but mainly because Typst provides a much better developer experience and is way more intuitive (once you get to know the building blocks properly).
And the result looks pretty good!
Chord spacing works pretty well (with some minor caveats)
and implementing chord transposition is very easy!
Porting the songs over wasn't exactly easy
but nothing that a bunch of sed scripts
stitched together couldn't handle.
Okay! Problem solved then? Well... Not really.
- I have great control over the typesetting and it's easy to tweak too!
- Chord spacing works pretty (apart from some edge cases)
- Writing songs is probably as hard as before.
I have a nice song book which looks good and works well but adding to it isn't exactly easy :(
Custom format
This is when I am now. Seeing as using existing typsetting systems directly won't give me the qulities I want I decided to create my own format!
I want a format that is simple to parse yet extensible for when I want some specific typesetting options or other possible metadata attached. But first let's see what other formats are out there!
Chord Charts File Formats
One would think that I would've done this research before embarking on this journey. Well better late then never, so I'm doing this now!
As far as I can tell there are basically two formats.
The first one is the simplest format one could think of. Just write the text and then place character (chords) directly above the lyrics separated by a bunch of spaces for the right alignment.
This sort of works as it's probably the easiest to understand from a user's perspective but it's also very annoying to create and maintain. If I want to change anything I'll have to align all of the chords again!
The other widely used format is the ChordPro format. ChordPro is actually a program that can generate chord-over-lyrics style output based on the input given in their format. And other program seemingly adopted its format too!
ChordPro File format
The ChordPro format is actually quite simple!
Anything inside square brackets ([]) is treated
as a chord but it's also sometimes a shorthand
for a section(?).
Anyything inside curly brackets ({}) is then treated
as a directive with some key and optionally
a value separated from its key with a colon (:).
There are a bunch of predefined directives
such as {start,end}_of_{verse,chorus,bridge,...}
or other directives like transpose, title, comment
or image.
I like that lyrics and other metadata is separated however I don't really like the syntax of it. I also don't like the fact that you can't attach a chord to a specific string of text and you have to just sort of put it at the start which sometimes results in misleading chord placement.
I also don't like that one has to first start a verse and then end a verse. It would be great if this could be one block or perhaps could be defined implicitly based on the number of line endings the same way it's done in LaTeX or Markdown.
To be continued...
And this is where I'm now. I've tried to design a couple options and so far I don't like what I've got.
This is my current best version yet but I think this is going to need a lot more thought before I pull the trigger and start writing a parser for it.
@title Undisclosed Desires
@author Muse
@capo 3
@transpose -4
@meta.composer { David Baker }
@meta.description {
This is a long multiline description
of this song!
}
#[verse]
[C]I know you've suffered
But I [Emi]don't want you to hide
[C]It's cold and loveless
I won't [Emi]let you be denied
[C]Soothing
// Chords range can be specified
// with angled brackets
I'll [Emi]{make} you feel pure
And [C]trust me
[Emi]You can be sure
#[chorus.first] {
[Ami]I want to [C]reconcile the [Emi]violence in your [G]heart
[Ami]I want to [C]recognize your [Emi]beauty's not just a [G]mask
[Ami]I want to [C]exorcise the [Emi]demons from your [G]past
[Ami]I want to [C]satisfy the [Emi]undisclosed [G]desires in your [C]heart
}
The idea for this DSL is the following:
- Metadata begin with
@and are in the form of a path separated by dots followed by some content. The content can be either until the end of the line or is delimited with curly braces. - Block content (such as verse, chorus, possibly images, etc.)
start with
#, then a path surrounded by square braces for better visibility and then a block level content. This content is either delimited by curly braces or ends with the first empty line that is encountered by the parser - Chords are surrounded by square braces and can be joined to some specific text with curly braces.
All content is delimited by curly braces which is consistent across all of the constructs and feels nice to me.
I think the way metadata is structured is fine and I like that it's visibly different from other elements but I'm not sure if metadata should be strictly strings for setting stuff like title, transposition or font size or if they should function more like directives in ChordPro and be able to for example set it's content to bold.
It's nice because it's all metadata but I don't like the fact that one doesn't know what a compiler will do with a specific meta tag. It would be good for formatting meta tags such as bold to still cause parsers that don't support it to emit the text inside it even if it doesn't support the specified tag.
The best option would be to separate metadata tags from formatting tags but I haven't landed on a nice syntax yet.
I definitely don't want to change the syntax for chords as I really like the syntax and it preserves at least some compatiblity with songs written for ChordPro which will make porting songs over somewhat easier.
Closing thoughts
And that's it. I'll probably sit and work on this once in a while and I'll update this article when get to another version that'll be hopefully nicer with better defined syntax and semantics!