Skip to content

joekotvas/RiffScore

Repository files navigation

RiffScore

npm version license

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.

RiffScore Editor

Installation

npm install riffscore

Quick Start

import { 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.

With Configuration

<RiffScore id="my-editor" config={{
  score: { 
    staff: 'treble',      // 'grand' | 'treble' | 'bass' | 'alto' | 'tenor'
    measureCount: 4,
    keySignature: 'G'
  }
}} />

See Configuration for all options.

Read-Only Mode

<RiffScore config={{
  ui: { showToolbar: false },
  interaction: { isEnabled: false }
}} />

See Interaction Configuration for more display modes.

Programmatic Control

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 selection

Note: 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.


Features

Core Editing

  • 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.

Chord Symbols

  • 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.

Page View & Print (Experimental)

⚠️ 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).

Machine-Addressable API

  • 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) and api.on('batch', callback).
  • Transaction Batching: Atomic operations with beginTransaction/commitTransaction for single undo steps.
  • Structured Feedback: Unified result objects and sticky error states for robust scripting.
  • Playback API: play(), pause(), stop(), rewind(), setInstrument() for programmatic audio control.

Engines

  • 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).

Keyboard Shortcuts

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 Print
Cmd+Shift+F Ctrl+Shift+F Toggle fullscreen

See the Interaction Guide for the complete keyboard reference.


Documentation

📚 View Full Documentation Index
Browse all guides, architectural records, and migration history.

Getting Started

Guide Description
📖 Configuration All config options for <RiffScore />
🎹 API Reference Imperative API for script control
📗 Cookbook Task-oriented recipes and examples

Deep Dives

Guide Description
🎨 Interaction Design UX philosophy and editor states
⌨️ Keyboard Navigation Navigation state machine details
🎯 Selection Model Multi-selection and vertical extension

Architecture

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

Contributing

Guide Description
🤝 Contributing Dev setup and guidelines
🧪 Testing Test patterns and fixtures
📋 Changelog Release history

Imperative API

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.


Repository Structure

riffscore/
├── src/        ← Library source
├── demo/       ← Next.js demo app
├── docs/       ← Documentation
├── dist/       ← Built library (ESM/CJS/DTS)
└── tsup.config.ts

Development

# Install dependencies
npm install
cd demo && npm install

# Build library
npm run build

# Run demo
npm run demo:dev

Coming Soon

  • Import: ABC and MusicXML import
  • Clipboard API: Copy, cut, and paste operations
  • Move Operations: Drag-and-drop and keyboard-based event moving