[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.

5089c1447fd543aa825d9b7ada859437

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}\)).

441a39b1fd7a4eb1aaf20819df85e9bf

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.

82c00f1f887a4e5489db95a86b2e8988

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