Hiyve Components - v1.0.0
    Preparing search index...

    Module @hiyve/react-music-notation

    @hiyve/react-music-notation

    Sheet music player for MusicXML scores. It renders a score, plays it back with transport, tempo and per-instrument volume controls, and can keep everyone in a room on the same note as a presenter plays, steps or clicks through the piece.

    This package is the player: the controls, the layout and the room sync. It does not contain the engine that engraves and plays a score. You provide that engine through the required loadEngine prop, and nothing renders until you do.

    Setup is three steps: install the package, add an engine, render a score.

    npm install @hiyve/react-music-notation @hiyve/utilities @mui/material @mui/icons-material @emotion/react @emotion/styled
    

    Synced playback across a room additionally needs:

    npm install @hiyve/react @hiyve/react-semantic-relay
    

    The player works with any OpenSheetMusicDisplay-compatible build. Which build you choose decides what your users get:

    Option A: public build Option B: a build with playback
    Where it comes from opensheetmusicdisplay on npm (BSD-3-Clause) A build that includes the audio player classes PlaybackManager, BasicAudioPlayer and LinearTimingSource, such as the build OpenSheetMusicDisplay provides to its sponsors
    Score rendering Yes Yes
    Cursor and note-by-note stepping Yes Yes
    Play, pause, stop No Yes
    Tempo and per-instrument volume No Yes
    Click a note to hear it No Yes

    You can start with option A and move to option B later; only the loadEngine function changes.

    Install it:

    npm install opensheetmusicdisplay
    

    Create the loader once, in a module of its own:

    // notationEngine.ts
    export const loadEngine = () => import('opensheetmusicdisplay');

    The player shows the score with previous-note and next-note buttons. The playback controls are hidden.

    1. Get the build. You need one file: the minified browser build, usually named opensheetmusicdisplay.min.js. It is not on public npm. You obtain it from its publisher and use it under its licence terms.

    2. Serve the file from your app. Either put it in your static folder, so that public/vendor/opensheetmusicdisplay.min.js is served at /vendor/opensheetmusicdisplay.min.js, or keep it beside your source and let your bundler emit it:

      const engineUrl = new URL('./vendor/opensheetmusicdisplay.min.js', import.meta.url).href;
      

      Load it as a script, as shown next. Do not import the file as a module.

    3. Create the loader once, in a module of its own:

      // notationEngine.ts
      import { createScriptEngineLoader } from '@hiyve/react-music-notation';

      export const loadEngine = createScriptEngineLoader('/vendor/opensheetmusicdisplay.min.js');

    createScriptEngineLoader(url, options?) adds the script to the page the first time a player needs it and reuses it afterwards, however many players you render.

    Option Type Default Description
    globalName string 'opensheetmusicdisplay' Name of the global the script defines
    nonce string – CSP nonce to set on the script element

    Either way, the engine is downloaded only when a player first mounts, so it adds nothing to your initial page load.

    import { MusicXmlPlayer } from '@hiyve/react-music-notation';
    import { loadEngine } from './notationEngine';

    function ScoreViewer({ url, onClose }: { url: string; onClose: () => void }) {
    return (
    <MusicXmlPlayer
    fileUrl={url}
    fileName="Minuet in G"
    loadEngine={loadEngine}
    onClose={onClose}
    onError={(error) => console.error(error)}
    />
    );
    }

    By default the player covers the viewport with its own header. Use variant="inline" to fill a container you size yourself:

    <div style={{ height: 600 }}>
    <MusicXmlPlayer variant="inline" fileUrl={url} loadEngine={loadEngine} />
    </div>

    Pass onError while you set up; the message tells you which step failed.

    What you see Cause Fix
    Failed to load the notation engine script The engine URL is wrong, or your Content Security Policy blocks it Open the URL in the browser; allow its origin in script-src
    The notation engine script loaded but did not define window.opensheetmusicdisplay The file is not a browser build, or it defines a different global Use the minified browser build; set globalName if the global differs
    The notation engine does not provide an OpenSheetMusicDisplay class loadEngine resolved with something that is not an engine Return the engine module itself from loadEngine
    Failed to fetch the score: 403 Forbidden (or another status) The browser could not read fileUrl Check the URL, its expiry and its CORS headers
    The score shows but there is no Play button The engine has no playback classes (option A) Expected with the public build; use a build with playback
    Play works but there is no sound The instrument samples could not be downloaded Allow the sample hosts in connect-src, or set soundfont (see Instrument samples)

    To check an engine in code, engineSupportsPlayback(engine) returns whether it can play audio.

    • Score rendering from any MusicXML URL, engraved as SVG and re-flowed when the container resizes.
    • Playback transport: play, pause, stop, and note-by-note stepping, with a cursor that follows the music.
    • Tempo slider, starting from the tempo the score states.
    • Per-instrument volume and mute for every part in the score.
    • Click a note to move the cursor there and hear it.
    • Synced playback: one presenter drives every follower's player, including tempo, volume and note clicks.
    • Render-only mode: with an engine that has no audio player, the score still renders and steps; playback controls are hidden.
    • Overlay or inline layout, with customizable labels and colours.

    SyncedMusicXmlPlayer mirrors the presenter's actions to every follower. A follower's controls are locked while it follows.

    import { SyncedMusicXmlPlayer } from '@hiyve/react-music-notation/sync';

    function RoomScore({ file, url, localUserId, ownerId }) {
    return (
    <SyncedMusicXmlPlayer
    variant="inline"
    fileUrl={url}
    loadEngine={loadEngine}
    fileId={file.fileId}
    localUserId={localUserId}
    isPresenter={localUserId === ownerId}
    presenterId={ownerId}
    enabled
    />
    );
    }

    Render it under a SemanticRelayProvider from @hiyve/react-semantic-relay. Every participant must pass the same fileId for the same score.

    Followers only accept commands sent by presenterId. Until presenterId is known they ignore every command, so pass it as soon as you have it.

    useMusicXmlPlayerSync gives you the transport without the component, for cases where you render MusicXmlPlayer yourself:

    import { useRef } from 'react';
    import { MusicXmlPlayer, type MusicXmlPlayerHandle } from '@hiyve/react-music-notation';
    import { useMusicXmlPlayerSync } from '@hiyve/react-music-notation/sync';

    function CustomSyncedScore({ url, fileId, localUserId, isPresenter, presenterId }) {
    const playerRef = useRef<MusicXmlPlayerHandle>(null);
    const { broadcast, isFollowing } = useMusicXmlPlayerSync({
    localUserId,
    isPresenter,
    presenterId,
    fileId,
    enabled: true,
    onRemoteCommand: (command) => playerRef.current?.apply(command),
    });

    return (
    <MusicXmlPlayer
    ref={playerRef}
    variant="inline"
    fileUrl={url}
    loadEngine={loadEngine}
    controlsLocked={isFollowing}
    onLocalCommand={broadcast}
    />
    );
    }

    FileManager from @hiyve/react-collaboration recognises .musicxml files. Register the player as their viewer:

    import { FileManager } from '@hiyve/react-collaboration';
    import { MusicXmlPlayer } from '@hiyve/react-music-notation';

    <FileManager
    customViewers={{
    musicxml: (_data, _file, _onClose, url) =>
    url ? <MusicXmlPlayer variant="inline" fileUrl={url} loadEngine={loadEngine} /> : null,
    }}
    />
    Prop Type Default Description
    fileUrl string required URL of the MusicXML document
    loadEngine NotationEngineLoader required Supplies the notation engine
    fileName string – Title shown in the overlay header
    onClose () => void – Called when the user closes the overlay
    variant 'overlay' | 'inline' 'overlay' Cover the viewport with a header, or fill the parent with none
    controlsLocked boolean false Disable every control and ignore clicks on the score
    onLocalCommand (command: MusicXmlPlayerCommand) => void – Called for each action the local user performs
    onError (error: Error) => void – Called when the engine or the score fails to load
    labels Partial<MusicXmlPlayerLabels> – Text overrides
    colors Partial<MusicXmlPlayerColors> – Colour overrides
    soundfont MusicXmlSoundfontOptions – Where playback fetches instrument samples
    sx SxProps<Theme> – Styles merged onto the root element

    The component's ref exposes apply(command), which performs a command as if the user had done it without reporting it through onLocalCommand.

    Takes every MusicXmlPlayer prop except controlsLocked and onLocalCommand, plus:

    Prop Type Default Description
    localUserId string required The local user's id
    isPresenter boolean required True for the one participant everyone else follows
    enabled boolean required Presenter: share my actions. Follower: follow the presenter
    fileId string required Identifies the open score; the same on every client
    presenterId string – The presenter's user id. Followers ignore commands from anyone else
    allowUnverifiedPresenter boolean false Accept commands from any sender while presenterId is empty

    Takes the six sync props above, plus:

    Option Type Description
    onRemoteCommand (command: MusicXmlPlayerCommand) => void Called with each command a follower should apply
    onError (error: Error) => void Called when a published command could not be delivered

    Returns:

    Field Type Description
    broadcast (command: MusicXmlPlayerCommand) => void Publish a local action. Does nothing unless presenting with sync on
    isFollowing boolean True when this client is following the presenter

    MusicXmlPlayerCommand is one of:

    Command Meaning
    { t: 'play' } Start or resume playback
    { t: 'pause' } Pause playback
    { t: 'stop' } Stop and return to the start
    { t: 'next' } / { t: 'back' } Step one note forward or back
    { t: 'tempo', bpm } Set the tempo
    { t: 'volume', instrument, volume } Set an instrument's volume, 0–1. instrument is its position in the score
    { t: 'mute', instrument, muted } Mute or unmute an instrument
    { t: 'noteAt', timestamp } Move to a position in the score and sound the notes there
    <MusicXmlPlayer
    fileUrl={url}
    loadEngine={loadEngine}
    labels={{ title: 'Partitur', play: 'Abspielen', pause: 'Pause', stop: 'Stopp' }}
    />
    Label Default
    title Sheet Music
    back Go back
    close Close
    previousNote Previous note
    nextNote Next note
    play Play
    pause Pause
    stop Stop
    tempo Tempo
    tempoUnit BPM
    volume Volume
    muteInstrument Mute
    unmuteInstrument Unmute
    noInstruments No instruments detected
    unknownInstrument Unknown
    loading Loading sheet music…
    loadError Failed to load sheet music

    DEFAULT_LABELS holds the defaults and mergeLabels(overrides) returns the defaults with your overrides applied.

    Each colour is an MUI theme palette path or any CSS colour.

    <MusicXmlPlayer
    fileUrl={url}
    loadEngine={loadEngine}
    colors={{ toolbar: 'grey.900', cursor: '#ff3366' }}
    />
    Colour Default Used for
    background background.default The player surface
    toolbar background.paper Header and control bar
    border divider Borders under the header and control bar
    sheet #FFFFFF The page the score is drawn on
    cursor primary.main The playback cursor

    DEFAULT_COLORS holds the defaults and mergeColors(overrides) returns the defaults with your overrides applied.

    During playback the engine downloads instrument samples. To serve them from your own host, pass soundfont:

    <MusicXmlPlayer
    fileUrl={url}
    loadEngine={loadEngine}
    soundfont={{
    baseUrl: 'https://cdn.example.com/soundfonts/',
    percussionBaseUrl: 'https://cdn.example.com/soundfonts/FluidR3_GM/',
    }}
    />

    The host must be laid out like the midi-js-soundfonts collection.

    • React 18 or 19, and MUI 9.
    • A notation engine, supplied through loadEngine.
    • fileUrl must be fetchable from the browser (CORS applies) and return uncompressed MusicXML. Compressed .mxl files are not supported.
    • Content Security Policy. The score is fetched with fetch, so its host must be allowed by connect-src. With a playback engine and no soundfont override, samples are fetched from https://gleitz.github.io and https://paulrosen.github.io. An engine loaded with createScriptEngineLoader must be allowed by script-src.
    • Browsers start audio only after a user gesture, so playback begins from a click on the player.
    • SyncedMusicXmlPlayer and useMusicXmlPlayerSync must be used under a SemanticRelayProvider from @hiyve/react-semantic-relay.
    Export Kind Purpose
    MusicXmlPlayer component The score player
    createScriptEngineLoader function Loader for an engine hosted as a script
    engineSupportsPlayback function Whether an engine can play audio
    DEFAULT_LABELS, mergeLabels defaults Label defaults and merge
    DEFAULT_COLORS, mergeColors defaults Colour defaults and merge
    readCursorTimestamp function The cursor's position in a score display, or null
    readSelectionStartTimestamp function The position of the last clicked note in a score display, or null
    pollForSelectionChange function Wait for a value to change, with a timeout; returns a cancel function
    MusicXmlPlayerProps, MusicXmlPlayerHandle, MusicXmlPlayerCommand types Player props, ref handle and commands
    MusicXmlPlayerLabels, MusicXmlPlayerColors, MusicXmlSoundfontOptions types Customization
    NotationEngine, NotationEngineLoader, ScriptEngineLoaderOptions types The engine contract
    NotationDisplay, NotationCursor, NotationCursorIterator, NotationSheet, NotationTimestamp, NotationInstrument, NotationInstrumentMap, NotationPlaybackManager, NotationPlaybackListener, NotationAudioPlayer, NotationSoundfontOptions, NotationTimingSource, PollDeps types The parts of an engine the player uses
    Export Kind Purpose
    SyncedMusicXmlPlayer component A player the presenter drives for every follower
    useMusicXmlPlayerSync hook The sync transport on its own
    MUSICXML_PLAYER_SYNC_TOPIC constant The relay topic sync messages are published on
    SyncedMusicXmlPlayerProps, UseMusicXmlPlayerSyncOptions, UseMusicXmlPlayerSyncResult, MusicXmlPlayerSyncPayload types Sync props, options, result and message shape

    Modules

    @hiyve/react-music-notation
    @hiyve/react-music-notation/sync