Cantus Firmus
Cantus Firmus app icon

Cantus Firmus

Mapping the hidden threads in Bach's opus

Bach set the same hymn tunes again and again. A chorale melody closing a cantata is the subject of an organ prelude written twenty years earlier, and harmonised a third time in four parts — but nothing in a catalogue numbered by BWV ever tells you so. This app is built on those threads: every melody is a place you can stand, and everything that shares it is one tap away.

If you know these pieces, it is the connection map you have been keeping in your head. If you don't, it is a way in that isn't a numbered list — pull on any tune and see where it goes. The same thesis as the network map, built for a screen you hold in one hand: a force-directed graph does not read on a phone, so here every edge is a row you can tap.

Not all of Bach — not yet. This is the music the chorale melodies actually run through: the cantatas, the four-part chorales, the organ chorale settings, the passions, motets and oratorio, and the keyboard concertos that share movements with them. The rest of the instrumental catalogue comes later.

What it does

The connections come first, because they are the point. Everything else exists to get you to one.

Connections

Swipe from a movement to everything that shares its tune

Follow a link and you get every other setting of that melody, grouped by the tune itself — cantata movements, four-part chorales and organ preludes side by side.

Both directions of every edge are precomputed when the database is built, so the whole web is one tap deep from anywhere.

The related-works screen for BWV 85 movement 3, listing every work sharing Allein Gott in der Höh sei Ehr
Melodies

The cantus firmus as a first-class place

A tune is not a footnote to a work — it is the thing the works have in common, so each one gets its own screen: its Zahn number, and every work that sets it.

The corpus turns out not to be one web but sixty-seven separate families, and the app knows which one a work belongs to.

A single chorale melody with its Zahn number and every work that sets it
Works and movements

One line per work, until you want more

Search resolves 140, bwv 140 or a German incipit to a single row; tap it and the cantata opens into its movements, with type, key and scoring.

The chain-link badge is the whole point: it marks where in a work a shared melody lives.

BWV 140 opened to its seven movements, with a chain-link badge on the closing chorale
Today

Two different kinds of anniversary

The home screen opens on what Bach premiered near this date, and separately on what he wrote for the nearest Sunday in the church year.

Most of these feasts were movable, so the two are genuinely different facts — and the app refuses to blend them.

The Today screen, showing a work premiered around this day and, for a Saturday, the works written for tomorrow's 10th Sunday after Trinity
Discover

For when you don't yet have a question

Search can only give back what you already knew to name. Discover hands you a work drawn at random from the whole catalog, a melody worth pulling on, a recording to hear, and the anniversaries falling near today.

The random work is deliberately unfiltered, so it can land on the works that belong to no melody family — the ones nothing else in the app will ever show you.

The Discover tab: a randomly chosen work, a melody, a recording to play, and nearby anniversaries
Quiz

Questions built from the real data

Four rounds — melodies, the church year, chronology, and a mix — with the wrong answers drawn from the catalog itself, so every one of them is plausibly right.

The listening round is kept separate under Needs sound, because you should not discover that on a train.

The Quiz screen, with four text rounds and a separate listening round marked Needs sound

And the rest

Smaller things that make it usable rather than merely complete.

Listening

Around 1,600 MIDI renderings stream on demand, with a queue, lock-screen controls and background playback. Nothing is bundled, so the app stays small.

Apple Music

Anything you have favourited shows a heart, matched on the BWV number — the one thing an 18th-century catalogue and a record label both print. Your recordings are playable in the app, listed beside the catalog's own link rather than replacing it: they are different performances, and only you know which you meant.

Notes

Write on any work or movement. Your writing lives in a separate file that an app update never touches.

Corrections

Fix a wrong title or date. Corrections are an overlay applied when a row is drawn, never an edit to the catalog — so the original is always still there to revert to.

Export

Everything you have corrected exports as a CSV the upstream pipeline reads directly. A connection noticed on the sofa does not have to die there.

Offline

The whole catalog is a read-only SQLite file inside the app. No account, no first-run download, no network needed to browse.

Where the data comes from

One source of truth, three consumers. The app, the network map and an Alfred workflow are all built from the same CSVs by the same pipeline — and each of them reports the same content hash, so "are these showing the same data?" is a string comparison rather than a guess.

In the database

    How it is put together

    A Python pipeline merges the per-work metadata — titles, premiere dates, liturgical occasion, keys, scoring, reference links, quotes from Wolff, Gardiner and Schweitzer — with the chorale network CSVs, then precomputes every edge in both directions and the connected components of the melody graph.

    Fifty-two assertions run against the result, including spot checks traced by hand. A build that breaks one of them does not ship.

    How it is built

    SwiftUI on iOS 17, Swift 6 strict concurrency, and no third-party dependencies at all — the catalog is read through the raw SQLite C API. An actor owns the file; the interface is value-routed.

    The database is regenerated from source and replaced wholesale on every update, which is exactly why your notes are kept somewhere else.

    Testing the beta

    Cantus Firmus is a personal project and is not on the App Store. It is distributed through TestFlight, Apple's beta service — free, and there is nothing to buy at any point.

    1. Install TestFlight from the App Store. It is Apple's own app and it is what actually installs the beta.
    2. Open the join link on the iPhone itself, not on a Mac — it hands off to TestFlight, which then offers you Cantus Firmus.
    3. Tap Install. TestFlight tells you when a new build is ready, and updates in place.
    Join link coming soon

    The public TestFlight link is with Apple for beta review and will appear here shortly.

    Sending feedback. In TestFlight, take a screenshot and share it, or long-press the app icon and choose Send Beta Feedback — both reach me with the build number and device attached, which a screenshot mailed on its own does not. Anything at all is welcome: a wrong date, a melody that should be linked and isn't, a screen that reads badly.

    Needs iOS 17 or later. No account and no sign-in: the catalog is inside the app and browses offline. Recordings stream, so those want a connection, and the Apple Music features need an Apple Music subscription — everything else works without one. A TestFlight build expires 90 days after it is uploaded; installing the next one resets that, and your notes and corrections survive the update. The network map needs none of this — it is public, and runs in a browser.

    Thanks

    None of this is original research. The connections, the recordings and the liturgical calendar all come from people who published their work for others to use — two of them supply everything you can actually hear in this app.

    whichbachcantata.be

    Curated per-occasion playlists, and the model for tying cantatas to the liturgical calendar. The app's Apple Music links are matched from those playlists, movement by movement.

    suzumidi.com

    MIDI files for many cantata movements. Nearly every recording the app streams is rendered from them — without this, there is no sound in Cantus Firmus at all.