# Quick Start

> Use WebMusic's npm packages to build a score player with synchronized notation and a three-octave piano keyboard.

Canonical documentation: https://koperative-lab.github.io/WebMusic/quick-start/

Source snapshot 1377617e9c9a1f153606135c04f6d9fbfecee6cd8b42698c3e9042a51a83b2ba: @webmusic/kernel@0.1.0, @webmusic/ui@0.1.0, @webmusic/score@0.1.0, @webmusic/audio@0.1.0, @webmusic/bridge@0.1.0. Includes Audio/Bridge additions; package version labels do not establish published compatibility. Match this source checkout's exports and declarations. The manifest retains the exact comparison with the unchanged main release baseline.

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.

[Interactive example on the documentation page](https://koperative-lab.github.io/WebMusic/quick-start/)

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>`](<https://koperative-lab.github.io/WebMusic/score/element/play/score-note-input/>).

## Install the packages

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

```bash
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.

## Add the player and its views

Download [Arabesque No. 1 (MXL)](<https://koperative-lab.github.io/WebMusic/mxl/Arabesque%20No.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.

```html
<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>
```

## Register the components

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.

```ts
import {defineScorePlayerElement} from '@webmusic/score/play/element';
import {definePitchViewElement, defineScoreViewElement} from '@webmusic/score/view/element';

defineScorePlayerElement();
defineScoreViewElement();
definePitchViewElement();
```

## How it works

| 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>`](<https://koperative-lab.github.io/WebMusic/score/element/view/score-sheet-view/>).

<details>
<summary>Add loading and error feedback</summary>

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`:

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

```ts
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`:

```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](<https://koperative-lab.github.io/WebMusic/score/>) for other formats, registration and integration options.

</details>

## Keep the behavior and change the interface

- **Use your own buttons:** see the
  [ScorePlayer engine and its commands](<https://koperative-lab.github.io/WebMusic/score/headless/play/score-player/>).
- **Reuse controls with custom bindings:** see the
  [Transport presenter](<https://koperative-lab.github.io/WebMusic/uikit/transport-time/transport/>).
- **Add analysis:** attach companions through the
  [shared playback source](<https://koperative-lab.github.io/WebMusic/score/api/#shared-playback-observation>).

Full references: [Score player](<https://koperative-lab.github.io/WebMusic/score/element/play/score-player/>) ·
[Score view](<https://koperative-lab.github.io/WebMusic/score/element/view/score-view/>) ·
[Pitch view](<https://koperative-lab.github.io/WebMusic/score/element/view/score-pitch-view/>).

To build with a coding agent, see [Agent Toolkit](<https://koperative-lab.github.io/WebMusic/agent-toolkit/>) for reference
selection, task prompts and project instructions.

## Play recorded audio

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

```bash
npm install @webmusic/audio @webmusic/kernel @webmusic/ui
```

Download the [Arabesque No. 1 WAV recording](<https://koperative-lab.github.io/WebMusic/wav/Arabesque%20No.1.wav>), save it as `public/arabesque.wav`,
and add the player to your browser application:

```html
<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](<https://koperative-lab.github.io/WebMusic/bridge/composition/>).

For custom controls and API + UI composition, see [Audio setup](<https://koperative-lab.github.io/WebMusic/audio/>),
[Audio playback](<https://koperative-lab.github.io/WebMusic/audio/#play>), and the
[Audio player reference](<https://koperative-lab.github.io/WebMusic/audio/element/play/audio-player/>).
