A web-based piano training application that helps musicians practice by connecting MIDI devices, displaying sheet music, and providing real-time feedback on note accuracy.
- Score Library: Browse and search 70 public domain classical music scores
- MIDI Connection: Connect MIDI devices via Web MIDI API (USB or Bluetooth)
- Real-time Note Display: Visual feedback showing played notes on a musical staff
- MusicXML Support: Load and display sheet music in MusicXML format
- Interactive Practice: Highlights notes as you play and validates accuracy
- Recording & Playback: Record MIDI performances and replay them
- API Endpoints: RESTful API for managing MIDI recordings (cassettes)
- File Management: Stores recordings as JSON files
- MIDI Processing: Parses MIDI messages from Bluetooth devices
- MusicXML Rendering: Uses OpenSheetMusicDisplay for sheet music visualization
- Note Validation: Compares played notes against sheet music
- Visual Feedback: Color-codes notes to show progress and errors
- Note Conversion: Converts MIDI note numbers to musical notation
- Score Extraction: Parses MusicXML files to extract playable notes
- Timing Analysis: Tracks note timing and duration
- Framework: Sinatra (Ruby web framework)
- Dependencies:
sinatra: Web frameworkjson: JSON processingfileutils: File system operationspuma: Web serverrack: Web server interface
- Libraries:
OpenSheetMusicDisplay: MusicXML visualizationAlpine.js: Reactive UI frameworkPico CSS: Lightweight CSS framework
- Framework: Minitest with Capybara
- Driver: Cuprite (headless Chrome)
- Test Coverage: MusicXML parsing, note extraction, playback
MIDI Device → Web MIDI API → MIDI Message Parser → Note Validator → Visual Feedback
↓
MusicXML Loader → Score Renderer → Note Extractor
↓
Recording System → API → File Storage
app.rb: Main Sinatra application with API endpointspublic/index.html: Score library page with search and filteringpublic/score.html: Score practice pagepublic/data/scores.json: Index of 70 available scorespublic/scores/: Directory with 70 MusicXML files (1.6MB)public/js/app.js: Alpine.js coordination layerpublic/js/library.js: Library page state and filteringpublic/js/midi.js: Web MIDI API & recordingpublic/js/musicxml.js: MusicXML parsing & validationpublic/js/cassettes.js: Cassette managementpublic/js/midi_mock.js: Mock implementation for testingpublic/js/utils.js: Utility functionspublic/styles.css: Custom stylingtest/piano_trainer_test.rb: Piano trainer teststest/library_test.rb: Library page testsRakefile: Test runner configuration
- Ruby 3.0+
- Chrome/Edge browser (for Web Bluetooth API support)
# Clone the repository
git clone git@github.com:isc/piano-trainer.git
cd piano-trainer
# Install Ruby dependencies
bundle install
# Start the server
ruby app.rbThe application will be available at http://localhost:4567
-
Browse Score Library (Home page:
/):- View all 70 available public domain scores
- Search by title or composer
- Click a score to load it for practice
-
Connect MIDI Device:
- Click "Connecter clavier MIDI"
- Select your MIDI device from the list (if multiple devices are connected)
- Grant MIDI permissions if prompted
-
Load Sheet Music:
- From the library: Click any score to automatically load it
- Or manually upload: Click "Charger partition MusicXML" to select a file from your computer
- The sheet music will be displayed
-
Practice Mode:
- Play notes on your MIDI keyboard
- The system highlights correct notes in green
- Incorrect notes show error messages
- Progress is tracked in real-time
-
Recording:
- Click "Démarrer enregistrement" to start recording
- Play your performance
- Click "Arrêter enregistrement" to stop
- Enter a name for your recording
-
Playback:
- Select a recording from the dropdown
- Click "Rejouer cassette" to play it back
The application includes 70 public domain classical music scores ready to practice:
- Composers: Bach, Beethoven, Chopin, Debussy, Mozart, Schumann, and more
- Styles: Sonatas, nocturnes, waltzes, preludes, minuets, variations
- Search: Filter by title or composer name
- Direct Loading: Click any score to instantly load and practice it
- Local Storage: All scores served from local
public/scores/directory (1.6MB)
Available scores include popular pieces like:
- Moonlight Sonata (3 versions)
- Fur Elise (multiple arrangements)
- Canon in D
- Clair de Lune
- And 65 more classical masterpieces
- Modular Architecture: Clean separation of concerns with 6 specialized JavaScript modules
- Note Validation: The system checks if you're playing the correct notes from the sheet music
- Progress Tracking: Shows which notes you've played correctly and what's next
- Error Feedback: Displays what note was expected vs. what you played
- Completion Detection: Shows a celebration message when you complete a piece
- Callback System: Loose coupling between modules via event callbacks
- URL Loading: Load scores programmatically with
score.html?url=<score_url>
Lists all available MIDI recordings (cassettes).
Response:
[
{
"name": "recording_name",
"file": "cassettes/recording_name.json",
"created_at": "2025-08-13T16:50:11+02:00"
}
]Saves a new MIDI recording.
Request Body:
{
"name": "my_recording",
"data": [
{
"timestamp": 100,
"data": [144, 60, 100] // MIDI message
}
]
}Response:
{
"success": true,
"message": "Cassette sauvegardée avec succès",
"file": "cassettes/my_recording.json"
}The application extracts musical information from MusicXML files:
- Score Discovery: Indexes scores in
public/data/scores.jsonfor library browsing - Note Extraction: Parses pitch, duration, and timing information
- Measure Analysis: Organizes notes by measure
- Validation: Converts to MIDI note numbers for comparison
- Visualization: Renders sheet music with OpenSheetMusicDisplay
public/data/scores.json contains metadata for all available scores:
{
"baseUrl": "/scores/",
"scores": [
{
"title": "Score Title",
"composer": "Composer Name",
"file": "score-filename.mxl"
}
]
}<note>: Individual musical notes<pitch>: Note pitch (step + octave)<duration>: Note duration<measure>: Musical measures<part>: Instrument parts
The application handles standard MIDI messages:
- Note On:
144(0x90) - Note pressed - Note Off:
128(0x80) - Note released - Note Number: 0-127 (MIDI note range)
- Velocity: 0-127 (how hard the note is played)
Standard MIDI format (3 bytes):
- Status byte (Note On: 144/0x90, Note Off: 128/0x80)
- Note number (0-127)
- Velocity (0-127)
# Run all tests (16 tests, 75 assertions)
bundle exec rake test
# Or run individual test files
bundle exec ruby test/piano_trainer_test.rb
bundle exec ruby test/library_test.rb
# Run with UI (non-headless)
DISABLE_HEADLESS=1 bundle exec rake testtest/piano_trainer_test.rb: 10 tests for core piano training featurestest/library_test.rb: 6 tests for score library functionalitytest/fixtures/simple-score.xml: Basic 4-note test scoretest/fixtures/schumann-melodie.xml: Complex multi-part score (256 notes)public/cassettes/*.json: Various cassette files for playback testing
- Browser console logs show MIDI message parsing
- Test logs capture browser output
- Error messages display in the UI for user feedback
- Web MIDI API: Chrome 43+, Edge 79+, Opera 30+
- ES6 Modules: Modern browsers
- Fetch API: Modern browsers
- Chrome 90+
- Edge 90+
- Opera 76+
If no MIDI device appears when clicking "Connecter clavier MIDI":
- Ensure your MIDI keyboard is connected (USB) or paired (Bluetooth) with your computer
- Check that your browser supports Web MIDI API (Chrome, Edge, Opera)
- Grant MIDI permissions when prompted by the browser
If you're using a Roland FP-30X (or FP-30) keyboard via Bluetooth and it doesn't appear:
- First, pair the keyboard with your operating system's Bluetooth settings
- On your keyboard, press and hold the Bluetooth button together with the first black key (F#/Gb), then release both
- Press Bluetooth again together with the first white key (F), then release both
- Re-pair the keyboard in your OS settings, then refresh the web page
This procedure resets the Bluetooth connection state on the keyboard.
- Code Style: Follow existing patterns and conventions
- Testing: Add tests for new features
- Documentation: Update docs for changes
- Compatibility: Ensure cross-browser support
- Mobile Support: Better mobile UI
- Additional Instruments: Support for bass clef, percussion
- Advanced Features: Tempo detection, metronome
- Export Options: Export recordings to standard formats
- OpenSheetMusicDisplay: MusicXML visualization
- Alpine.js: Reactive UI framework
- Pico CSS: Lightweight styling
For issues, questions, or contributions, please open an issue or pull request on the GitHub repository.