WaveRoll is an interactive JavaScript library that enables comparative visualization and synchronized playback of multiple MIDI piano rolls on a browser.
- You can try the web demo at https://crescent-stdio.github.io/wave-roll/.
- NPM package: https://www.npmjs.com/package/wave-roll
npm install wave-roll<!DOCTYPE html>
<html>
<head>
<script type="module">
import 'wave-roll';
</script>
</head>
<body>
<wave-roll
style="width: 100%;"
files='[
{"path": "./audio/track.wav", "name": "Track Audio", "type": "audio"},
{"path": "./midi/ground_truth.mid", "name": "Ground Truth", "type": "midi"},
{"path": "./midi/modelA.mid", "name": "Model A", "type": "midi"}
]'>
</wave-roll>
</body>
</html>You can try the ES Module demo here and sample codes.
<!DOCTYPE html>
<html>
<head>
<script type="module">
import 'https://cdn.jsdelivr.net/npm/wave-roll@latest/dist/wave-roll.es.js';
</script>
</head>
<body>
<wave-roll
style="width: 100%;"
files='[
{"path": "./audio/track.wav", "name": "Track Audio", "type": "audio"},
{"path": "./midi/ground_truth.mid", "name": "Ground Truth", "type": "midi"},
{"path": "./midi/modelA.mid", "name": "Model A", "type": "midi"}
]'>
</wave-roll>
</body>
</html>You can try the UMD demo here and sample codes.
<!DOCTYPE html>
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/wave-roll@latest/dist/wave-roll.umd.js"></script>
</head>
<body>
<wave-roll
style="width: 100%;"
files='[
{"path": "./audio/track.wav", "name": "Track Audio", "type": "audio"},
{"path": "./midi/ground_truth.mid", "name": "Ground Truth", "type": "midi"},
{"path": "./midi/modelA.mid", "name": "Model A", "type": "midi"}
]'>
</wave-roll>
</body>
</html>For GitHub Pages deployment, you can use the CDN directly:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>WaveRoll Demo</title>
<script type="module">
import 'https://cdn.jsdelivr.net/npm/wave-roll@latest/dist/wave-roll.es.js';
</script>
</head>
<body>
<wave-roll
style="width: 100%"
files='[
{"path": "./audio/track.wav", "name": "Track Audio", "type": "audio"},
{"path": "./midi/ground_truth.mid", "name": "Ground Truth", "type": "midi"},
{"path": "./midi/modelA.mid", "name": "Model A", "type": "midi"}
]'>
</wave-roll>
</body>
</html>import 'wave-roll';
function MidiComparison() {
const files = [
{ path: "./audio/track.wav", name: "Track Audio", type: "audio" },
{ path: "./midi/ground_truth.mid", name: "Ground Truth", type: "midi" },
{ path: "./midi/modelA.mid", name: "Model A", type: "midi" }
];
return (
<wave-roll
style={{ width: '100%' }}
files={JSON.stringify(files)}
/>
);
}Try the standalone version with drag-and-drop file upload interface:
- Live Demo: https://crescent-stdio.github.io/wave-roll/standalone.html
- Source Code: docs/examples/standalone.html
The standalone demo is a minimal, ready-to-use interface where users can directly upload and compare MIDI/audio files without any additional setup.
You can embed WaveRoll in Jupyter notebooks using IFrame:
from IPython.display import IFrame
IFrame(src='https://crescent-stdio.github.io/wave-roll/standalone.html', width='100%', height='800px')- Example Notebook: docs/examples/jupyter-notebook/wave-roll-demo.ipynb
- Open in Google Colab:
This is particularly useful for music information retrieval (MIR) research, allowing you to visualize and compare transcription results directly in your analysis notebooks.
| Attribute | Type | Description |
|---|---|---|
files |
string |
JSON string array of file objects with path and name properties |
style |
string |
CSS styles for the component container |
readonly |
boolean attribute |
When present, hides UI controls for adding/removing files (read‑only mode) |
Add the readonly attribute to disable file addition and deletion in the Settings modal (the "Add MIDI Files" button and per‑file delete buttons are hidden):
<wave-roll
style="width: 100%; height: 600px;"
files='[
{"path": "./audio/track.wav", "name": "Track Audio", "type": "audio"},
{"path": "./midi/ground_truth.mid", "name": "Ground Truth", "type": "midi"},
{"path": "./midi/modelA.mid", "name": "Model A", "type": "midi"}
]'
readonly
></wave-roll>You can toggle this at runtime too:
const el = document.querySelector('wave-roll');
el.setAttribute('readonly', ''); // enable read-only
el.removeAttribute('readonly'); // disable read-onlyinterface FileItem {
path: string; // URL or relative path to the file
name: string; // Display name shown in UI
type?: "midi" | "audio"; // Defaults to "midi" when omitted
}# Install dependencies
npm install
# Run development server
npm run dev
# Build for production
npm run build
# Run tests
npm testThis library includes functionality ported from mir_eval, Music Information Retrieval evaluation library.
MIT License - see LICENSE file for details
If you use WaveRoll in your research, please cite:

