Rooks

useAudio

About

A comprehensive hook to control and manage audio elements in your React application with advanced features like volume control, playback rate adjustment, seeking, and error handling.

Examples

Basic example

import { useAudio } from "rooks";
 
export default function App() {
  const audioSrc =
    "https://commondatastorage.googleapis.com/codeskulptor-demos/DDR_assets/Sevish_-__nbsp_.mp3";
  const [audioRef, audioState, audioControls] = useAudio({
    autoPlay: false,
  });
 
  return (
    <>
      <div>Status: {audioState.isPlaying ? "Playing" : "Not playing"}</div>
      <div>Volume: {Math.round(audioState.volume * 100)}%</div>
      <div>Time: {audioState.currentTime.toFixed(1)}s / {audioState.duration.toFixed(1)}s</div>
      {audioState.hasError && <div>Error: {audioState.error}</div>}
      
      <audio ref={audioRef} src={audioSrc} />
      
      <div>
        <button onClick={audioControls.play}>Play</button>
        <button onClick={audioControls.pause}>Pause</button>
        <button onClick={audioControls.toggleMute}>
          {audioState.isMuted ? "Unmute" : "Mute"}
        </button>
      </div>
    </>
  );
}

Advanced audio player with controls

import { useAudio } from "rooks";
 
export default function AudioPlayer() {
  const audioSrc =
    "https://commondatastorage.googleapis.com/codeskulptor-demos/DDR_assets/Sevish_-__nbsp_.mp3";
  
  const [audioRef, audioState, audioControls] = useAudio(
    {
      autoPlay: false,
      volume: 0.8,
      preload: "metadata",
    },
    {
      onPlay: () => console.log("Audio started playing"),
      onPause: () => console.log("Audio paused"),
      onEnded: () => console.log("Audio ended"),
      onError: (error) => console.error("Audio error:", error),
    }
  );
 
  const formatTime = (time) => {
    const minutes = Math.floor(time / 60);
    const seconds = Math.floor(time % 60);
    return `${minutes}:${seconds.toString().padStart(2, "0")}`;
  };
 
  const handleSeek = (e) => {
    const rect = e.target.getBoundingClientRect();
    const percent = (e.clientX - rect.left) / rect.width;
    audioControls.setCurrentTime(percent * audioState.duration);
  };
 
  return (
    <div style={{ padding: "20px", maxWidth: "400px" }}>
      <audio ref={audioRef} src={audioSrc} />
      
      {audioState.isLoading && <div>Loading...</div>}
      {audioState.isBuffering && <div>Buffering...</div>}
      {audioState.hasError && <div>Error: {audioState.error}</div>}
      
      {/* Progress Bar */}
      <div
        onClick={handleSeek}
        style={{
          width: "100%",
          height: "8px",
          background: "#ddd",
          cursor: "pointer",
          margin: "10px 0",
        }}
      >
        <div
          style={{
            width: `${(audioState.currentTime / audioState.duration) * 100}%`,
            height: "100%",
            background: "#007bff",
          }}
        />
      </div>
      
      {/* Time Display */}
      <div style={{ display: "flex", justifyContent: "space-between" }}>
        <span>{formatTime(audioState.currentTime)}</span>
        <span>{formatTime(audioState.duration)}</span>
      </div>
      
      {/* Controls */}
      <div style={{ display: "flex", gap: "10px", margin: "10px 0" }}>
        <button onClick={() => audioControls.rewind(10)}>⏪ 10s</button>
        <button onClick={audioControls.togglePlay}>
          {audioState.isPlaying ? "⏸️ Pause" : "▶️ Play"}
        </button>
        <button onClick={() => audioControls.fastForward(10)}>⏩ 10s</button>
        <button onClick={audioControls.toggleMute}>
          {audioState.isMuted ? "🔇" : "🔊"}
        </button>
      </div>
      
      {/* Volume Control */}
      <div style={{ margin: "10px 0" }}>
        <label>Volume: {Math.round(audioState.volume * 100)}%</label>
        <input
          type="range"
          min="0"
          max="1"
          step="0.1"
          value={audioState.volume}
          onChange={(e) => audioControls.setVolume(parseFloat(e.target.value))}
          style={{ width: "100%", margin: "5px 0" }}
        />
      </div>
      
      {/* Playback Speed */}
      <div style={{ margin: "10px 0" }}>
        <label>Speed: {audioState.playbackRate}x</label>
        <div style={{ display: "flex", gap: "5px", margin: "5px 0" }}>
          <button onClick={() => audioControls.setPlaybackRate(0.5)}>0.5x</button>
          <button onClick={() => audioControls.setPlaybackRate(1)}>1x</button>
          <button onClick={() => audioControls.setPlaybackRate(1.25)}>1.25x</button>
          <button onClick={() => audioControls.setPlaybackRate(1.5)}>1.5x</button>
          <button onClick={() => audioControls.setPlaybackRate(2)}>2x</button>
        </div>
      </div>
      
      {/* Loop Control */}
      <div>
        <label>
          <input
            type="checkbox"
            checked={audioState.loop}
            onChange={(e) => audioControls.setLoop(e.target.checked)}
          />
          Loop
        </label>
      </div>
    </div>
  );
}

Audio player with playlist

import { useAudio } from "rooks";
import { useState } from "react";
 
