
Security News
arXiv Is Rate Limiting Authors Following a Flood of AI Slop Submissions
arXiv now limits authors to two submissions a month as AI slop overwhelms moderators, delays good papers, and sparks debate over applying the limit to everyone.
Neo-Riemannian music theory: PLR transformations, hexatonic and octatonic cycles, the Tonnetz lattice, and shortest transformation paths between triads. Typed, zero dependencies.
Neo-Riemannian music theory for JavaScript and TypeScript: the P, L, R transformations, hexatonic and octatonic cycles, the Tonnetz lattice, and shortest transformation paths between triads. Typed, zero runtime dependencies, runs in the browser and in Node.
npm install tonnetz
import { triad, p, l, r, transform, hexatonicPole, pathBetween, triadName } from "tonnetz";
const c = triad(0, "major"); // C major
p(c); // { root: 0, quality: "minor" } C minor
l(c); // { root: 4, quality: "minor" } E minor
r(c); // { root: 9, quality: "minor" } A minor
triadName(hexatonicPole(c)); // "G# minor" (no common tones)
triadName(transform(c, "LPR")); // "C# minor" (apply L, then P, then R)
pathBetween(c, triad(5, "minor")); // "PLR" shortest route to F minor
import { parseChord, chordCandidates } from "tonnetz";
parseChord("C"); // { root: 0, quality: "major", pitchClasses: [0, 4, 7] }
parseChord("Cm7"); // { root: 0, quality: "minor7", pitchClasses: [0, 3, 7, 10] }
parseChord("F#maj7"); // { root: 6, quality: "major7", pitchClasses: [1, 6, 10, 11] }
parseChord("Bbdim7"); // { root: 10, quality: "diminished7", pitchClasses: [0, 3, 6, 10] }
parseChord("Csus4"); // { root: 0, quality: "sus4", pitchClasses: [0, 5, 7] }
parseChord("xyz"); // null
// Identify all chords that contain the pitch classes 0, 4, 7 (C major triad).
chordCandidates([0, 4, 7]);
// [{ root: 0, quality: "major", symbol: "C" }, ...]
Triads are { root, quality } with root a pitch class (0..11, C = 0). All functions are pure.
Neo-Riemannian theory (the PLR transformations and the Tonnetz) is standard in modern
music theory, but there is no JavaScript or TypeScript library for it. tonal covers
tonal harmony but not transformational theory; Tone.js is for audio; music21 is a
large Python framework that does not run in a browser. tonnetz fills that gap with a
small, correct, dependency-free engine, well suited to interactive teaching tools and
analysis.
| Capability | tonnetz | tonal | music21 |
|---|---|---|---|
| P, L, R transformations | yes | no | yes |
| Compound transformations | yes | no | yes |
| Hexatonic / octatonic cycles | yes | no | partial |
| Shortest PLR path | yes | no | no |
| Tonnetz lattice coordinates | yes | no | no |
| Common chord symbol parsing | yes | yes | yes |
| Zero runtime dependencies | yes | yes | no |
| Runs in the browser | yes | yes | no |
| Language | TS | JS/TS | Python |
triad(root, quality) constructs a triad, reducing the root modulo 12.triadToPitchClasses(t) returns the three pitch classes.isConsonantTriad(pcs) identifies a pitch-class set as a major or minor triad, or null.triadName(t) returns a readable name such as "C major".allTriads() returns all 24 consonant triads.triadsEqual(a, b) compares root and quality.p(t), l(t), r(t) are the Parallel, Leittonwechsel, and Relative transformations.n(t) (Nebenverwandt) and s(t) (Slide) are the named compound transformations.transform(t, "LPR") applies a sequence of operations, left to right.hexatonicPole(t) returns the maximally distant triad (LPL).hexatonicCycle(t) returns the six triads of the hexatonic cycle.octatonicCycle(t) returns the eight triads of the octatonic cycle.pathBetween(from, to) returns the shortest P, L, R sequence mapping one triad to another.commonTones(a, b) counts shared pitch classes.pitchAtLattice(fifths, majorThirds) maps a Tonnetz lattice coordinate to a pitch class.parseChord(symbol) parses a standard chord symbol string (e.g. "Cm7", "F#maj7", "Bbdim7",
"Csus4", "CmM7") into { root, quality, pitchClasses }. Root is a pitch class (C = 0,
C# = 1, ..., B = 11). Accepts all 12 roots with sharps and flats. Returns null for
unrecognized input.chordCandidates(pcs) is the inverse: given a pitch-class array, returns all recognized chord
symbols whose pitch-class content exactly matches, sorted deterministically by root then quality.ChordQuality is the type for the quality field, a union of string literals.Supported quality labels and the spellings accepted for each:
| Quality label | Accepted suffixes | Example |
|---|---|---|
major | (none), maj, M | C, Cmaj |
minor | m, min, - | Cm, Cmin |
diminished | dim, o | Cdim, Co |
augmented | aug, + | Caug, C+ |
dominant7 | 7 | C7 |
major7 | maj7, M7 | Cmaj7 |
minor7 | m7, min7, -7 | Cm7 |
halfDiminished7 | m7b5, ø7, ø | Cm7b5 |
diminished7 | dim7, o7 | Cdim7 |
sus2 | sus2 | Csus2 |
sus4 | sus4 | Csus4 |
minorMajor7 | mM7, m(maj7), min(maj7) | CmM7 |
Each preserves exactly two common tones, and each is its own inverse. Together they generate the dihedral group of order 24 acting on the 24 consonant triads, which is why a path always exists between any two triads. These properties are tested.
npm run example
npm test
Tests cover the exact textbook transformations and group-theoretic invariants (involutions, the two-common-tone property, and that every shortest path actually maps its source onto its target across all 24 triads).
Issues and pull requests are welcome. See CONTRIBUTING.md.
MIT. See LICENSE.
FAQs
Neo-Riemannian music theory: PLR transformations, hexatonic and octatonic cycles, the Tonnetz lattice, and shortest transformation paths between triads. Typed, zero dependencies.
We found that tonnetz demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.

Security News
arXiv now limits authors to two submissions a month as AI slop overwhelms moderators, delays good papers, and sparks debate over applying the limit to everyone.

Research
/Security News
A new GhostAction wave hits hundreds of GitHub repos, expanding CI/CD secret theft to cloud and AI credentials in source code and git history.

Research
/Security News
Tensorlake npm SDK version 0.5.144 was compromised in a ChainDrop / Shai-Hulud attack, delivering credential-stealing malware.