Events
All events emitted by NMMusicPlayer<T>. Subscribe with player.on(event, handler).
MusicEventMap extends BaseEventMap from the player core.
Music-specific events are listed first; inherited core events follow.
Music-specific events
current
'current': { item: MusicPlaylistItem | undefined; index: number }
Fires when the current track changes.
item is undefined when the queue is exhausted or cleared.
index is -1 in that case.
player.on('current', ({ item, index }) => {
if (item) {
document.title = item.name;
updateAlbumArt(item.cover);
}
});
trackEndingSoon
'trackEndingSoon': { remaining: number; currentTrack: MusicPlaylistItem }
Fires when the current track has trackEndingSoonThreshold seconds remaining (default: 10).
remaining is the actual seconds left at the time of emission.
AutoAdvancePlugin listens to this event for preload and crossfade cues.
crossfadeStart
'crossfadeStart': { from: MusicPlaylistItem | null; to: MusicPlaylistItem; duration: number }
Fires when a crossfade begins.
from is null when there is no current track.
duration is the total fade duration in milliseconds (converted from the duration seconds value in CrossfadeOptions before emission).
crossfadeComplete
'crossfadeComplete': { track: MusicPlaylistItem }
Fires when the crossfade completes and the incoming track is now the primary.
repeat
'repeat': { state: RepeatState }
Fires when repeatState() changes.
shuffle
'shuffle': { state: ShuffleState }
Fires when shuffleState() changes.
backend:changed
'backend:changed': { kind: AudioBackendKind }
Fires when backend(kind) completes a backend swap.
kind is 'audio-element' or 'webaudio'.
Declared in MusicEventMap, fully typed, no cast required.
player.on('backend:changed', ({ kind }) => {
console.log('Switched to', kind);
});
Transport events (inherited)
| Event | Payload | When |
|---|---|---|
play | ActionOptions | Playback starts |
pause | ActionOptions | Playback pauses |
playing | undefined | Media rendering after buffering resolves |
ended | undefined | Current track finishes naturally |
Time events (inherited)
| Event | Payload | When |
|---|---|---|
time | { time: number } | Position updates (from the backend timeupdate) |
duration | { duration: number } | Duration becomes known |
progress | { time: number; duration: number; percentage: number } | Throttled position update (default every 5 s). Use for server-side watch-position saves instead of time to avoid per-frame callback noise. Configure the interval with progressIntervalMs in setup(); set to 0 to disable. |
Use time to drive a real-time seekbar.
Use progress to save resume positions server-side.
Duration fires once after loadedmetadata.
Queue events (inherited)
| Event | Payload | When |
|---|---|---|
queue | T[] | Queue replaced |
queue:append | { items: T[]; from: number } | Items appended. from is the index of the first appended item. |
queue:prepend | { items: T[] } | Items prepended |
queue:remove | { id: string | number; index: number; item: T } | Item removed. index is the position that was vacated. |
queue:move | { from: number; to: number } | Item moved |
queue:clear | { previousLength: number } | Queue cleared. previousLength is the count before clearing. |
queue:exhausted | undefined | Queue played through with no repeat active |
backlog | ReadonlyArray<T> | Backlog changes |
Volume events (inherited from BaseEventMap)
mute and volume are inherited from BaseEventMap and are not re-declared in MusicEventMap.
| Event | Payload | When |
|---|---|---|
volume | { level: number } | Volume changes. level is on the 0–100 scale. |
mute | { muted: boolean } | Mute state changes |
Phase events (inherited)
'phase': { from: PlayerPhase; to: PlayerPhase }
Fires on every player phase transition. Useful for orchestrating UI state (loading spinners, disabled controls).
PlayerPhase values: 'idle' | 'setup' | 'ready' | 'loading' | 'starting' | 'playing' | 'paused' | 'buffering' | 'seeking' | 'ended' | 'stopped' | 'disposing' | 'disposed'
Setup and lifecycle events (inherited)
| Event | Payload | When |
|---|---|---|
ready | undefined | Setup complete, all plugins resolved |
firstFrame | undefined | First audio frame decoded after load |
dispose | undefined | Player teardown begins |
Error events (inherited)
| Event | Payload | When |
|---|---|---|
error | PlayerErrorEvent | Recoverable error |
fatal | PlayerErrorEvent | Non-recoverable, playback stopped |
warning | PlayerErrorEvent | Non-fatal degradation |
interface PlayerErrorEvent {
error: PlayerError; // .code (e.g. 'plugin:lyrics/fetch-failed'), .message, .severity, .suggestion?, .context?
severity: 'fatal' | 'error' | 'warning' | 'info';
scope: ErrorScope;
timestamp: number;
// plus cancellable-event controls: markHandled() / preventDefault() / stopImmediatePropagation() / ...
}
// Access the code/message via event.error.code / event.error.message
Auth events (inherited)
| Event | Payload | When |
|---|---|---|
auth:refreshed | { tokenAcquiredAt: number } | Token refresh succeeded. tokenAcquiredAt is a Date.now() timestamp. |
auth:failed | { error: PlayerErrorEvent['error'] } | Token refresh failed |
Subscribing and unsubscribing
const handler = ({ item }: { item: MusicPlaylistItem | undefined }) => {
if (item) updateNowPlaying(item);
};
player.on('current', handler); // subscribe
player.off('current', handler); // unsubscribe
player.once('ready', () => {}); // one-shot
From inside a plugin, always use this.on(...) rather than player.on(...), as the plugin base class auto-disposes all its listeners when the plugin is removed.
EQ events (EqualizerPlugin)
EqualizerPlugin emits events namespaced as plugin:equalizer:<event>.
Subscribe via the string form on player.on():
player.on('plugin:equalizer:band:changed', ({ band }) => {
updateSlider(band.frequency, band.gain);
});
player.on('plugin:equalizer:change', ({ bands, selectedPreset }) => {
renderFullEqState(bands, selectedPreset);
});
These are EqualizerPlugin events, not MusicEventMap events.
Plugin events
Plugin events are namespaced under plugin:<id>:<event>.
Examples:
| Event | Payload |
|---|---|
plugin:lyrics:line | { text: string } |
plugin:lyrics:lineEnter | { text: string } |
plugin:lyrics:lineExit | { text: string } |
plugin:lyrics:loaded | { count: number } |
player.on('plugin:lyrics:line', ({ text }) => {
updateLyricsDisplay(text);
});
See also
- MusicPlaylistItem, item shape in
currentpayload - Crossfade,
crossfadeTo()and crossfade event details - AutoAdvancePlugin, listens to
trackEndingSoonandended - LyricsPlugin,
line,lineEnter,lineExit,loadedevents