Piano roll

@xromdev/pianoroll v0.2.0 · MIT

Notes falling onto a keyboard: a block's bottom edge touches its key at the instant the note sounds, its length is the duration, its colour the voice. The blocks are canvas, the keys are DOM and CSS, and no frame goes through a framework — the view follows your clock itself and writes to a key only when that key changes. Below, any NSF is decoded into notes from what the sound chip does, and played.

npm install @xromdev/pianorollnpm ↗GitHub ↗README ↗NSF player →
framework renders per frame
0
framework renders per frame
gzipped, React optional
~6 KB
gzipped, React optional
when paused or scrolled away
0 ms
when paused or scrolled away
keys, in a real piano’s proportions
88
keys, in a real piano’s proportions
or drop one here — it stays in your browser
Demo tune
four bars from the APU playground, looped
pulse 1pulse 2trianglenoise
measuring…

Use it — any page

import { PianoRollView } from '@xromdev/pianoroll';
import '@xromdev/pianoroll/pianoroll.css';

const roll = new PianoRollView(document.querySelector('#roll'), { lookAhead: 2.5 });
roll.setNotes([
  // midi, start and duration in seconds; the track picks the colour
  { midi: 60, start: 0, duration: 0.5, track: 'pulse1' },
  { midi: 64, start: 0.5, duration: 0.5, track: 'pulse2', velocity: 0.6 },
  { midi: 43, start: 0, duration: 1, track: 'triangle' },
]);
roll.play(() => audioContext.currentTime - startedAt); // follows your clock

Use it — React

import { PianoRoll } from '@xromdev/pianoroll/react';
import '@xromdev/pianoroll/pianoroll.css';

// With clock, the roll animates on its own frames — React never re-renders for it.
<PianoRoll notes={notes} clock={() => audio.currentTime} height={420} />

// Or drive it yourself — a scrubber, a paused picture:
<PianoRoll notes={notes} time={position} />