Audio Provider
Svelte 5 provider component that manages audio playback lifecycle, state synchronization, and error handling.
AudioProvider is the engine behind every audio component. It initializes the HTML audio element, registers all event listeners, syncs playback state with the audioStore, handles errors and retries, preloads the next track, and persists state to localStorage.
It is required. All audio components depend on it.
Installation
Copy and paste the following code into your project.
Usage
The recommended place to mount AudioProvider is in your layout file so all pages share the same playback state:
<script lang="ts">
import { AudioProvider } from "$lib/components/ui/audio/provider/index.js";
let { children } = $props();
const tracks = [
{
id: "1",
title: "My Track",
artist: "Artist Name",
url: "https://example.com/audio.mp3",
},
];
</script>
<AudioProvider {tracks}>
{@render children()}
</AudioProvider> API Reference
Props
Track Shape
Each track in the tracks array must follow this shape:
interface Track {
id: string | number;
title: string;
artist?: string;
url: string; // URL to the audio file or stream
cover?: string; // Optional album art URL
duration?: number; // Optional pre-known duration in seconds
} How It Works
AudioProvider coordinates three layers:
htmlAudio— a singleton that holds the actualHTMLAudioElementaudioStore— a Svelte 5 reactive class instance that holds all playback stateAudioProvider— the glue layer that wires DOM events → store updates and store changes → DOM mutations
tracks prop → audioStore.queue
audioStore state changes → htmlAudio (DOM)
htmlAudio DOM events → audioStore state
audioStore → localStorage
Lifecycle
On mount, AudioProvider:
- Calls
htmlAudio.init()to create the underlyingHTMLAudioElement - Creates a secondary muted
<audio>element for pre-loading the next track - Attaches all event listeners via
AbortController(automatically cleaned up on destroy) - Restores the last playback position, volume, and track from
localStorage
Reactive $effect Sync
Eight $effect runes keep the HTMLAudioElement in sync with audioStore:
Error Handling
AudioProvider classifies MediaError codes and decides if an error is recoverable:
For recoverable errors, it retries up to 3 times with exponential backoff (1s → 2s → 4s). If all retries fail, audioStore.isError is set to true with an error message.
Next Track Preloading
When playback starts, AudioProvider preloads the next track in a secondary muted <audio> element. This minimizes buffering delay when skipping or when the current track ends. It respects shuffle and repeat mode when calculating which track to preload.
State Persistence
audioStore automatically saves to localStorage under the key audio:ui:store whenever any of these fields change:
- Current track and queue
- Volume and mute
- Playback rate
- Repeat mode and shuffle
- Current playback position
- Queue insert mode and current index
On the next page load, AudioProvider restores these values and seeks to the last known position.
Notes
Only one instance — mount
AudioProvideronce at the top of your app. Multiple instances will conflict because they share the samehtmlAudiosingleton.tracksprop is additive — ifaudioStore.queuealready has tracks (e.g. restored fromlocalStorage), thetracksprop won't overwrite them unless they differ by length orid. This prevents overwriting the restored queue on page reload.No playback on restore —
AudioProviderrestores the last position and loads the track, but does not auto-play. The user must press play.Live stream detection — live streams are detected via
audio.duration === InfinityorNaN. On a live stream end event (stream drops),audioStore.isErroris set with the message"Live stream connection lost".