You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Notes scored against 100% chart even when lower difficulty is displayed #226
Restored from slopsmith/slopsmith#629 — original issue, opened by @ianmackay123 on 2026-05-30. [restored-from: slopsmith/slopsmith#629]
Version: 0.2.9-alpha.5 (macOS, Apple Silicon / M1) Audio path: Browser (Web Audio) — native engine unavailable, see note below
Summary
With dynamic difficulty set below 100%, the highway draws the reduced
note set correctly, but scoring appears to evaluate against the full
100% chart. Notes that are not shown at the current difficulty are
counted as missed/wrong.
Steps to reproduce
Load any song/arrangement.
Set difficulty below 100% so fewer notes are displayed.
Play the notes as shown on the highway.
Observe the score/verdict.
Expected
Scoring matches the displayed (reduced) note set. Only shown notes are
evaluated.
Actual
Notes absent from the displayed difficulty are still scored, so correct
play at lower difficulty is marked wrong.
Possible cause
The 0.2.8 engine-side NoteVerifier receives the chart via a single
setChart call at song/arrangement load. If setChart is passed the full
100% chart while the renderer applies the dynamic-difficulty reduction
independently, the engine would score against notes the player never
sees. Worth checking whether the difficulty-reduced chart (rather than
the full chart) is what gets pushed to the engine.
Note
Reproduced on the browser audio path because the native engine fails to
load on this machine (separate dlopen symbol error). If the scorer
behaves differently on the native path, that is useful signal.
Confirmed the cause you suspected. The plugin pushed hw.getNotes() / hw.getChords() — the raw 100% chart — to the engine's NoteVerifier, and read the same raw arrays in the browser matchNotes/checkMisses paths. The highway has exposed a difficulty-filtered view all along (getFilteredNotes() / getFilteredChords()), whose doc comment says plugins that score "only the notes the player is currently expected to play" should prefer it. We didn't. Every scoring path now reads through it.
Two things the fix turned up that are worth recording here:
The engine re-push is gated on a chart signature that was counts + first/last onset. That was fine when the chart only changed on song load, but the mastery slider can swap which notes are displayed without changing how many — so a same-count difficulty change (or a chord voicing swapped at the same onset) hashed identically and would have left the engine scoring the old chart. The signature now hashes note and chord-constituent identity.
Filtering also makes a chart legitimately chord-only, which two paths didn't expect: startDrillHere() aborted with "chart has no notes", and A/V auto-calibration bailed with 'no notes' and silently skipped the take. Both were latent before this — low mastery just makes them reachable, since most arrangements reduce to chord stabs.
Not merged, awaiting sign-off. One caveat: you reported this on the browser audio path, and the plugin's vm test harness has no AudioContext stub, so that path is covered by a source guard rather than a behavioural test. Worth one on-device pass at a reduced difficulty before release.
Activity
byrongamatos commentedon Jul 13, 2026
Fix up for review: got-feedBack/feedBack-plugin-notedetect#65.
Confirmed the cause you suspected. The plugin pushed
hw.getNotes()/hw.getChords()— the raw 100% chart — to the engine's NoteVerifier, and read the same raw arrays in the browsermatchNotes/checkMissespaths. The highway has exposed a difficulty-filtered view all along (getFilteredNotes()/getFilteredChords()), whose doc comment says plugins that score "only the notes the player is currently expected to play" should prefer it. We didn't. Every scoring path now reads through it.Two things the fix turned up that are worth recording here:
The engine re-push is gated on a chart signature that was counts + first/last onset. That was fine when the chart only changed on song load, but the mastery slider can swap which notes are displayed without changing how many — so a same-count difficulty change (or a chord voicing swapped at the same onset) hashed identically and would have left the engine scoring the old chart. The signature now hashes note and chord-constituent identity.
Filtering also makes a chart legitimately chord-only, which two paths didn't expect:
startDrillHere()aborted with "chart has no notes", and A/V auto-calibration bailed with 'no notes' and silently skipped the take. Both were latent before this — low mastery just makes them reachable, since most arrangements reduce to chord stabs.Not merged, awaiting sign-off. One caveat: you reported this on the browser audio path, and the plugin's vm test harness has no
AudioContextstub, so that path is covered by a source guard rather than a behavioural test. Worth one on-device pass at a reduced difficulty before release.fix(scoring): score the difficulty the player is shown, not the 100% …
fix(scoring): score the difficulty the player is shown, not the 100% …