Jon Latane | 09.27.17 | Code Development

Topologica: Your Event-Sourced, Flux-Driven Dream Code


Discover Topologica  – an instrumental app  allowing anyone with an Android phone to intuitively traverse orbifolds to make music without underlying mathematical or musical theory.

The idea sparked while reading Dmitri Tymoczko’s A Geometry of Music. I became interested in the concept of orbifolds and the increasingly popular Flux design patterns in software engineering – particularly when combined with event sourcing toolkits.

With a classical background, I was inspired to study jazz piano and the development of novel chord analysis and identification algorithms with ChordCalc.  Reinforcing my education, I combined these ideas into a domain-specific language. Based on familiar concepts like major, minor, dominant, augmented and diminished chords, this allowed developers to programmatically define musical orbifolds.

Today, we go behind the making of Topologica and discover applications of a concept dating back to the 60s to see just how deeply it is influencing the newest trends in the development world!

Introduction to Orbifolds

What’s an orbifold? It’s a generalization of a manifold, and while we could go to great lengths to define and differentiate either term, let’s keep it simple:

  • A manifold is, essentially, any continuous service in a 1D, 2D, 3D or higher Euclidean space. In 3D space, some trivial examples would be a single plane, the surface of a sphere, or the outer surface of an infinitely long cylinder. Generally, a manifold will be defined as an equality relation of x, y, and z coordinates. Of course, there are more complex manifolds:

  • An orbifold does not require a Euclidean space; it is the result of a finite group of actions (orbits) on a discrete space. For instance, a basket that can hold 5 apples and treat its state as our space. It has finitely many possible states: in fact, there are only 6 possible states (0–5 apples). Now, suppose we allow 2 actions/orbits: you can add an apple (unless there are 5 already in the basket), or remove an apple (unless there are none). Now, from any starting state, we can reach any of the other 5 possible states. The orbifold consisting of these actions in this space resembles a line from 0 to 5, which is a manifold in 1D space!


The Bishop’s Orbifold

Next, we’ll briefly examine a 2-dimensional orbit. Suppose we have a lone bishop on a chess board. As we know, it can move diagonally in any direction, for any number of spaces:

There are four orbits/actions in the group of possible actions taken by the bishop: up-left, up-right, down-left and down-right. Most of you have probably noted that the bishop above can never land on a dark-colored space under this action; thus, the domain of spaces a bishop can reach is dependent upon where it started. This set of possible states given a starting point is known as the fundamental domain of the group of actions the bishop can take.

The Orbifold of 1-to-12 Note Chords

In traditional western music, we have a 12-note tonal system. We’ll define a chord as a root tone (chosen from the base 12) and 0–11 additional tones. This makes 24,576 different chords possible:

If we restrict this space to only 1-to-4 note chords, the size is reduced to 2,784:

Of course, many of these are not your standard major, minor, or dominant chord structures. In fact, the vast majority will probably sound bad! Jazz (and other) musicians make use of the concepts of tonic, subdominant and dominant to vastly simplify this space. Read on for more information (particularly the section on ii-V-I).

Topologica provides mobile-friendly, musician-readable views of orbifolds that are (for now) programatically defined, but will eventually be user-defineable.

The Chromatic Orbit

In Topologica’s Kotlin DSL, every chord is represented by a root and an extension. The following orbit will take any chord and either raise it or lower it by a half step, moving every note of the chord up and down along the chromatic scale:

object Chromatic : Orbit {
    override fun forward(c: Chord) = Chord(c.root + 1, c.extension)
    override fun back(c: Chord) = Chord(c.root - 1, c.extension)
}

The “back” direction is unique to Topologica’s design and a fundamental component of why it can make browsing through an orbifold so intuitive. In general, mathematical orbits do not need to have a reverse direction.

This orbit is cyclic, meaning that if we keep going forward we will eventually land on the same chord — in exactly 12 steps. Moreover, given any starting chord, the Chromatic Orbit can reach 11 other chords, meaning its fundamental domain is always of size 12.

Music Theory Crash Course: The Diminished, Minor, Dominant, Major, Augmented Orbit

Most musicians can recognize 5 types of chords: Diminshed, Minor, Dominant, Major and Augmented. It’s crucial for any musician to understand what each of these chords sounds like, so here’s a video introduction that will also introduce the basics of how to use Topologica:

For a quick reference, the 5 primary “flavors” of chords we’ll concern ourselves with are:

  • Major: Root, Major 3rd (4 half steps up), Perfect 5th (3 half steps up), Major 7th (4 half steps up). Written as CM7 for the Major 7 chord, or just C for the standard Major chord.
  • Minor: Root, Minor 3rd (3 half steps up), Perfect 5th (4 half steps up), Minor 7th (3 half steps up). Written as Cm7 for the minor 7 chord, or just Cm for the standard minor chord.
  • Dominant: Root, Major 3rd (4 half steps up), Perfect 5th (3 half steps up), Minor 7th (3 half steps up). Written as C7; the seventh is mandatory for the chord’s flavor to be dominant, although in the context of a musical key, a standard major chord can function as a dominant chord.
  • Augmented: Root, Major 3rd (4 half steps up), Augmented 5th (4 half steps up), Major 7th (3 half steps up). Written as C7#5, or as C(#5) if the 7th is omitted.
  • Diminished: Root, Minor 3rd (3 half steps up), Diminished 5th (3 half steps up), Minor 7th (4 half steps up). Written as Cm7b5, or as Cmb5 if the 7th is omitted.

To make navigating between chords easier, I’ve defined the following orbit in Topologica:

object DimMinDomMajAug : Orbit {
    override fun forward(c: Chord): Chord = when {
            c.isDiminished -> c.replaceOrAdd(6, 7)
            c.isMinor -> c.replaceOrAdd(3, 4).replaceOrAdd(11, 10)
            c.isDominant -> c.replaceOrAdd(10, 11)
            else -> c.replaceOrAdd(7, 8)
        }

    override fun back(c: Chord): Chord = when {
        c.isAugmented -> c.replaceOrAdd(8, 7)
        c.isMinor -> c.replaceOrAdd(7, 6)
        c.isMajor && !c.isDominant -> c.replaceOrAdd(11, 10)
        else -> c.replaceOrAdd(4, 3)
    }
}

Put simply: given any input chord, this orbit will move you to a Diminshed, Minor, Dominant Major or Augmented chord when moving it forward and back along the orbit. A diminished chord cannot go backward and an augmented chord cannot go forward, meaning this orbit is non-cyclic. If we were to plot the AugDomMajMin orbit against the Chromatic orbit, we would have one cyclic orbit and one non-cyclic orbit.

Topologically, this orbifold would resemble the manifold of a cylinder with a height of 5 units (along the DimMinDomMajAug axis) and a circumference of 12 units (along the chromatic axis). Because this orbit explicitly does not affect the root of chords it acts upon, and the chromatic orbit only acts on the root, it is impossible for us to arrive somewhere else on this orbit by moving on the chromatic orbit or vice-versa. This is not always the case, as we will see below.

The ii-V-I Orbit

The ii-V-I progression is perhaps the most fundamental progression in all of jazz, and perhaps of music in general. Its brilliance lies in the fact that each chord has a different “flavor” as discussed above (ii is minor, V is dominant, and I is major), and that the progression involves moving in descending fifths (V is a fifth below ii, and I is a fifth below V). Here’s a quick rundown of what the progression sounds like — you will likely be able to match this sound up with many, songs you know!

Let’s take a look at the ii-V-I (“two, five, one”) progression in Topologica’s Kotlin DSL:

object TwoFiveOne : Orbit {
 override fun forward(c: Chord) = when {
   c.isMinor -> Chord(c.root - 7, Dom7)
   c.isDominant -> Chord(c.root - 7, Maj7)
   else -> Chord(c.root + 2, min7)
  }

 override fun back(c: Chord) = when {
   c.isDominant -> Chord(c.root + 7, min7)
   c.isMinor -> Chord(c.root - 2, Maj7)
   else -> Chord(c.root + 7, Dom7)
 }
}

In the “forward” direction:

  • Any minor (or diminished) chord will be moved to a dominant a fifth below it (which is the same as a fourth above it, and is the ii-V part of the progression).
  • Any dominant chord will be moved to a Major (7) chord a fifth below it (the V-I part of the progression).
  • Any other chord (generally major) will be raised by a whole step and given the notes of a Minor 7 chord (I-ii).

You may notice that in this case, the reverse direction is not a perfect inverse of the forward direction. For instance, a CMaj7#5 would move forward to a Dm7, but going back from there would only yield a CMaj7, losing the #5.

Here’s how Topologica renders the ii-V-I, when I is the selected chord:

ii-V-I orbit, with I selected in C Major

Note that this orbit is also cyclic, with a length of 3, if you start on any Major 7, Minor 7, or Dominant 7 chord. Its fundamental domain has size 36 (or 37 if you start on any chord with a different extension than Major 7, Minor 7 or Dominant 7).

The Orbifold of the Chromatic and ii-V-I Orbits

Recall that an orbifold is basically just two or more orbits. Now that we’ve defined two orbits in the space of musical chords, let’s look at the orbifold produced by their product. If we were to lay all of the possible chords out, it would look like this:

Note that both orbits are cyclic, and that:

  • Motion in the Chromatic directions won’t ever land you somewhere else in the ii-V-I directions
  • Motion in the ii-V-I directions won’t ever land you elsewhere in the Chromatic directions