export default function PlaylistPlayer() {
  const playlist = [
    {
      title: "Track 1",
      src: "https://commondatastorage.googleapis.com/codeskulptor-demos/DDR_assets/Sevish_-__nbsp_.mp3"
    },
    {
      title: "Track 2", 
      src: "https://commondatastorage.googleapis.com/codeskulptor-demos/pyman_assets/intromusic.ogg"
    }
  ];
  
  const [currentTrack, setCurrentTrack] = useState(0);
  
  const [audioRef, audioState, audioControls] = useAudio(
    {
      autoPlay: false,
    },
    {
      onEnded: () => {
        // Auto-play next track
        if (currentTrack < playlist.length - 1) {
          setCurrentTrack(prev => prev + 1);
        }
      },
    }
  );
 
  const playTrack = (index) => {
    setCurrentTrack(index);
    // The audio src will change, and we can play after it loads
    setTimeout(() => audioControls.play(), 100);
  };
 
  return (
    <div style={{ padding: "20px" }}>
      <h3>Now Playing: {playlist[currentTrack]?.title}</h3>
      
      <audio ref={audioRef} src={playlist[currentTrack]?.src} />
      
      <div style={{ margin: "20px 0" }}>
        <button onClick={audioControls.togglePlay}>
          {audioState.isPlaying ? "Pause" : "Play"}
        </button>
        <button 
          onClick={() => playTrack(Math.max(0, currentTrack - 1))}
          disabled={currentTrack === 0}
        >
          Previous
        </button>
        <button 
          onClick={() => playTrack(Math.min(playlist.length - 1, currentTrack + 1))}
          disabled={currentTrack === playlist.length - 1}
        >
          Next
        </button>
      </div>
      
      <div>
        <h4>Playlist:</h4>
        {playlist.map((track, index) => (
          <div 
            key={index}
            onClick={() => playTrack(index)}
            style={{
              padding: "10px",
              cursor: "pointer",
              background: index === currentTrack ? "#e0e0e0" : "transparent",
              border: "1px solid #ccc",
              margin: "2px 0"
            }}
          >
            {track.title} {index === currentTrack && audioState.isPlaying && "🎵"}
          </div>
        ))}
      </div>
    </div>
  );
}

Arguments

Argument valueTypeDescriptionDefault
optionsObjectSee table below
callbacksObjectSee table below

Options

Options valueTypeDescriptionDefault
autoPlayBooleanIndicates if the audio should start playing automaticallyfalse
isMutedBooleanIndicates if the audio should be muted by defaultfalse
volumeNumberInitial volume level (0-1)1
playbackRateNumberInitial playback speed (0.25-4)1
loopBooleanWhether the audio should loopfalse
preloadStringHow much of the audio to preload ("none", "metadata", "auto")"metadata"

Callbacks

Callbacks valueTypeDescriptionDefault
onPlayFunctionCalled when audio starts playingundefined
onPauseFunctionCalled when audio is pausedundefined
onEndedFunctionCalled when audio playback endsundefined
onMuteFunctionCalled when audio is mutedundefined
onUnmuteFunctionCalled when audio is unmutedundefined
onLoadedMetadataFunctionCalled when audio metadata is loadedundefined
onTimeUpdateFunctionCalled when current time changes (currentTime)undefined
onDurationChangeFunctionCalled when duration changes (duration)undefined
onVolumeChangeFunctionCalled when volume changes (volume)undefined
onRateChangeFunctionCalled when playback rate changes (rate)undefined
onErrorFunctionCalled when an error occurs (error)undefined
onLoadStartFunctionCalled when loading startsundefined
onCanPlayFunctionCalled when audio can start playingundefined
onWaitingFunctionCalled when audio is bufferingundefined

Returns

Returns an array with three elements:

Return valueTypeDescriptionDefault
refCallback RefA ref that should be used on the audio element you want to controlundefined
stateObjectAn object containing isPlaying and isMuted properties
controlsObjectAn object containing audio control methods

State Properties

PropertyTypeDescription
isPlayingBooleanWhether the audio is playing
isMutedBooleanWhether the audio is muted
volumeNumberCurrent volume level (0-1)
currentTimeNumberCurrent playback position in seconds
durationNumberTotal duration of audio in seconds
playbackRateNumberCurrent playback speed (0.25-4)
isLoadingBooleanWhether the audio is loading
isBufferingBooleanWhether the audio is buffering
loopBooleanWhether the audio is set to loop
hasErrorBooleanWhether an error has occurred
errorStringError message (only present when hasError is true)

Control Methods

MethodTypeDescription
playFunctionStart playing the audio (returns Promise)
pauseFunctionPause the audio
togglePlayFunctionToggle play/pause state (returns Promise)
muteFunctionMute the audio
unmuteFunctionUnmute the audio
toggleMuteFunctionToggle mute/unmute state
setVolumeFunctionSet volume level (0-1)
setCurrentTimeFunctionSet current playback position in seconds
setPlaybackRateFunctionSet playback speed (0.25-4)
seekFunctionSeek forward/backward by specified seconds
fastForwardFunctionFast forward by 10 seconds (or custom amount)
rewindFunctionRewind by 10 seconds (or custom amount)
setLoopFunctionEnable/disable looping

On this page