Skip to content

Search documentation

Enter a title, API, or topic.

    ↑ ↓ NavigateEnter Open resultEsc Close

    Quick Start

    Build a player for Arabesque No. 1, supplied as a compressed MusicXML score. One player loads the file; a staff view follows its position and a three-octave piano keyboard highlights sounding notes within its C3–C6 range. Playback uses the built-in oscillator, so no SoundFont is needed.

    Loading score…

    Press Play to enable sound. Try pausing and seeking with the player’s slider: both views follow the same player. The keyboard is a visual readout; for playable keys, see <score-note-input>.

    In your browser application, install WebMusic’s Score package and its Kernel and UI dependencies:

    Terminal window
    npm install @webmusic/score @webmusic/kernel @webmusic/ui

    Score supplies the musical behavior and Web Components; Kernel supplies runtime primitives, and UI supplies their controls and views. The example below uses TypeScript and an ES-module bundler such as Vite to resolve the package imports.

    Download Arabesque No. 1 (MXL) and save it as public/arabesque.mxl in your application. The player reads compressed MusicXML directly; no manual extraction is needed. The example assumes that your application serves this file at /arabesque.mxl; set src to your file’s public URL.

    Put this markup in index.html. The player owns the file load, musical state and sound; the two player="#piece" selectors attach views to that same instance.

    <main id="music-demo">
    <score-player id="piece" src="/arabesque.mxl" time-control="full"></score-player>
    <score-view player="#piece" type="staff"></score-view>
    <score-pitch-view player="#piece" type="keyboard" low="48" high="84" fit-to-width="true" white-key-height="128" black-key-height="80"></score-pitch-view>
    </main>
    <script type="module" src="/src/main.ts"></script>
    <style>
    #music-demo { display: grid; gap: 1rem; max-width: 48rem; margin: 2rem auto; }
    #music-demo > * { min-width: 0; margin: 0; }
    #music-demo > :not([hidden]) { display: block; }
    </style>

    Add this to src/main.ts. Importing the /element entries gives you the registration functions; calling them makes the three tags available to the browser.

    import {defineScorePlayerElement} from '@webmusic/score/play/element';
    import {definePitchViewElement, defineScoreViewElement} from '@webmusic/score/view/element';
    defineScorePlayerElement();
    defineScoreViewElement();
    definePitchViewElement();
    Code What it does
    <score-player id="piece" src="…"> Loads the MusicXML once and owns playback, sound and the current position. Its controls let you play, pause and seek.
    time-control="full" Shows elapsed time and total duration beside the player controls.
    <score-view player="#piece" type="staff"> Borrows the loaded score and playback position to draw notation with a following playhead and active-note highlights.
    <score-pitch-view player="#piece" type="keyboard"> Borrows the same player’s sounding notes and highlights their piano keys.
    low="48" high="84" Sets the keyboard’s MIDI endpoints to C3 and C6: a three-octave span with both end keys included, for 37 keys.
    white-key-height="128" black-key-height="80" Sets white keys to 128 px tall and black keys to 80 px tall.
    fit-to-width="true" Fits all three octaves to the container width without horizontal scrolling; key heights stay unchanged.

    Keep id="piece" unique within the document. The player="#piece" selectors connect both views to that player, so pausing or seeking updates their display automatically. You do not need to load the score again or synchronize a second player. To use another piece, change the player’s src; the views follow its replacement score and note state.

    The staff view provides lightweight notation. For detailed engraving, see <score-sheet-view>.

    Add loading and error feedback

    The bare player publishes readiness and load errors through .playback; it does not draw those messages itself. To add the same feedback as the demo, place a status element directly after <score-player> in index.html:

    <p id="player-status" role="status" hidden></p>

    Add this helper to src/main.ts after the registration code. It shows loading or failure messages and hides the status element once the score is ready.

    import type {ScorePlayerElement} from '@webmusic/score/play/element';
    
    /** Show loading and failures; keep the status row hidden during normal playback. */
    export function mountQuickStartStatus(player: ScorePlayerElement, status: HTMLElement): () => void {
      let lastState = '';
      const write = (message: string): void => {
        status.hidden = message.length === 0;
        if (status.textContent !== message) status.textContent = message;
      };
      const message = (error: unknown): string => error instanceof Error ? error.message : String(error);
      const unsubscribe = player.playback.subscribe((snapshot) => {
        const state = `${snapshot.sourceRevision}:${snapshot.readiness}:${snapshot.state}`;
        if (state === lastState) return;
        lastState = state;
        if (snapshot.readiness === 'error') {
          write(`Could not load the score: ${message(snapshot.error)}`);
        } else if (snapshot.readiness !== 'ready') {
          write('Loading score…');
        } else {
          write('');
        }
      });
      const onError = (event: Event): void => {
        const {error} = (event as CustomEvent<{error: unknown}>).detail;
        write(`Playback failed: ${message(error)}`);
      };
      player.addEventListener('webscore:error', onError);
      return () => {
        unsubscribe();
        player.removeEventListener('webscore:error', onError);
      };
    }

    Then call it at the end of src/main.ts:

    const player = document.querySelector<ScorePlayerElement>('#piece')!;
    const status = document.querySelector<HTMLElement>('#player-status')!;
    let detachStatus = mountQuickStartStatus(player, status);
    window.addEventListener('pagehide', () => {
    detachStatus();
    player.stop();
    });
    window.addEventListener('pageshow', (event) => {
    if (event.persisted) detachStatus = mountQuickStartStatus(player, status);
    });

    The Play click supplies the browser’s user activation for sound. A missing or invalid file now produces a visible message. Check the requested URL in the browser’s Network panel; replace src or reload the page to retry after fixing the file. A failed playback command also appears in the status text.

    For a framework component, call detachStatus() in its unmount cleanup. Removing the player from the DOM releases its owned playback resources; removing either view releases only that view’s bindings and rendering resources. Use the Score setup for other formats, registration and integration options.

    Keep the behavior and change the interface

    Section titled “Keep the behavior and change the interface”

    Full references: Score player · Score view · Pitch view.

    To build with a coding agent, see Agent Toolkit for reference selection, task prompts and project instructions.

    For recorded sound, install @webmusic/audio alongside Kernel and UI:

    Terminal window
    npm install @webmusic/audio @webmusic/kernel @webmusic/ui

    Download the Arabesque No. 1 WAV recording, save it as public/arabesque.wav, and add the player to your browser application:

    <script type="module">
    import '@webmusic/audio/play/auto';
    </script>
    <audio-player src="/arabesque.wav" controls></audio-player>

    Click Play to enable audio. This example uses built-in WAV decoding and needs no optional decoder. The player owns its file and playback resources; removing it releases them. Check its error state and the URL if loading fails. A Score player and an Audio player run independently. To coordinate them, use the Bridge composition.

    For custom controls and API + UI composition, see Audio setup, Audio playback, and the Audio player reference.