In other words, with just these two orbits, we can now reach 36 different chords (12 major, minor and dominant chords)! We could actually tile the entire image over and over like a texture and it would be an accurate representation of where we can go from any point. Overall, this orbifold resembles a spherical manifold: just like the surface of the earth, if you go far east enough (forward on ii-V-I), you end up where you started, and if you go far north enough (and then south, but moving in the same direction — up chromatically), you’ll end up in the same spot!

The Alternating Major/Minor 3rds Orbit

In jazz and pop music, ii-V-I (and, to a lesser degree, IV-V-I) is a fundamental progression. But these three chords correspond to notions of tonic (the I), subdominant (the ii/IV — jazz musicians often prefer ii because it’s minor and thus sounds very different), and dominant (the V, which creates tension that makes the listener want to hear a I again). However, if we were to categorize every step of a major scale into one of these three boxes it may look like this (chords in C major are also given):

  • I (C) is tonic
  • ii (Dm) is subdominant and minor
  • iii (Em) is tonic and minor
  • IV (F) is subdominant and major
  • V (G or often G7) is dominant
  • vi (Am) is tonic and minor
  • vii° (B°, Bdim, or Bm♭5) is dominant and diminished

Generally we don’t need to do a lot harmonically with the time we’re on a dominant chord, as it’s already the most tense sound in most music. However, being able to move between our tonic and subdominant options is useful. With that in mind, we’ll take a look at one final orbit in Topologica:

object AlternatingMajorMinorThirds : Orbit {
  override fun forward(c: Chord) = when {
    c.isDominant -> c
    c.isMinor -> Chord(c.root + 3, Maj7)
    else -> Chord(c.root + 4, min7)
  }

  override fun back(c: Chord) = when {
    c.isDominant -> c
    c.isMinor -> Chord(c.root - 4, Maj7)
    else -> Chord(c.root - 3, min7)
  }
}

Suppose we’re in the key of C. This orbit sends a C chord forward to Em or backward to Am, an Am forward to C or backward to F (providing a two-chord route from tonic into subdominant territory), and an F chord backward to Dm (staying in subdominant territory). If we follow the Dm further, we end up on B♭ and have started to change the key, meaning the tonic-dominant relationship breaks down — but we can still write sequences that sound cool! If we take the orbit forward from C to Em then to G, we end up in dominant territory.

The Alternating Major/Minor 3rds Orbit is also cyclic, of length 24. From any given starting point, you can go forward or back 24 steps, hit every major and minor chord, and end where you started.

Here’s a quick video rundown so you can hear the musical effect of the Alternating Major/Minor 3rds Orbit:

What about an orbifold of ii-V-I and Alternating Major/Minor 3rds?

Recall that when constructing the Chromatic/ii-V-I orbifold above, we noted that:

  • Motion in the Chromatic directions won’t ever land you somewhere else in the ii-V-I directions
  • Motion in the ii-V-I directions won’t ever land you elsewhere in the Chromatic directions

These principles do not apply anymore, since the Alternating Major/Minor 3rds will always take us from a I in some key to a ii in another key. Building the entire structure out as above, but linking identical chords, we get the following structure:

Even with a longer (24-chord) cycle, we still are only able to access the same 36 chords as with the orbifold above. However, our possibilities of motion have increased drastically!

The Discrete Space of Possible States of Your App

If you’re a developer or tech aficionado, you may be familiar with a “new” trend in software that’s taken hold in the last five or so years: Flux. The basic idea behind Flux is that every change to application state can be called an action, and any action represents a transformation from one fully-fleshed-out app state. Implementation-wise, this is solved with a Dispatcher and a Store and various View components:

If this reminds you of the previous orbifold-based problem spaces, you’re onto something. In fact, I will go so far as to assert that Flux actions are just orbits within an orbifold. The main difference between an arbitrary Flux app, the apple scenario, the bishop scenario, and the musical scenario is that your app’s state is an infinitely large discrete space. However, it’s not uncountably infinite (unless your Action parameters can be uncountably infinite, but this is something computers can’t do very well): for any app state, you could simply encode the series of Actions taken to reach that state as a long series of bits, and you would have assigned a unique integer that identifies that app state. In fact, that’s just what event sourcing is!

Every programmer should take the time to study the basics of music. It changes the way we think about the world and our work, and reinforces our understanding that, while we may be making new products, very little that we do is really conceptually “new.” Through this humility and understanding of the past, we can all build a better future together.

Jon Latané is a senior software engineer at Smashing Boxes, accompanist with the Durham Community Chorale and numerous other productions and individuals, and the developer of Topologica in what little free time he has left.

Careers at Smashing Boxes


Open Positions

We don’t just make great products, we help build great companies.


Contact Us

Get exclusive access to Smashing Boxes news, case studies, and events.

Sign up now
close ×

Get exclusive access to Smashing Boxes news, case studies, and events. Sign up now!

* indicates required