HTML Audio

Core audio helper and singleton for loading and controlling HTML5 audio.

The $htmlAudio singleton manages playback of HTML5 audio with automatic retry logic, event handling, and volume fading. Use it alongside the Audio Store for full player functionality.

Installation

pnpm dlx shadcn@latest add @audio/html

Import

Import the singleton and helpers from the audio library:

import { $htmlAudio, formatDuration, type Track } from "@/lib/html-audio";

Or use the React hook for better integration:

import { useAudio } from "@/hooks/use-audio";
 
function MyComponent() {
  const { htmlAudio } = useAudio();
  // Use htmlAudio instead of $audio
}

Core API

$htmlAudio (Singleton)

Manages the underlying HTMLAudioElement, playback state, retries, and events. Initialize on client start - the instance is server-safe.

import { $htmlAudio } from "@/lib/html-audio";
 
// Initialize on the client
$htmlAudio.init();
 
// Load and play
await $htmlAudio.load("https://example.com/audio.mp3", 0);
await $htmlAudio.play();

Lifecycle

MethodDescription
init()Initialize on the client. Safe to call multiple times.
cleanup()Reset and release the audio element (pause, clear src).

Playback

MethodDescription
load(url, startTime?, isLive?)Load an audio source and wait for readiness. Pass isLive for live streams (longer timeout, no seek). Returns Promise<void>.
play()Start or resume playback. Returns promise that resolves when browser allows playback.
pause()Pause playback immediately.
seek(time)Seek when metadata is available. Ignored for live streams.

Volume

MethodDescription
setVolume(volume, fadeTime?)Set or fade volume (0–1). If fadeTime > 0, animates smoothly.
getVolume()Return current volume (0–1).
setMuted(muted)Mute or unmute. Restores previous volume when unmuting.

State

MethodDescription
getDuration()Return loaded source duration (seconds) or 0 if unavailable.
getCurrentTime()Return current playback position (seconds).
isPaused()Return boolean - is playback paused.
getBufferedRanges()Return underlying TimeRanges or null.
getSource()Return current source URL string.
getAudioElement()Return raw HTMLAudioElement or null on server.

Events

The library emits custom events via an internal EventTarget:

$htmlAudio.addEventListener("bufferingStart", () => console.log("Buffering..."));
$htmlAudio.addEventListener("bufferingEnd", () => console.log("Ready to play"));
$htmlAudio.addEventListener("playbackStarted", () => console.log("Playing"));
$htmlAudio.addEventListener("audioError", () => console.error("Error"));
$htmlAudio.addEventListener("bufferUpdate", (e) => {
  if (e instanceof CustomEvent) console.log("Buffered:", e.detail.bufferedTime);
});

Utilities

formatDuration(seconds)

Format seconds to MM:SS string. Handles invalid input by returning "0:00".

import { formatDuration } from "@/lib/html-audio";
 
formatDuration(125); // "2:05"
formatDuration(3661); // "61:01"

isLive(duration)

Check if a duration value indicates a live stream. This is a method on the HtmlAudio class.

import { useAudio } from "@/hooks/use-audio";
 
function MyComponent() {
  const { htmlAudio } = useAudio();
  const duration = htmlAudio.getDuration();
 
  if (htmlAudio.isLive(duration)) {
    // Handle live stream (no seeking)
  }
}

Types

Track

Common audio track object with optional fields:

PropTypeDefaultDescription
idstring | number-Unique identifier for the track.
urlstring-URL of the audio file or stream.
titlestring-Track title.
artiststring-Artist name.
artworkstring-Album artwork URL.
imagesstring[]-Array of image URLs.
durationnumber-Track duration in seconds.
albumstring-Album name.
genrestring-Genre.
liveboolean-Whether this is a live stream.
[key: string]unknown-Additional properties.

Examples

Basic Playback

import { $htmlAudio } from "@/lib/html-audio";
 
$htmlAudio.init();
 
async function playTrack(url: string) {
  try {
    await $htmlAudio.load(url, 0);
    await $htmlAudio.play();
  } catch (error) {
    console.error("Playback failed:", error);
  }
}

Volume Management

import { $htmlAudio } from "@/lib/html-audio";
 
// Immediate change
$htmlAudio.setVolume(0.5);
 
// Smooth fade over 1 second
$htmlAudio.setVolume(0.8, 1000);
 
// Mute with memory
$htmlAudio.setMuted(true);
$htmlAudio.setMuted(false); // Restores previous volume

React Component

import { $htmlAudio, formatDuration } from "@/lib/html-audio";
import { useEffect, useState } from "react";
 
function TimeDisplay() {
  const [time, setTime] = useState(0);
 
  useEffect(() => {
    const updateTime = () => setTime($htmlAudio.getCurrentTime());
    const interval = setInterval(updateTime, 100);
    return () => clearInterval(interval);
  }, []);
 
  return <span>{formatDuration(time)}</span>;
}

Event Monitoring

import { $htmlAudio } from "@/lib/html-audio";
 
$htmlAudio.addEventListener("bufferingStart", () => {
  console.log("Loading audio...");
});
 
$htmlAudio.addEventListener("bufferingEnd", () => {
  console.log("Ready to play");
});
 
$htmlAudio.addEventListener("audioError", (event) => {
  console.error("Audio error:", event);
});
  • Audio Store - Zustand store for queue and playback state management
  • Audio Player - Composable player UI components

Notes

  • Singleton pattern: All methods access the same $htmlAudio instance
  • Server-safe: Methods check for client-side availability before executing
  • Automatic retries: Handles load/play errors with exponential backoff (max 3 attempts)
  • Volume fading: Animations use requestAnimationFrame for smooth transitions
  • Live streams: Disable seeking and use extended timeout for reliability
  • formatDuration handles edge cases (NaN, Infinity, negative values)