Method
How the comparison is built: how the labels of three engines are brought onto a common grid, which differences count as differences, how each label is graded against the textbook, and what the resulting figures can and cannot support. Two quantities are kept strictly apart throughout. Agreement asks whether the engines write the same Roman numeral at the same position. Accuracy asks whether a label matches the one the textbook derives. Neither is ever read as the other.
Pipeline
| Scores | .mxl · .musicxml · .xml · .mid · .midi; MIDI files are engraved
with MuseScore before packing. |
| Engines | Each engine is called through its canonical adapter. No engine logic is re-implemented for this site. |
| Alignment | The alignment step creates one row per distinct onset across the three engines. A label persists to later rows within its own bar and never crosses a barline. Carried cells are marked held and excluded from the comparison (see below). |
| Pack | The data pack stores the labels only; normalisation and classification take place in the browser. |
| Grades | A separate grading step derives the textbook label at every compared position and writes the locked 1/0 grades. |
Three spelling dialects
| Engine | Key prefix | Examples |
|---|---|---|
| musWM | none | i · V₇ · ii₄₂ · vii°⁶ · iiø⁶₅ · IMaj₇ · V₇(no5) |
| AnalysisGNN | always, followed by a space | g: i · g: V7 · g: ii%65 · g: vo · Ab: I6 |
| AugmentedNet | occasionally | i · V7 · iiø65 · viio7 · Cad64 · B-:V |
Comparison basis: what counts as a claim
The engines segment the music independently. musWM often assigns one label to a whole harmonic span, whereas AnalysisGNN tends to label every beat. To place them on a common grid, alignment carries each engine’s most recent label forward within its bar, so a cell may hold a label the engine never asserted at that position. Such cells are marked held.
Every figure on this site uses the onset basis. A held cell is treated exactly like a missing label: it is displayed struck through and receives no grade. The question is therefore: where an engine actually commits to a label, do the engines agree?
| Basis | Held cells | Status |
|---|---|---|
| Onset only | Excluded | Used throughout |
| Sustained | Compared as if re-asserted on every beat | Not offered: it counts claims no engine made and inflates disagreement between engines that segment at different rates |
In When-in-Rome 335, 37.8% of all labelled cells are held (105,278 of 278,392). A choice with that much influence on the result is fixed rather than left to a setting.
Alignment: where a label is placed
A corpus is built under one of two alignments, named in the note above every table. This corpus uses window.
| Alignment | What it does |
|---|---|
| window | Each musWM label is placed at the beat that musWM itself reports as the start of its analysis window. musWM can anchor a label to the start of a beat; window alignment moves it to the start of the window it describes, so the label is compared with the moment it actually describes. |
| beat | Labels stay on the beat each engine anchored them to. This reproduces the RN tab of the main site and the earlier benchmark workbooks exactly. |
Only musWM labels are ever moved. AnalysisGNN and AugmentedNet report no analysis window — they label onsets directly — and are used exactly as their adapters return them. The builder restricts repositioning to musWM by name, and a before-and-after comparison confirmed that the rows of the other two engines pass through byte-identical.
Should two musWM labels would land on the same position, the second move is not made and that label keeps its own beat, so no label is lost.
In When-in-Rome 335, 12,174 of 44,850 musWM onsets (27.1%) were repositioned.
What the choice of alignment changes
Repositioning can only move a musWM label away from one position and onto another. Leaving a beat for which it had no evidence removes a disagreement that was never a genuine claim; leaving a beat on which all three engines happened to agree removes an agreement; arriving at a new beat creates a new comparison. No position at which all three engines remain present switches between identical and different. Because the pool of comparable rows changes, an agreement rate is meaningful only together with the alignment under which it was computed.
Where a label sits is not always where its evidence is
Two further effects can separate a label from the moment it describes. Both arise in the shared alignment step, so the RN tab of the main site exhibits them as well.
| Effect | What happens | How it is shown |
|---|---|---|
| Beat snap | A label may be anchored at the start of a beat although the chord it names is heard slightly later. Under window alignment this is corrected at build time; under beat alignment it is left in place. | Corrected before the figures are computed. |
| Dropped repeat | The alignment step removes a label identical to the engine’s previous one, and the bar-local reset then leaves the cell blank. The cell reads “no label” although the engine did assert something at that position. | The cell carries an emitted, dropped badge, and the score window shows what was emitted. |
Comparison modes
Only the normalised comparison is used on this site. The strict and degree-only modes are described for completeness; neither is offered in the tables.
| Mode | What it does |
|---|---|
| Strict | Raw strings compared character by character, with only the key prefix
(g:) separated — musWM writes no prefix, so retaining it would make every row
differ. |
| Normalised | Strict comparison, plus the following equivalences:
|
| Degree only | Normalised, with inversion and seventh then removed; the degree
and any applied target remain (V7/vi → V/vi, iiø65 → ii), and
Cad64 counts as I. This is looser than musical equality and is not
suitable for reporting an agreement rate. |
Differences that normalisation deliberately preserves: Cad64 ↔ I64 (a
cadential reading of the six-four), different applied targets (/III ↔ /V),
and different local-key decisions.
Repeated labels
A label is written once, at the row where its run begins; later rows of the same run are left blank for that engine. This is a fixed presentation choice.
| What is hidden | A cell whose label is identical to the same engine’s label in the row directly above. A blank cell, or a different label, starts a new run. |
| Classification | Unaffected. Identical, different and coverage gap are computed from the full values. |
| Grading | Unaffected. Grades are the locked textbook grades at the positions where an engine emitted a label; a blank repeat cell is not a separate claim. |
In When-in-Rome 335 nearly every repeat is a carried cell rather than a genuine re-assertion: of 106,074 repeated cells, 105,278 are carried, and only 784 are an engine emitting the same label again at the next onset (760 AnalysisGNN, 24 AugmentedNet). On the onset basis, hiding repeats therefore changes what is read, not what is counted.
Row classes
| Class | Condition | Page |
|---|---|---|
| identical | All three engines emit a label, and the three normalise to the same string | Alignment |
| different | All three engines emit a label, and at least one differs | Disagreements |
| partial | One or two engines emit no label | Coverage gaps |
| empty | No engine emits a label | Coverage gaps |
Grading
All grades on this site are derived from the textbook and locked. At every row where all three engines emit a label at the same onset, the reference is the Roman numeral that Kostka, Payne & Almén, Tonal Harmony, 8th edition, assign given musWM’s local key, mode and chord code at that position.
| 1 | The engine’s normalised label equals the textbook label. |
| 0 | It does not. |
| — | The engine emits no label here. Absence is a matter of coverage rather than error, and the row is not compared. |
| at onset | The cell holds a label carried from an earlier onset. The engine made no claim at this position, so the row is not compared. |
| blank | The textbook derives no label mechanically (a two-note sonority, dense ornament), so there is nothing to grade against. |
Grades are the same on Alignment and Disagreements, and none can be changed by hand. Where the textbook accepts two spellings of one chord — a suspension figure over the same degree (pp. 201–202), or V/III ≡ VII in minor (p. 257) — both count as equal for all three engines.
Tolerances. A triad and a seventh chord on the same root and bass count as the same label (V = V7, V6 = V65, V64 = V43, vii° = vii°7 = viiø7); a different bass is still a difference. The grades are therefore tolerant agreement with the textbook label, not exact label identity.
An engine’s score is correct ÷ graded, reported over all compared rows and separately for Alignment and Disagreements. Ratings lists every reason a row was not compared, with counts, together with the number of labels each engine emitted, so the share of the corpus behind each figure is always in view.
What the figure does and does not say. The reference is built from musWM’s reading — its key, notes, bass and root — but never from its label. Where musWM’s label departs from the textbook label, musWM scores 0; the reference stays as the textbook writes it, and AnalysisGNN and AugmentedNet are checked against that same label, so an engine that writes it scores 1 even where musWM scores 0. Because the key is musWM’s, a label that would be right in a key another engine read differently scores 0; the share of compared rows on which each engine shares musWM’s key is reported beside the result. On Alignment rows the three engines wrote the same label and therefore share a score by definition.
Placement on the written bar
AnalysisGNN and AugmentedNet read the MusicXML and report written bar numbers. musWM reads MIDI, and until 15 September 2026 that MIDI was MuseScore’s export of the score, which plays repeats and numbers bars on its own timeline. musWM’s bar numbers therefore did not always name the written bar: in some works every label sat one bar or four bars off, repeated sections were analysed twice, and one export stopped before the end of the score.
musWM’s input is now rendered from the MusicXML in written bar order, one MIDI bar per written bar with the bar’s real length (pickups and irregular bars included). Each musWM row is placed by its own time position: tick → written bar → written bar number and beat. The label itself is not touched. All three engines now share one timeline, and every work uses the direct bar map.
Alignment check. The build then tests, for every work and engine, whether the notes an engine reports at an onset exist in the written bar the row is shown on: the bass for all three engines, and musWM’s analysed pitch classes as well. It does not ask whether a chord is right, so it favours no engine. A work fails when musWM’s or AugmentedNet’s bass is found in the bar less than 90.0% of the time; AnalysisGNN is reported but does not decide, because its bass is a separate per-note prediction rather than the bass of the chord it labels.
| Before (14 Sept) | After (15 Sept) | |
|---|---|---|
| musWM bass found in its bar | 90.8% | 99.8% |
| musWM pitch classes within its bar | 81.0% | 99.3% |
| AugmentedNet bass found in its bar | 99.3% | 99.3% |
| Works failing the check | 67 of 335 | 3 of 335 |
| Works on an approximate or repeat-expanded bar map | 64 | 0 |
The 3 remaining works (126, 162, 179) show scattered misses rather than a constant offset and are listed for review by eye; they are left out of the independent note check. Where a musWM label would be moved onto a position another musWM label already holds, the move is not made and the label stays on its own beat, so no label is lost; no collision warning remains.
Independent note check
The textbook grades take musWM’s key, root, bass and notes as given, so they cannot show whether those choices are right. This check builds its reference only from the notes written in the score — no engine output, no chord code — and applies it to every engine that reports the field.
- A bar is used only when all its notes (every part, concert pitch, grace notes ignored) form exactly one complete triad or seventh chord; augmented triads and diminished sevenths are left out because their root cannot be read from the notes.
- The written spelling must stack in thirds from that root, so E–G–A♯ is not read as E diminished.
- Every beat must contain the root, and no beat may hold a complete triad on another root, so a bar that moves C–E–A → F–A–C is not taken as one F major seventh.
- The bass is the lowest sounding note, tested only where it stays the same through the bar.
- Key cannot be read from one chord; it is tested only for compatibility with the written key signature, which counts every modulation as incompatible and is therefore not a key accuracy.
The check covers the unambiguous case only. It does not grade Roman numerals, it does not reach passages with chord changes, suspensions or passing tones, and its result cannot be generalised to the whole corpus. Results are on the Summary.
Bar-number mapping
Since 15 September 2026 all three engines report written bars and every work uses direct. The
other two mappings are kept for corpora built before that date, where musWM could follow the repeat-expanded
order of performance.
| Mapping | Condition | Transform |
|---|---|---|
direct | The reported range fits within the written range | Used as reported |
expanded | The reported range is wider, and music21’s repeat expansion is long enough to cover it | Position in the expansion → the written bar at that position |
approx | The repeat structure cannot be resolved | Written bars mapped cyclically — the bar shown is approximate, and the score window says so |
The mapping used for each work is listed under Works. Where there is doubt, switch the score window to Whole work and confirm the bar by eye.
Corrections
15 September 2026.
- Alignment fault corrected. musWM labels had been placed on MIDI bar numbers rather than written bars (see Placement on the written bar). The whole corpus was rebuilt with the current engine. Every figure on the site changed; the three-engine agreement rose from 20.2% to 22.5% over 16,091 comparable rows.
- Textbook criterion audited. The coded rule set now reproduces all 56 labelled chords of the book’s reference examples (Examples 16-3, 16-4, 17-1, 17-2 and the tables on p. 65); the first version reproduced 50. V7/VI in minor is now derived as the book writes it; the secondary functions of VII in minor and the melodic-minor chords with the same notes are accepted in both spellings for every engine; positions whose notes form an augmented sixth chord are left out for every engine, because the notes alone cannot separate it from V7/♭II. Known limits that need context (Picardy third, common-tone diminished seventh, which note of a chord is a nonchord tone) are stated rather than hidden.
- Two engine rules corrected after the audit (user approval). In minor, a major-minor seventh chord on III is now written V7/VI rather than III7 (Example 16-4, p. 257; 97 events). A leading-tone chord (vii°, vii°7, viiø7, secondary ones included) is no longer read where the root of the V7 that contains it sounds in musWM’s window and had been set aside as a nonchord tone; the chord is then written as that V7 (187 cells). Theory conformance moved from 96.1% to 96.8%.
- Grading: leading-tone references checked against the score. Where the textbook reference is a leading-tone chord but the root of the V7 containing it sounds in the written score at that onset, the reference would rest on musWM’s window leaving that note out; such positions (27) are not graded, for any engine.
- Margin statement withdrawn. The earlier “±5 / ±1 point” margin mixed a resampling interval with a rule-change shift and is no longer given.
- Independent note check added (above).
What these figures do not establish
- They do not show that any engine is correct in an absolute sense. Agreement among three engines may reflect a shared convention as readily as a shared mistake.
- The textbook grade tests naming, not hearing. The key and notes are musWM’s, so the grade asks whether a chord, as musWM read it, is named as the textbook would name it. It is not an accuracy, and the three engines’ grades are not a ranking.
- The independent note check covers only bars holding one unambiguous chord; it says nothing about passages where the harmony changes inside the bar.
- An agreement rate depends on its denominator. The Summary reports agreement over comparable rows; a rate over all rows would be a different number and cannot stand in for it.
- A coverage gap is not a disagreement, and the two are never pooled.
- Labels are never recomputed in the browser. The pack holds what the engines produced on the day it was built.
- Every figure uses the onset basis. A figure that counted carried labels would answer a different question and cannot be compared with these.
- A row’s bar and beat are where the aligned table places the label, not necessarily where the engine found its evidence. The evidence line in the score window shows the difference.
Textbook audit of the rules added to musWM (12–13 September 2026)
Three rules were added to musWM after the corpus was first graded: P12, which replaces an augmented reading with a non-augmented reading of the same pitch classes; P13, which reads a non-diatonic diminished triad as an incomplete fully diminished seventh with leading-tone function; and the beat extension, which re-reads an augmented triad found on a sub-beat together with the rest of its metrical beat. Every label these rules produce in this corpus was checked against Kostka, Payne & Almén, Tonal Harmony, 8th edition. The findings are reported here and have not been applied to the engine; the labels on this site are the engine’s labels as they currently stand.
Loading…
Real time and explainability
The three engines differ in a property that matters as soon as analysis has to keep pace with listening. musWM is rule-based, causal and explainable: a label is formed from the notes held in a capacity-limited window, uses nothing that has not yet sounded, and carries the representations it was derived from. AnalysisGNN and AugmentedNet are learned score-level models; both take a complete score and use bidirectional context, so each label also depends on later music. On this site all three were run on complete scores, so the comparison itself is unaffected; the difference concerns what each engine can do during listening.
| musWM | AnalysisGNN | AugmentedNet | |
|---|---|---|---|
| Input | Notes as they sound | A complete score, turned into a graph of all its notes | A complete score, parsed and encoded frame by frame |
| Context behind a label | Past only. A label uses nothing that has not yet sounded | Past and future: graph edges to later notes and bidirectional recurrent layers | Past and future: bidirectional recurrent layers over the whole sequence |
| When a label exists | During listening, once the chord in the window is stable | After the whole piece has been processed | After the whole piece has been processed; chord boundaries are resolved over the full output |
| How it is derived | Deterministic lookup and textbook-referenced rules | Weights learned from annotated corpora | Weights learned from annotated corpora |
| What can be traced | Pitch-class set, bass, root, chord code and local key are recorded with every label | The predicted label | The predicted label |
Sources: the pinned implementations used here
(AugmentedNet models.py and inference.py; AnalysisGNN models/analysis.py,
models/chord.py). This describes design objectives, not analytical quality.
Where each engine comes from, and how change is tracked
musWM runs from this repository. AugmentedNet runs from a local checkout of
napulen/AugmentedNet pinned at tag
v1.9.1, with the weights shipped in that tag; nothing is fetched at inference time, so its
output changes only when the pin is moved deliberately. AnalysisGNN is treated differently: it is
called through the authors’ live service at
analysisgnn.com, so its output is whatever the authors
serve on the day a corpus is built. This is intentional — the figures follow the engine as its
authors publish it. A pinned local copy of
manoskary/analysisgnn is kept alongside, so that a
run can still be reproduced if the service changes or disappears.
Because one engine can change beneath the figures, every corpus build is fingerprinted and compared with the build before it. The table below is that record. It is append-only — entries are never edited or removed — so any figure that moves can be traced to the build that moved it. A changed fingerprint without a corresponding change in this repository means that the upstream engine changed, not the method of comparison.