useAudio
Manage an HTML5 audio element with an easy-to-use control interface.
🎵
SoundHelix Song 1
Experimental Synth Wave
0:000:00
Interactive Audio Controller
Usage
import { useAudio } from 'usehooks-ts'
export default function MyAudioPlayer() {
const [state, controls] = useAudio('https://example.com/audio.mp3')
return (
<div>
<p>Playing: {state.playing ? 'Yes' : 'No'}</p>
<p>Time: {Math.floor(state.currentTime)}s</p>
<button onClick={controls.toggle}>
{state.playing ? 'Pause' : 'Play'}
</button>
<button onClick={() => controls.seek(state.currentTime + 10)}>
Forward 10s
</button>
</div>
)
}API
const [state, controls] = useAudio(src)State Values
| Name | Type | Description |
|---|---|---|
playing | boolean | Current playback status |
duration | number | Total audio length in seconds |
currentTime | number | Current playback position |
volume | number | Volume level (0 to 1) |
muted | boolean | Whether audio is muted |
Controls
| Name | Type | Description |
|---|---|---|
play | () => Promise | Start playback |
pause | () => void | Pause playback |
toggle | () => void | Toggle play/pause |
seek | (time) => void | Jump to specific time |
setVolume | (v) => void | Update volume |
mute | () => void | Mute audio |
unmute | () => void | Unmute audio |
Hook
import { useCallback, useEffect, useRef, useState } from 'react'
export interface AudioState {
playing: boolean
duration: number
currentTime: number
volume: number
muted: boolean
}
export interface AudioControls {
play: () => Promise<void>
pause: () => void
toggle: () => void
seek: (time: number) => void
setVolume: (volume: number) => void
mute: () => void
unmute: () => void
}
export function useAudio(src: string): [AudioState, AudioControls] {
const audioRef = useRef<HTMLAudioElement | null>(null)
const [state, setState] = useState<AudioState>({
playing: false,
duration: 0,
currentTime: 0,
volume: 1,
muted: false,
})
useEffect(() => {
const audio = new Audio(src)
audioRef.current = audio
const updateState = () => {
setState({
playing: !audio.paused,
duration: audio.duration || 0,
currentTime: audio.currentTime,
volume: audio.volume,
muted: audio.muted,
})
}
audio.addEventListener('play', updateState)
audio.addEventListener('pause', updateState)
audio.addEventListener('timeupdate', updateState)
audio.addEventListener('volumechange', updateState)
audio.addEventListener('loadedmetadata', updateState)
return () => {
audio.pause()
audio.removeEventListener('play', updateState)
audio.removeEventListener('pause', updateState)
audio.removeEventListener('timeupdate', updateState)
audio.removeEventListener('volumechange', updateState)
audio.removeEventListener('loadedmetadata', updateState)
audioRef.current = null
}
}, [src])
const controls: AudioControls = {
play: useCallback(() => audioRef.current?.play() || Promise.resolve(), []),
pause: useCallback(() => audioRef.current?.pause(), []),
toggle: useCallback(() => {
if (audioRef.current?.paused) {
audioRef.current.play()
} else {
audioRef.current?.pause()
}
}, []),
seek: useCallback((time: number) => {
if (audioRef.current) {
audioRef.current.currentTime = time
}
}, []),
setVolume: useCallback((volume: number) => {
if (audioRef.current) {
audioRef.current.volume = volume
}
}, []),
mute: useCallback(() => {
if (audioRef.current) {
audioRef.current.muted = true
}
}, []),
unmute: useCallback(() => {
if (audioRef.current) {
audioRef.current.muted = false
}
}, []),
}
return [state, controls]
}