Lifecycle
The player’s phase machine tracks what the player is doing at any point.
player.phase() returns one of 13 stable string values.
Phase diagram
| Phase | Transition |
|---|---|
idle | setup() called |
setup | Plugin use() runs, then transitions to ready |
ready | load() or current() called, transitions to loading |
loading | Media loads, transitions to starting |
starting | First frame decoded, transitions to playing |
playing | Can recover from buffering or seeking |
playing | Branches to paused, ended, or stopped |
paused | Next play() call transitions back to starting |
ended | Next play() call transitions back to starting |
stopped | Next play() call transitions back to starting |
starting | dispose() called, transitions to disposing |
disposing | Teardown completes, transitions to disposed |
disposed | Terminal state, instance can be garbage collected |
All 13 phases
| Phase | When |
|---|---|
idle | Before setup() is called |
setup | setup() is running, plugins are initializing |
ready | All plugins resolved, player can accept transport commands |
loading | load() or current() is in progress |
starting | Media is loaded, play() dispatched, waiting for first frame |
playing | Playback is active |
paused | Playback is paused |
buffering | Buffer underrun, player is stalled, waiting for data |
seeking | A seek operation is in progress |
ended | Current item played to the end |
stopped | stop() was called, position reset to 0 |
disposing | dispose() is in progress, teardown running |
disposed | Player is fully torn down, instance can be garbage collected |
Listening to phase changes
player.on('phase', ({ from, to }) => {
console.log(`Phase transition: ${from} → ${to}`);
});
Current phase at any point:
player.phase(); // 'playing' | 'paused' | etc.
Setup stages
The setup pipeline fires events at each stage. Each stage can fail independently:
| Event | Payload | Fires when |
|---|---|---|
beforeSetup | void | Immediately before setup begins (last chance to add plugins) |
setupStart | { container: HTMLElement } | Setup begins |
configResolved | { config } | Config normalized |
pluginsRegistering | void | Plugin use() running |
pluginsRegistered | void | All plugins’ use() resolved |
streamsReady | void | HLS/native stream adapters registered |
authReady | void | Auth pipeline wired |
playlistResolving | { url: string } | Fetching a URL-based playlist |
playlistReady | { length: number } | Playlist loaded (or URL load complete) |
mediaReady | void | Media backend ready |
ready | void | Full setup complete, all plugins resolved |
Each stage has a paired *Error event for observability:
player.on('pluginsRegisteredError', (err) => {
console.error('Plugin registration failed:', err.code, err.message);
});
URL-based playlist loading
If setup({ playlist: 'https://...' }) receives a URL string instead of an array, the setup pipeline fetches and parses it as a JSON array of playlist items.
The fetch uses the player’s auth pipeline.
Events:
playlistResolving: fetch started,{ url }playlistReady: fetch succeeded,{ length }playlistError: fetch or parse failed,{ url, error, code }, player still reachesreadywith an empty queue
The fetch never throws, all errors are emitted and the player completes setup regardless.
play/pause lifecycle events
Each transport action emits a sequence of before + post events:
| Action | Event sequence |
|---|---|
| play | beforePlay → play → playing (+ firstFrame on first play) |
| pause | beforePause → pause |
| stop | beforeStop → stop |
| next | beforeNext → next → current |
| previous | beforePrevious → previous → current |
| load | beforeLoad → current → loading → starting → playing |
| seek | beforeSeek → seek → seeked |
A prevented action emits *Prevented instead of the post-action event:
player.on('playPrevented', ({ reason }) => {
// reason: 'listener-prevented' | 'delay-rejected' | 'delay-timeout'
});
Dispatch context
player.dispatching() returns the stack of events currently being processed:
player.on('beforePlay', () => {
player.dispatching(); // ['beforePlay']
});
Useful for debugging circular event chains and for plugins that need to know they are operating inside an event handler.