[1]:
import automuse
import automuse.chord as chord
import automuse.scale as scale
import automuse.modes as modes

Circle of Fifths

The mythical Circle of Fifths, often found in introductory music theory textbooks, highlights some interesting relation in music. This notebook seeks to explore these.

To construct the circle, let us keep counting fifths, beginning at $:nbsphinx-math:`mathrm{C}`$:

[2]:
from automuse import reach
[3]:
def count_fifths(base: str, length: int):
    accumulated: list[str] = []
    for _ in range(length):
        accumulated.append(base)
        base = reach(base, "perfect 5")
    return accumulated
[4]:
exterior_ring: list[str] = count_fifths("C0", 13)
print(exterior_ring)
['C0', 'G0', 'D1', 'A1', 'E2', 'B2', 'F#3', 'C#4', 'G#4', 'D#5', 'A#5', 'F6', 'C7']

Conveniently, counting notes this way gives all 12 notes of an octave, with the 13th note coming back to \(\text{C}\). These 12 notes form a cycle.

Recall that A is the submediant of C; consequently the Am scale is the relative minor of CM. Let us count another 12 notes, this time starting with A:

[5]:
interior_ring: list[str] = count_fifths("A0", 13)
print(interior_ring)
['A0', 'E1', 'B1', 'F#2', 'C#3', 'G#3', 'D#4', 'A#4', 'F5', 'C6', 'G6', 'D7', 'A7']

To form the circle of fifth, stack these cycles on top of each other. This is so very isomorphic.

ab8c7879afc54c6d90fa3c04b3ec9983

The circle of fifth has many uses. First of all, it matches every chord to its relative major: for every note in the exterior ring, its major scale has the same notes as the minor scale constructed with its interior neighbour:

[6]:
chord.chord("C", modes.MAJOR)
[6]:
['C0', 'E0', 'G0']
[7]:
for i in range(len(exterior_ring)):
    assert automuse.same_class(
        scale.scale(exterior_ring[i], modes.MAJOR),
        scale.scale(interior_ring[i], modes.MINOR)
    )

Continue for more uses of the circle.

Usage: Neighbouring Scales

The circle of fifths is a neat way to arrange scales. The closest (in \(\mathscr{l}_1\)) neighbours of a scale are its neighbours on the circle: for example, CM and GM differ by exactly 1 note which differs by exactly 1 semitone; the same is true for GM and DM.

[8]:
print(sorted(scale.scale("C", modes.MAJOR)))
print(sorted(scale.scale("G", modes.MAJOR)))
print(sorted(scale.scale("D", modes.MAJOR)))
['A0', 'B0', 'C0', 'D0', 'E0', 'F0', 'G0']
['A0', 'B0', 'C1', 'D1', 'E1', 'F#1', 'G0']
['A0', 'B0', 'C#1', 'D0', 'E0', 'F#0', 'G0']

Geometry

Coincidentally, neighbours of a note are in its major scale: its left neighbour the subdominant (\(\hat{4}\)), its right neighbour the dominant (\(\hat{5}\)), and its interior neighbour the submediant (\(\hat{6}\)).

e7de052911314925af81eb99d2fb28f9

Because chords are constructed from scale degrees, chords can be expressed on the circle as polygons. For example, connecting notes on a C major triad results in a triangle; rotating this triangle clockwise by \(30\degree\) gives the G major triad.

d7b4d06da6ee47c0aa51028b0170ae41

Tools of this kind are common in music. Another good example is the Tonnetz for visualising Neo-Riemannian transforms. The Tonnetz which will not be explained here.

Usage: Sharps and Flats

Another interesting pattern: starting at C and counting clockwise, each subsequent scale has one more sharp. Going down from C has the same effect: each subsequent scale has one more flat.

As a result, each note on the circle corresponds to a number of sharps and flats. These accidentals are often notated right after the staff.

[9]:
for base in count_fifths("C0", 12):
    print(len(
        [x for x in scale.scale(base, modes.MAJOR)
         if "#" in x]
    ))
0
1
2
3
4
5
5
5
4
3
2
1