{"id":46574988,"url":"https://github.com/crescent-stdio/wave-roll","last_synced_at":"2026-03-07T09:31:17.149Z","repository":{"id":308504789,"uuid":"1005493115","full_name":"crescent-stdio/wave-roll","owner":"crescent-stdio","description":"[ISMIR 2025 LBD] JavaScript Library for Comparative MIDI Piano-Roll Visualization","archived":false,"fork":false,"pushed_at":"2025-12-02T06:44:08.000Z","size":5088,"stargazers_count":15,"open_issues_count":0,"forks_count":2,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-12-04T22:12:23.216Z","etag":null,"topics":["midi","midi-piano","midi-visualisation","music","music-information-retrieval","piano-roll"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/wave-roll","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/crescent-stdio.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-06-20T10:06:36.000Z","updated_at":"2025-12-03T06:14:46.000Z","dependencies_parsed_at":"2025-08-06T10:17:15.535Z","dependency_job_id":"3d8f8517-369e-4689-9be3-d3af5119e27f","html_url":"https://github.com/crescent-stdio/wave-roll","commit_stats":null,"previous_names":["crescent-stdio/wave-roll"],"tags_count":9,"template":false,"template_full_name":null,"purl":"pkg:github/crescent-stdio/wave-roll","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/crescent-stdio%2Fwave-roll","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/crescent-stdio%2Fwave-roll/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/crescent-stdio%2Fwave-roll/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/crescent-stdio%2Fwave-roll/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/crescent-stdio","download_url":"https://codeload.github.com/crescent-stdio/wave-roll/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/crescent-stdio%2Fwave-roll/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30210829,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-07T09:02:10.694Z","status":"ssl_error","status_checked_at":"2026-03-07T09:02:08.429Z","response_time":53,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["midi","midi-piano","midi-visualisation","music","music-information-retrieval","piano-roll"],"created_at":"2026-03-07T09:31:16.562Z","updated_at":"2026-03-07T09:31:17.111Z","avatar_url":"https://github.com/crescent-stdio.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# WaveRoll\n\n\u003cdiv style=\"text-align: center;\"\u003e\n  \u003cimg src=\"./wave-roll-logo.png\" alt=\"WaveRoll\" width=\"40%\"/\u003e\n\u003c/div\u003e\n\n\u003e **WaveRoll** is an interactive [JavaScript library](https://www.npmjs.com/package/wave-roll) that enables comparative visualization and synchronized playback of multiple MIDI piano rolls on a browser. \n\n![NPM Version](https://img.shields.io/npm/v/wave-roll)\n![NPM License](https://img.shields.io/npm/l/wave-roll)\n[![arXiv](https://img.shields.io/badge/arXiv-2511.09562-b31b1b.svg)](https://arxiv.org/abs/2511.09562)\n\n![Screenshot of WaveRoll](./wave-roll.png)\n\n- You can try the web demo at [https://crescent-stdio.github.io/wave-roll/](https://crescent-stdio.github.io/wave-roll/).\n- NPM package: [https://www.npmjs.com/package/wave-roll](https://www.npmjs.com/package/wave-roll)\n- VS Code Extension: [WaveRoll Studio](https://github.com/crescent-stdio/wave-roll-studio) (also available on Open VSX)\n- Multi-instrument MIDI files are supported with automatic GM instrument mapping and per-track mute/volume/visibility controls.\n\n\n## Installation\n\n### [NPM: wave-roll](https://www.npmjs.com/package/wave-roll)\n\n```bash\nnpm install wave-roll\n```\n\n### Usage (NPM)\n\n```html\n\u003c!DOCTYPE html\u003e\n\u003chtml\u003e\n\u003chead\u003e\n  \u003cscript type=\"module\"\u003e\n    import 'wave-roll';\n  \u003c/script\u003e\n\u003c/head\u003e\n\u003cbody\u003e\n  \u003cwave-roll\n    style=\"width: 100%;\"\n    files='[\n      {\"path\": \"./audio/track.wav\", \"name\": \"Track Audio\", \"type\": \"audio\"},\n      {\"path\": \"./midi/ground_truth.mid\", \"name\": \"Ground Truth\", \"type\": \"midi\"},\n      {\"path\": \"./midi/modelA.mid\", \"name\": \"Model A\", \"type\": \"midi\"}\n    ]'\u003e\n  \u003c/wave-roll\u003e\n\u003c/body\u003e\n\u003c/html\u003e\n```\n\n### Using CDN (ES Module)\nYou can try the ES Module demo [here](https://crescent-stdio.github.io/wave-roll/examples/es-module/) and [sample codes](https://github.com/crescent-stdio/wave-roll/blob/main/docs/examples/es-module/index.html).\n\n\n```html\n\u003c!DOCTYPE html\u003e\n\u003chtml\u003e\n\u003chead\u003e\n  \u003cscript type=\"module\"\u003e\n    import 'https://cdn.jsdelivr.net/npm/wave-roll@latest/dist/wave-roll.es.js';\n  \u003c/script\u003e\n\u003c/head\u003e\n\u003cbody\u003e\n  \u003cwave-roll\n    style=\"width: 100%;\"\n    files='[\n      {\"path\": \"./audio/track.wav\", \"name\": \"Track Audio\", \"type\": \"audio\"},\n      {\"path\": \"./midi/ground_truth.mid\", \"name\": \"Ground Truth\", \"type\": \"midi\"},\n      {\"path\": \"./midi/modelA.mid\", \"name\": \"Model A\", \"type\": \"midi\"}\n    ]'\u003e\n  \u003c/wave-roll\u003e\n\u003c/body\u003e\n\u003c/html\u003e\n```\n\n### Using UMD CDN (Traditional Script)\nYou can try the UMD demo [here](https://crescent-stdio.github.io/wave-roll/examples/umd/) and [sample codes](https://github.com/crescent-stdio/wave-roll/blob/main/docs/examples/umd/index.html).\n\n```html\n\u003c!DOCTYPE html\u003e\n\u003chtml\u003e\n\u003chead\u003e\n  \u003cscript src=\"https://cdn.jsdelivr.net/npm/wave-roll@latest/dist/wave-roll.umd.js\"\u003e\u003c/script\u003e\n\u003c/head\u003e\n\u003cbody\u003e\n  \u003cwave-roll\n    style=\"width: 100%;\"\n    files='[\n      {\"path\": \"./audio/track.wav\", \"name\": \"Track Audio\", \"type\": \"audio\"},\n      {\"path\": \"./midi/ground_truth.mid\", \"name\": \"Ground Truth\", \"type\": \"midi\"},\n      {\"path\": \"./midi/modelA.mid\", \"name\": \"Model A\", \"type\": \"midi\"}\n    ]'\u003e\n  \u003c/wave-roll\u003e\n\u003c/body\u003e\n\u003c/html\u003e\n```\n\n### GitHub Pages Usage\n\nFor GitHub Pages deployment, you can use the CDN directly:\n\n```html\n\u003c!DOCTYPE html\u003e\n\u003chtml\u003e\n\u003chead\u003e\n  \u003cmeta charset=\"UTF-8\"\u003e\n  \u003cmeta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"\u003e\n  \u003ctitle\u003eWaveRoll Demo\u003c/title\u003e\n  \u003cscript type=\"module\"\u003e\n    import 'https://cdn.jsdelivr.net/npm/wave-roll@latest/dist/wave-roll.es.js';\n  \u003c/script\u003e\n\u003c/head\u003e\n\u003cbody\u003e\n  \u003cwave-roll\n    style=\"width: 100%\"\n    files='[\n      {\"path\": \"./audio/track.wav\", \"name\": \"Track Audio\", \"type\": \"audio\"},\n      {\"path\": \"./midi/ground_truth.mid\", \"name\": \"Ground Truth\", \"type\": \"midi\"},\n      {\"path\": \"./midi/modelA.mid\", \"name\": \"Model A\", \"type\": \"midi\"}\n    ]'\u003e\n  \u003c/wave-roll\u003e\n\u003c/body\u003e\n\u003c/html\u003e\n```\n\n### In React\n\n```jsx\nimport 'wave-roll';\n\nfunction MidiComparison() {\n  const files = [\n    { path: \"./audio/track.wav\", name: \"Track Audio\", type: \"audio\" },\n    { path: \"./midi/ground_truth.mid\", name: \"Ground Truth\", type: \"midi\" },\n    { path: \"./midi/modelA.mid\", name: \"Model A\", type: \"midi\" }\n  ];\n\n  return (\n    \u003cwave-roll \n      style={{ width: '100%' }}\n      files={JSON.stringify(files)}\n    /\u003e\n  );\n}\n```\n\n### Standalone Demo\n\nTry the standalone version with drag-and-drop file upload interface:\n\n- **Live Demo**: [https://crescent-stdio.github.io/wave-roll/standalone.html](https://crescent-stdio.github.io/wave-roll/standalone.html)\n- **Source Code**: [docs/examples/standalone.html](https://github.com/crescent-stdio/wave-roll/blob/main/docs/examples/standalone.html)\n\nThe standalone demo is a minimal, ready-to-use interface where users can directly upload and compare MIDI/audio files without any additional setup.\n\n### Using in Jupyter Notebook\n\nYou can embed WaveRoll in Jupyter notebooks using `IFrame`:\n\n```python\nfrom IPython.display import IFrame\nIFrame(src='https://crescent-stdio.github.io/wave-roll/standalone.html', width='100%', height='800px')\n```\n\n- **Example Notebook**: [docs/examples/jupyter-notebook/wave-roll-demo.ipynb](https://github.com/crescent-stdio/wave-roll/blob/main/docs/examples/jupyter-notebook/wave-roll-demo.ipynb)\n- **Open in Google Colab**: [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/crescent-stdio/wave-roll/blob/main/docs/examples/jupyter-notebook/wave-roll-demo.ipynb)\n\nThis is particularly useful for music information retrieval (MIR) research, allowing you to visualize and compare transcription results directly in your analysis notebooks.\n\n## API\n\n### Attributes\n\n| Attribute | Type | Description |\n|-----------|------|-------------|\n| `files` | `string` | JSON string array of file objects with `path` and `name` properties |\n| `style` | `string` | CSS styles for the component container |\n| `readonly` | `boolean attribute` | When present, hides UI controls for adding/removing files (read‑only mode) |\n\n#### Read‑only Mode\n\nAdd 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):\n\n```html\n\u003cwave-roll\n  style=\"width: 100%; height: 600px;\"\n  files='[\n    {\"path\": \"./audio/track.wav\", \"name\": \"Track Audio\", \"type\": \"audio\"},\n    {\"path\": \"./midi/ground_truth.mid\", \"name\": \"Ground Truth\", \"type\": \"midi\"},\n    {\"path\": \"./midi/modelA.mid\", \"name\": \"Model A\", \"type\": \"midi\"}\n  ]'\n  readonly\n\u003e\u003c/wave-roll\u003e\n```\n\nYou can toggle this at runtime too:\n\n```js\nconst el = document.querySelector('wave-roll');\nel.setAttribute('readonly', '');      // enable read-only\nel.removeAttribute('readonly');       // disable read-only\n```\n\n### File Object Structure\n\n```typescript\ninterface FileItem {\n  path: string;   // URL or relative path to the file\n  name: string;   // Display name shown in UI\n  type?: \"midi\" | \"audio\"; // Defaults to \"midi\" when omitted\n  color?: string; // Custom color for track visualization (hex or CSS color)\n}\n```\n\n### JavaScript API\n\nControl the player programmatically:\n\n```javascript\nconst waveRoll = document.querySelector('wave-roll');\n\n// Playback controls\nawait waveRoll.play();\nwaveRoll.pause();\nwaveRoll.seek(30);  // Seek to 30 seconds\n\n// State\nconsole.log(waveRoll.isPlaying);  // boolean\nconsole.log(waveRoll.getState()); // { currentTime, duration, tempo, ... }\n```\n\n### Tempo Control\n\nWaveRoll includes an interactive tempo control with popover input:\n\n- Click the BPM badge in the player controls to adjust tempo\n- Supports a wide range of tempo adjustments\n- Audio playback automatically adjusts with pitch preservation\n\n### MIDI Export\n\nExport MIDI files with modified tempo via the Settings panel:\n\n1. Adjust tempo using the tempo control\n2. Open Settings (gear icon)\n3. Click \"Export with Current Tempo\"\n\nThe exported file preserves all note data with updated tempo metadata.\n\n**Programmatic usage:**\n\n```javascript\nimport { exportMidiWithTempo } from 'wave-roll';\n\n// Export MIDI with new tempo (triggers download)\nawait exportMidiWithTempo(midiFile, 144);\n\n// Export as Blob for custom handling\nimport { exportMidiWithTempoAsBlob } from 'wave-roll';\nconst blob = await exportMidiWithTempoAsBlob(midiFile, 144);\n```\n\n## Development\n\n```bash\n# Install dependencies\npnpm install\n\n# Run development server\npnpm dev\n\n# Build for production\npnpm build\n\n# Run tests\npnpm test\n```\n\n## Acknowledgments\nThis library includes functionality ported from [mir_eval](https://github.com/mir-evaluation/mir_eval), Music Information Retrieval evaluation library.\n\n## License\n\nMIT License - see [LICENSE](LICENSE) file for details\n\n## Citation\n\nIf you use WaveRoll in your research, please cite:\n\n```bibtex\n@inproceedings{waveroll2025,\n  title={WaveRoll: JavaScript Library for Comparative MIDI Piano-Roll Visualization},\n  author={Park, Hannah and Jeong, Dasaem},\n  booktitle={Proceedings of 26th International Society for Music Information Retrieval Conference (ISMIR)},\n  year={2025}\n}\n```","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcrescent-stdio%2Fwave-roll","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcrescent-stdio%2Fwave-roll","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcrescent-stdio%2Fwave-roll/lists"}