RiffScore is a self-hostable, embeddable sheet music editor for React.
Unlike commercial platforms that require users to leave your site or pay subscription fees, RiffScore allows you to embed interactive, editable scores directly into your application.
npm install riffscoreimport { RiffScore } from 'riffscore';
function App() {
return <RiffScore id="my-editor" />;
}That's it! RiffScore renders a fully interactive grand staff editor with sensible defaults.
Note: Styles and fonts are bundled automatically — no separate CSS import or font setup required.
<RiffScore id="my-editor" config={{
score: {
staff: 'treble', // 'grand' | 'treble' | 'bass' | 'alto' | 'tenor'
measureCount: 4,
keySignature: 'G'
}
}} />See Configuration for all options.
<RiffScore config={{
ui: { showToolbar: false },
interaction: { isEnabled: false }
}} />See Interaction Configuration for more display modes.
const api = window.riffScore.get('my-editor');
api.select(0) // Select first measure (0-indexed)
.addNote('C4', 'quarter') // Add a quarter note
.addNote('E4') // Add with current duration
.play(); // Play from selectionNote: All API indices are 0-based. Use
select(0)for the first measure,select(1)for the second, etc.
See the Cookbook for more recipes.
- Self-Hostable: No external dependencies or platform lock-in.
- Embeddable: Drop it into any React application.
- Configurable: Full control over UI, interactions, and score content.
- SMuFL Compliance: Beautiful engraving using the Bravura font.
- Export Options: JSON, MusicXML, and ABC notation export.
- Theming: Built-in dark, light, cool, and warm themes.
- Click-to-Edit: Click above any beat to add or edit chord symbols.
- Flexible Input: Letter names (
Cmaj7), solfège (Do), Roman numerals (IV7). - Multiple Notations: Display in letter, Roman numeral, Nashville number, or solfège.
- Playback: Chord voicings play back alongside the score.
- Full API: CRUD, selection, and navigation via
addChord(),selectChord(), etc.
⚠️ Experimental — not part of the stable feature set yet. Page View is in active development with known layout defects on multi-system grand-staff scores. It is being hardened in a dedicated milestone (see the roadmap). The scroll-based editor is the supported default; the items below describe the in-progress capability.
- Multi-System Layout: Automatic system breaks with first system indent and justified measures.
- Multi-Page Pagination: True page breaks with visual gaps between pages.
- Layout Options: Letter/A4 page sizes, margin presets, staff size (50-150%), system spacing.
- Score Setup Dialog: Configure metadata (title, composer, copyright) and layout via
Cmd+,. - Inline Editing: Click directly on title, composer, or copyright to edit in place.
- Print Support: Professional PDF output via native browser dialog (
Cmd+P).
- Imperative Control: Programmatically control the score via
window.riffScore(API Reference) - Fluent Chaining:
api.select(1).addNote('C4').play()— chainable methods for concise scripting. - Event Subscriptions: React to state changes with
api.on('score', callback)andapi.on('batch', callback). - Transaction Batching: Atomic operations with
beginTransaction/commitTransactionfor single undo steps. - Structured Feedback: Unified
resultobjects and sticky error states for robust scripting. - Playback API:
play(),pause(),stop(),rewind(),setInstrument()for programmatic audio control.
- Music Theory: Powered by Tonal.js for scales, chords, and transposition.
- Audio Playback: Tone.js sampler with multiple instrument support.
- MIDI Input: Connect a MIDI keyboard for note entry (experimental).
| Mac | Windows | Action |
|---|---|---|
| Entry & Editing | ||
1-7 |
1-7 |
Set duration (64th to whole) |
. |
. |
Toggle dotted |
R |
R |
Toggle note/rest mode |
T |
T |
Toggle tie |
- / = / 0 |
- / = / 0 |
Flat / Sharp / Natural |
Enter |
Enter |
Insert note/rest at cursor |
↑ / ↓ |
↑ / ↓ |
Transpose selection |
| Navigation & Selection | ||
← / → |
← / → |
Previous / Next event |
Shift+←/→ |
Shift+←/→ |
Extend selection horizontally |
Cmd+↑/↓ |
Ctrl+↑/↓ |
Navigate within chord |
Alt+↑/↓ |
Alt+↑/↓ |
Switch staff (grand staff) |
Cmd+Shift+↑/↓ |
Ctrl+Shift+↑/↓ |
Extend selection vertically |
Cmd+A |
Ctrl+A |
Select all (progressive) |
Esc |
Esc |
Clear selection / Cancel |
| Playback | ||
Space |
Space |
Play / Pause |
P |
P |
Play from selection |
Shift+Space |
Shift+Space |
Replay from last start |
Shift+Alt+Space |
Shift+Alt+Space |
Play from beginning |
| History | ||
Cmd+Z |
Ctrl+Z |
Undo |
Cmd+Shift+Z |
Ctrl+Y |
Redo |
| View | ||
Cmd+\ |
Ctrl+\ |
Toggle scroll/page view |
Cmd+, |
Ctrl+, |
Open Score Setup dialog |
Cmd+P |
Ctrl+P |
|
Cmd+Shift+F |
Ctrl+Shift+F |
Toggle fullscreen |
See the Interaction Guide for the complete keyboard reference.
📚 View Full Documentation Index
Browse all guides, architectural records, and migration history.
| Guide | Description |
|---|---|
| 📖 Configuration | All config options for <RiffScore /> |
| 🎹 API Reference | Imperative API for script control |
| 📗 Cookbook | Task-oriented recipes and examples |
| Guide | Description |
|---|---|
| 🎨 Interaction Design | UX philosophy and editor states |
| ⌨️ Keyboard Navigation | Navigation state machine details |
| 🎯 Selection Model | Multi-selection and vertical extension |
| Guide | Description |
|---|---|
| 📘 Architecture | Technical overview and design principles |
| 🧩 Coding Patterns | Common patterns and architectural standards |
| 🧱 Data Model | Score schema and quant system |
| 🔧 Commands | Command pattern reference |
| 🎼 Layout Engine | Engraving and positioning |
| 📜 ADRs | Architecture Decision Records |
| Guide | Description |
|---|---|
| 🤝 Contributing | Dev setup and guidelines |
| 🧪 Testing | Test patterns and fixtures |
| 📋 Changelog | Release history |
Control the editor programmatically from external scripts:
const api = window.riffScore.get('my-editor');
// Build a chord (indices are 0-based)
api.select(0) // First measure
.addNote('C4', 'quarter')
.addNote('E4')
.addNote('G4')
.addTone('C5'); // Stack into chord
// Batch operations (single undo step)
api.beginTransaction();
for (let i = 0; i < 16; i++) {
api.addNote(`C${(i % 3) + 4}`, 'sixteenth');
}
api.commitTransaction('Scale run');
// Subscribe to changes
api.on('batch', (e) => console.log(`Committed: ${e.label}`));See the API Reference and Cookbook for all available methods.
riffscore/
├── src/ ← Library source
├── demo/ ← Next.js demo app
├── docs/ ← Documentation
├── dist/ ← Built library (ESM/CJS/DTS)
└── tsup.config.ts
# Install dependencies
npm install
cd demo && npm install
# Build library
npm run build
# Run demo
npm run demo:dev- Import: ABC and MusicXML import
- Clipboard API: Copy, cut, and paste operations
- Move Operations: Drag-and-drop and keyboard-based event moving
