• English
  • @muse-player/core

    Core React hooks and components for MXL/MusicXML score rendering and playback.

    pnpm add @muse-player/core

    Peer dependencies: react >=18, react-dom >=18

    Warning

    This package does not include Verovio or any MXL parsing capability. It only consumes pre-rendered static artifacts (manifest.json, data.json, SVG pages) produced by @muse-player/server or the muse-render CLI. You must pre-render your MXL files before using these hooks.


    Hooks

    useScore

    Loads pre-rendered score data from a manifest URL. The URL must point to a manifest.json produced by @muse-player/server or muse-render. There is no way to load raw MXL/MusicXML files through this hook.

    function useScore(): ScoreState

    Parameters: None

    Returns:

    FieldTypeDescription
    loadResult(url: string) => Promise<void>Fetches manifest at url, loads all pages and data
    loadingbooleantrue while fetch is in progress
    errorstring | nullError message if load failed
    svgstringSVG markup for the current page
    currentPagenumberCurrently active page (1-indexed)
    totalPagesnumberTotal number of SVG pages
    renderPage(page: number) => voidSwitch to a different page (1-indexed)
    midiBase64stringBase64-encoded MIDI data for playback
    timeMapTimeMapEntry[]Time-indexed note events
    scoreDataScoreData | null{ title, totalPages } from the manifest
    getElementsAtTime(ms: number) => { notes: string[]; page: number }Active note IDs and page at a millisecond timestamp
    getElementAttr(xmlId: string) => Record<string, string>Verovio attributes for a note element
    getPageWithElement(xmlId: string) => numberPage number containing the element (1-indexed, 0 if not found)
    getTimeForElement(xmlId: string) => numberTime in seconds when a note first appears in the timemap

    usePlayback

    Full MIDI playback engine. Parses base64 MIDI, schedules notes via Tone.js, tracks time and measure, and supports real-time tempo changes.

    function usePlayback(
      midiBase64: string,
      timeMap: TimeMapEntry[],
      onTimeUpdate?: (time: number) => void,
      onNotesUpdate?: (noteIds: string[]) => void,
      instrument?: { sampler: any } | null,
    ): PlaybackControls

    Parameters:

    ParameterTypeDescription
    midiBase64stringBase64-encoded MIDI data from useScore
    timeMapTimeMapEntry[]Timemap from useScore
    onTimeUpdate(time: number) => voidCalled on each 16th-note tick with current time in seconds. Use to drive page changes via getElementsAtTime.
    onNotesUpdate(noteIds: string[]) => voidCalled when new notes are activated. Receives array of active XML note IDs.
    instrument{ sampler: any } | nullTone.Sampler instance from usePianoSampler. If null, falls back to a basic PolySynth.

    Returns:

    FieldTypeDescription
    play() => Promise<void>Starts audio context and playback
    pause() => voidPauses (preserves position)
    stop() => voidStops and resets to beginning
    seek(seconds: number) => voidJumps to absolute time
    seekToMeasure(index: number) => voidJumps to a measure by timemap index
    playingbooleanWhether playback is active
    currentTimenumberCurrent position in seconds
    totalDurationnumberTotal MIDI duration in seconds
    currentMeasurenumberCurrent measure index (0-indexed)
    temponumberCurrent BPM (default: 120)
    updateTempo(bpm: number) => voidChanges BPM in real-time
    Warning

    The first play() call triggers Tone.start() which is required by browser audio policy. This must be called from a user gesture handler.


    usePianoSampler

    Loads realistic piano samples via Tone.Sampler with a compressor and reverb effects chain.

    function usePianoSampler(options?: UsePianoSamplerOptions): PianoSamplerState

    Parameters:

    ParameterTypeDescription
    options.baseUrlstringBase URL for piano sample MP3 files. Default: CDN at nbrosowsky.github.io/tonejs-instruments/samples/piano/

    Returns:

    FieldTypeDescription
    loadingbooleantrue while samples are downloading
    readybooleantrue when sampler is ready to play
    errorstring | nullError message if loading failed
    samplerTone.Sampler | nullPass to usePlayback as { sampler }

    The audio chain is: Sampler -> Compressor -> Reverb -> Destination


    useAutoScroll

    Automatically scrolls the score container during playback to keep the current measure visible.

    function useAutoScroll(
      containerRef: React.RefObject<HTMLDivElement | null>,
      currentMeasure: number,
      isPlaying: boolean,
    ): AutoScrollState

    Parameters:

    ParameterTypeDescription
    containerRefRefObject<HTMLDivElement>Ref to the scrollable score container
    currentMeasurenumberCurrent measure from usePlayback
    isPlayingbooleanWhether playback is active

    Returns:

    FieldTypeDescription
    autoScrollEnabledbooleanWhether auto-scroll is active
    setAutoScrollEnabled(enabled: boolean) => voidManually toggle auto-scroll

    Manual scrolling disables auto-scroll for 5 seconds. It re-enables immediately when playback starts.


    useNoteHighlight

    Draws colored overlay rectangles around actively playing notes on the score. Blue for right hand (staff 1), red for left hand (staff 2).

    function useNoteHighlight(
      containerRef: React.RefObject<HTMLDivElement | null>,
      activeNoteIds: string[],
      getElementAttribute: (xmlId: string) => Record<string, string>,
    ): void

    Parameters:

    ParameterTypeDescription
    containerRefRefObject<HTMLDivElement>Ref to the score container
    activeNoteIdsstring[]Active note IDs from usePlayback's onNotesUpdate
    getElementAttribute(xmlId: string) => Record<string, string>From useScore's getElementAttr

    This hook is purely side-effectful — it returns nothing.


    Component

    ScoreRenderer

    Renders a single SVG page inside a scrollable container.

    <ScoreRenderer containerRef={containerRef} svg={svg} />
    PropTypeDescription
    containerRefRefObject<HTMLDivElement>Ref to the outer scrollable div (used by useAutoScroll and useNoteHighlight)
    svgstringRaw SVG markup string to render

    Types

    TimeMapEntry

    interface TimeMapEntry {
      off?: string[];       // XML IDs of notes ending at this time
      on?: string[];        // XML IDs of notes starting at this time
      qstamp: number;       // Quarter-note stamp (position in score)
      tempo?: number;       // Tempo at this entry (BPM)
      tstamp: number;       // Absolute timestamp in milliseconds
    }

    ScoreData

    interface ScoreData {
      title: string;
      totalPages: number;
    }

    ScoreManifest

    interface ScoreManifest {
      data: string;          // Relative path to data.json
      pages: string[];       // Relative paths to SVG page files
      scoreData: { title: string; totalPages: number };
    }

    ScoreRenderResult

    interface ScoreRenderResult {
      elementAttributes: ElementAttributes;
      midiBase64: string;
      scoreData: { title: string; totalPages: number };
      svgPages: string[];
      timemap: TimeMapEntry[];
    }

    ElementAttributes

    interface ElementAttributes {
      [xmlId: string]: Record<string, string>;
    }

    NoteEvent

    interface NoteEvent {
      duration: number;  // Note duration in seconds
      midi: number;      // MIDI note number (0-127)
      pitch: string;     // Note name (e.g., "C4", "F#5")
      time: number;      // Start time in seconds
    }