Grading piano timing in the browser with Web MIDI
I picked up piano again this year and wanted to connect my digital piano to my laptop, read real sheet music, and figure out which notes or beats I missed. But none of the apps I tested could do that. So I created Keystair , a browser-based piano course that listens to a USB MIDI keyboard. Most of it is standard React code. A few browser details took me longer than they should have, so here they…
Grading piano timing in the browser with Web MIDI involves using a simple API to read the input from a USB MIDI keyboard. The key challenge is handling the note-off signal, as many keyboards send a note-on with a velocity of 0 instead of a note-off. The code initializes access to the MIDI inputs, attaches a handler to each input, and reads three bytes per message.
When a note is pressed, the code checks if the note is a note-on or note-off, and if it's a note-on, it calls a function with the pitch, on/off status, and timestamp. If the note-off has a velocity of 64, it's considered as the sustain pedal being pressed. The event's timeStamp is synchronized with performance.now(), which is important for timing.
Output works by sending MIDIOutput.send() with a timestamp, allowing the app to play an example at a precise moment on the student's own piano. Permissions are handled by checking if the site already has access, and reconnecting on the next visit if it doesn't. Different browsers have different levels of support for Web MIDI, so if a browser doesn't support it, the app falls back to using the microphone.
The metronome click is generated from the Web Audio API, with a clock maintained by an AudioContext. All times are kept in performance.now() milliseconds, and only converted to the audio clock time when scheduling sounds. This ensures that a click meant for beat 3 is heard right then, not slightly later. The timing windows for notes are determined by the tempo, with notes counted as being on time within half a beat, up to 400 milliseconds.
Written by urgent.news from Dev.to's reporting — not their text. Machine-written — may contain errors; check the original before relying on it.