A piano roll editor for adding musical expression to quantized MIDI scores. Import a MusicXML file, shape note velocities and articulations by hand, draw sustain pedal and tempo curves, then play the result back through an external synth via Web MIDI.
Expression comes from deliberate editing, not randomness. There is no auto-humanization — the tool is an instrument.
- MusicXML import — parses
score-partwiseexports from MuseScore and similar software, including ties, dynamics, multi-voice measures, multiple rests, and tremolo expansion - Piano roll canvas — 88-key range (A0–C8), velocity-colored notes, snap-to-grid note placement and resizing, rect selection, note muting, undo/redo
- Velocity editing — apply a shaped ramp across a selection (Linear / Ease in / Ease out / S-curve), set absolute values (5–120 grid), or nudge by ±1/5/10; per-key velocity curve for device calibration
- Sustain pedal lane — draw a CC64 curve with snappable control points; interpolated value is seeded correctly on mid-piece playback start
- Tempo lane — draw a tempo ratio curve (×0.8–×1.2) as a monotone-cubic spline that shapes playback timing and overall piece duration
- Minimap — full-piece overview with viewport indicator; click or drag to pan
- Bookmarks — ruler markers added with Ctrl+click;
←/→seek between them - Web MIDI output — lookahead scheduler (30 ms tick / 150 ms window) sends note on/off and CC64 to any browser MIDI port (e.g. FluidSynth, hardware piano); an adjustable re-strike gap gives a repeated key's action time to reset
- Project save/load — versioned JSON preserving all edits; auto-save to
localStoragewith per-piece view state restoration - No build step, no dependencies — vanilla JS ES modules, Canvas 2D, served with any static HTTP server
- A browser with Web MIDI API support (Chrome / Edge; Firefox needs a site-permission add-on, which it prompts to install on first MIDI request)
- A MIDI output port the browser can see (e.g. FluidSynth via a virtual MIDI loopback, or a hardware instrument)
- Python 3 (or any static file server) to serve the files over localhost
git clone https://github.com/phinau/pianizer.git
cd pianizer
python3 -m http.serverOpen http://localhost:8000 in Chrome or Edge, click Connect in the toolbar
to pick a MIDI output port, then load a MusicXML file.
Press ? at any time to open an in-app overlay listing the shortcuts below.
| Key / gesture | Action |
|---|---|
| Click note | Select just that note |
| Shift+click note | Toggle note in the selection (add, or remove if already selected) |
| Drag empty | Rectangle select (replaces selection) |
| Shift+drag empty | Rectangle select (extends selection) |
| Click empty | Clear selection |
| Ctrl+Z | Undo (removes the last selection change) |
| Escape | Clear selection |
| Key / gesture | Action |
|---|---|
| 1 | Velocity tool |
| 2 | Curve tool (bake an eased velocity ramp over the selection) |
| 3 | Velocity delta tool |
| Alt+click | Insert note at cursor (one snap-step long) |
| Alt+drag | Insert note, dragging sets its duration |
| Drag note body | Move selection (axis-locked: timing or pitch) |
| Drag left / right edge | Resize note start / end (handles show on hover) |
| Delete / Backspace | Delete selected notes |
| M | Mute / unmute selected notes |
| Ctrl+Z / Ctrl+Y | Undo / redo |
| Key / gesture | Action |
|---|---|
| Scroll | Pan horizontal |
| Ctrl+scroll | Zoom horizontal |
| Right-drag | Pan (horizontal + vertical) |
| Home / End | Scroll to start / end |
| Key / gesture | Action |
|---|---|
| Space | Play / pause |
| Click empty roll / ruler | Seek playhead |
| Drag ruler | Scrub playhead |
| Ctrl+click ruler | Add bookmark |
| Ctrl+right-click bookmark | Remove bookmark |
| ← / → | Seek to prev / next bookmark |
| Key / gesture | Action |
|---|---|
| Click empty | Add control point (snaps to grid and Y to neighbours / baseline) |
| Ctrl+click | Add without snapping |
| Drag point | Move control point (snaps to grid and Y; hold Ctrl to disable) |
| Right-click | Remove nearest control point |
pianizer/
index.html entry point, layout, tool windows, velocity curve editor, autosave
engine/
state.js AppState (EventTarget) — single source of truth
musicxml.js MusicXML score-partwise parser
midi-out.js Web MIDI output and lookahead scheduler
ui/
roll.js PianoRoll canvas — render, selection, pan, note editing
curve-lane.js CurveLane base class — shared pedal/tempo lane logic
pedal-lane.js PedalLane — sustain pedal curve (CC64, value 0–1)
tempo-lane.js TempoLane — tempo ratio curve (0.8–1.2)
minimap.js MiniMap — full-piece overview, viewport indicator
toolbar.js <ph-toolbar> custom element
dom-utils.js Layout constants and shared helpers
npm install # installs Vitest + Playwright (dev only)
npm test # engine unit tests + Playwright UI smoke tests
npm run test:engine # unit tests only
npm run test:ui # UI tests only (starts a static server automatically)MIT — see LICENSE.