from klotho import plot, play
from klotho.tonos import ToneLattice
from klotho.topos.graphs.lattices.algorithms import shortest_path
from klotho.tonos.utils import harmonic_distanceHarmonic Distance¶
We’ve been talking about points being “close” or “far” in the lattice. But what does that mean, harmonically?
Informally, harmonic distance is how “simple” or “complex” an interval feels. A perfect fifth ((\frac{3}{2})) is simple — it’s one of the most consonant intervals. The ratio (\frac{243}{128}) is complex — it’s dissonant and hard to hear as a single interval.
James Tenney formalized this idea. For a ratio (\frac{p}{q}) (in lowest terms), the Tenney height is:
[\text{HD}(p/q) = \log_2(p \cdot q)]
Smaller numerator and denominator means lower Tenney height means harmonically “closer” to the unison. Larger numbers means higher Tenney height means harmonically “further.”
| Ratio | (p \cdot q) | Tenney height |
|---|---|---|
| 3/2 | 6 | ~2.58 |
| 5/4 | 20 | ~4.32 |
| 9/8 | 72 | ~6.17 |
| 243/128 | 31104 | ~14.92 |
Let’s see this in action. We’ll find the shortest path between a few pairs of points in a 2-D lattice and look at the harmonic distance of each ratio along the way:
tl_hd = ToneLattice(2, resolution=3)
pairs = [((0, 0), (2, 0)), ((0, 0), (0, 2)), ((0, 0), (2, 2)), ((0, 0), (3, -2))]
for start, end in pairs:
sp = shortest_path(tl_hd, start, end)
plot(tl_hd, path=sp, figsize=(7,7), fit='tight').play()
print(f"Path: {start} -> {end} ({len(sp)-1} steps)")
for c in sp:
r = tl_hd.get_ratio(c)
hd = harmonic_distance(r)
print(f" {c} -> {r} (HD = {hd:.2f})")
print()Path: (0, 0) -> (2, 0) (3 steps)
(0, 0) -> 1 (HD = 0.00)
(0, 0) -> 1 (HD = 0.00)
(1, 0) -> 3/2 (HD = 2.58)
(2, 0) -> 9/8 (HD = 6.17)
Path: (0, 0) -> (0, 2) (3 steps)
(0, 0) -> 1 (HD = 0.00)
(0, 0) -> 1 (HD = 0.00)
(0, 1) -> 5/4 (HD = 4.32)
(0, 2) -> 25/16 (HD = 8.64)
Path: (0, 0) -> (2, 2) (5 steps)
(0, 0) -> 1 (HD = 0.00)
(0, 0) -> 1 (HD = 0.00)
(1, 0) -> 3/2 (HD = 2.58)
(2, 0) -> 9/8 (HD = 6.17)
(2, 1) -> 45/32 (HD = 10.49)
(2, 2) -> 225/128 (HD = 14.81)
Path: (0, 0) -> (3, -2) (6 steps)
(0, 0) -> 1 (HD = 0.00)
(0, 0) -> 1 (HD = 0.00)
(1, 0) -> 3/2 (HD = 2.58)
(2, 0) -> 9/8 (HD = 6.17)
(2, -1) -> 9/5 (HD = 5.49)
(2, -2) -> 18/25 (HD = 8.81)
(3, -2) -> 27/25 (HD = 9.40)
Notice: points close to the origin tend to have low harmonic distance. Points further away tend to have higher harmonic distance. The lattice geometry and harmonic complexity are correlated.
Distance from Origin¶
Let’s make this correlation more explicit. We can look at every point in the lattice, compute its Manhattan distance from the origin (i.e., the minimum number of single-axis steps to reach it), and compare that with its harmonic distance:
tl = ToneLattice(2, resolution=3)
print(f"{'Coord':>10s} {'Ratio':>10s} {'Steps':>5s} {'HD':>6s}")
print("-" * 38)
for coord in sorted(tl.coords, key=lambda c: sum(abs(x) for x in c)):
r = tl.get_ratio(coord)
steps = sum(abs(x) for x in coord)
hd = harmonic_distance(r)
if steps <= 3:
print(f"{str(coord):>10s} {str(r):>10s} {steps:>5d} {hd:>6.2f}") Coord Ratio Steps HD
--------------------------------------
(0, 0) 1 0 0.00
(-1, 0) 2/3 1 2.58
(0, -1) 4/5 1 4.32
(0, 1) 5/4 1 4.32
(1, 0) 3/2 1 2.58
(-2, 0) 8/9 2 6.17
(-1, -1) 8/15 2 6.91
(-1, 1) 5/3 2 3.91
(0, -2) 16/25 2 8.64
(0, 2) 25/16 2 8.64
(1, -1) 3/5 2 3.91
(1, 1) 15/8 2 6.91
(2, 0) 9/8 2 6.17
(-3, 0) 16/27 3 8.75
(-2, -1) 32/45 3 10.49
(-2, 1) 5/9 3 5.49
(-1, -2) 64/75 3 12.23
(-1, 2) 25/24 3 9.23
(0, -3) 64/125 3 12.97
(0, 3) 125/64 3 12.97
(1, -2) 24/25 3 9.23
(1, 2) 75/64 3 12.23
(2, -1) 9/5 3 5.49
(2, 1) 45/32 3 10.49
(3, 0) 27/16 3 8.75
The trend is clear: as we move further from the origin, harmonic distance generally increases. The origin itself (, the unison) has a Tenney height of 0. Its immediate neighbors — the simplest intervals — have the lowest harmonic distances. Points two or three steps out tend to be more complex.
This is exactly what makes the lattice representation so powerful: geometric proximity approximates harmonic similarity. Points that are close together in the lattice tend to sound consonant with each other; points that are far apart tend to sound more dissonant.
A Note on Generators and Conditioning¶
One important nuance: the Tenney height of a ratio is a property of the ratio itself — it doesn’t depend on which generators define the lattice axes. But how lattice step count maps to harmonic distance does depend on the generators.
In a well-conditioned basis (like or ), each step corresponds to a musically meaningful interval, so step count tracks harmonic distance well. In a poorly-conditioned basis (like one using , the syntonic comma), a single step might correspond to a tiny, complex interval — many steps for little harmonic movement. We’ll revisit this idea when we discuss generators and change of basis in a later notebook.
Summary¶
Harmonic distance (Tenney height) quantifies the “complexity” of a ratio: .
Points near the origin in the lattice have low harmonic distance — they are the simplest, most consonant intervals.
Geometric proximity ≈ harmonic similarity: the lattice organizes intervals so that nearby points tend to be consonant with each other.
The quality of this correspondence depends on choosing good generators — a topic for a later notebook.