A practice loop can start below a score's written tempo and rise a little on each pass. The useful boundary is the end of the selected passage: changing speed while notes from that pass are still being scheduled makes the transition harder to reason about.
In Pianory's score player, the sheet UI supplies a five-percentage-point tempo step. The player reaches the end of the selected passage, checks whether it will repeat, then raises the percentage and caps the ramp at 100% of the written tempo. It reports the new percentage and anchors the next pass at the loop's start. A 90% start with a five-point step therefore reports 95%, then 100%, and stops rising.
The passage range resolves to quarter-note positions with an exclusive end event. Notes that cross the passage end are clipped to that boundary before the next pass is scheduled. Those rules let the repeat restart at a defined score position instead of inheriting a note tail past the loop.
One important distinction: 100% is the ramp's destination, not the tempo slider's maximum. The general tempo control permits 25%–125%. The optional ramp is a practice behavior layered on top of that control. It advances at a repeat boundary; it does not measure whether the player performed the passage cleanly.
A focused unit test starts a one-measure loop at 90% with a five-point step and checks callbacks [95, 100] plus the final setting. Timeline tests cover the exclusive range. Those checks do not measure audible note-onset spacing after a tempo change, so I would add an audio-scheduling regression test before claiming that every ramped pass sounds exact in a browser.
This is a narrow implementation lesson: make the repeat boundary explicit, update the tempo once there, and keep the next pass anchored to the selected score range.
AI assisted with drafting this article; I checked its claims against the repository and focused tests. I make Pianory, the piano-learning app used in this example.
Top comments (0)