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.
EnginesEach engine is called through its canonical adapter. No engine logic is re-implemented for this site.
AlignmentThe 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).
PackThe data pack stores the labels only; normalisation and classification take place in the browser.
GradesA separate grading step derives the textbook label at every compared position and writes the locked 1/0 grades.

Three spelling dialects

EngineKey prefixExamples
musWMnone i · V₇ · ii₄₂ · vii°⁶ · iiø⁶₅ · IMaj₇ · V₇(no5)
AnalysisGNNalways, followed by a space g: i · g: V7 · g: ii%65 · g: vo · Ab: I6
AugmentedNetoccasionally 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?

BasisHeld cellsStatus
Onset onlyExcludedUsed throughout
SustainedCompared 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.

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

EffectWhat happensHow 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.

ModeWhat it does
StrictRaw 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:
  • Sub- and superscript digits become ASCII: V₇ → V7, I⁶₄ → I64
  • Diminished: °o (vii°⁶viio6)
  • Half-diminished: ø and %h (iiø⁶₅ii%65)
  • Figured-bass abbreviations: 422, 636, 65365, 7537, 53 ≡ (none)
  • Maj is dropped: IMaj₇I7
  • Over the same bass, a triad name and a seventh name are not a difference: root position VV7; first inversion V6V65 (ii6ii65); second inversion V64V43; likewise IIM7, vii°vii°7viiø7, V/VV7/V. A different bass remains a difference (VV6, I65I), and the third inversion (2) has no triad counterpart. The same rule governs Alignment, Disagreements and the textbook grades.
  • Voicing remarks are dropped: (no3), (no5), (dyad)
Degree onlyNormalised, 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: Cad64I64 (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

ClassConditionPage
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

Preliminary results. Every figure on this site is recomputed from the corpus pack at each build. On 15 September 2026 the pack was rebuilt after an alignment fault was found and corrected, and the textbook criterion was audited against the book — see Corrections. The textbook grades measure tolerant agreement with a label derived from musWM’s own reading of the chord; they are not an accuracy against an independent reference.

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.

1The engine’s normalised label equals the textbook label.
0It does not.
The engine emits no label here. Absence is a matter of coverage rather than error, and the row is not compared.
at onsetThe cell holds a label carried from an earlier onset. The engine made no claim at this position, so the row is not compared.
blankThe 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 bar90.8%99.8%
musWM pitch classes within its bar81.0%99.3%
AugmentedNet bass found in its bar99.3%99.3%
Works failing the check67 of 3353 of 335
Works on an approximate or repeat-expanded bar map640

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.

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.

MappingConditionTransform
directThe reported range fits within the written range Used as reported
expandedThe reported range is wider, and music21’s repeat expansion is long enough to cover itPosition in the expansion → the written bar at that position
approxThe 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.

What these figures do not establish

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.

musWMAnalysisGNNAugmentedNet
InputNotes as they soundA complete score, turned into a graph of all its notesA complete score, parsed and encoded frame by frame
Context behind a labelPast only. A label uses nothing that has not yet soundedPast and future: graph edges to later notes and bidirectional recurrent layersPast and future: bidirectional recurrent layers over the whole sequence
When a label existsDuring listening, once the chord in the window is stableAfter the whole piece has been processedAfter the whole piece has been processed; chord boundaries are resolved over the full output
How it is derivedDeterministic lookup and textbook-referenced rulesWeights learned from annotated corporaWeights learned from annotated corpora
What can be tracedPitch-class set, bass, root, chord code and local key are recorded with every labelThe predicted labelThe 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.