react-audio-tracks
v1.2.10
Published
A light-weight solution to manage audio tracks and captions in front-end web projects. Provides a handy custom react hook.
Readme
Summary
- A light-weight solution to manage audio tracks and captions in front-end web projects.
- Provides handy custom react hooks to listen to audio tracks' state and captions.
Demo
- https://react-audio-tracks.vercel.app/
📊 Comparison to Similar Libraries
| Feature | react-audio-tracks | react-h5-audio-player | react-player | |---------|-------------------|----------------------|--------------| | Multi-track | ✅ Yes | ❌ No | ❌ No | | Queue system | ✅ Yes | ❌ No | ❌ No | | Captions | ✅ Yes, with built-in intl support and custom metadata handling | ⚠️ Basic | ⚠️ Basic | | TypeScript | ✅ Excellent | ⚠️ Partial | ✅ Good | | Bundle size | 📨 ~11KB | 📨 ~10KB | 📦 ~64KB | | Testing | ✅ Yes | ✅ Yes | ✅ Yes | | Documentation | ⚠️ Basic | ✅ Good | ✅ Excellent |
Unique selling points:
- Only library with true multi-track support
- Advanced queue management with priorities
- Built-in internationalized subtitle engine
- Great for complex audio applications (games, music apps, etc.)
🎯 Target Users
Ideal for:
- Music/audio streaming apps
- Educational platforms with narration
- Games with sound effects and music
- Podcast/audio book players
- Complex web applications needing concurrent audio
Not ideal for:
- Native mobile apps (React Native has different APIs using Expo-audio, I might publish one I already built)
🤝 Next Steps for Maintainers
- Review this analysis - Validate findings
- Prioritize fixes - Start with issues
- Set up testing - Prevent future regressions
- Improve CI/CD - Automate quality checks
- Plan major version - If breaking changes needed
- Engage community - Share roadmap, gather feedback
Demo source code
- Refer to the Git repo
Example
Initialize the main class RATM (React Audio Track Manager) with parameters.
import { RATM } from "react-audio-tracks"
import Subtitles from "./Subtitles.json"
RATM.initialize({
debug: true,
subtitlesJSON: Subtitles,
trackLength: 3,
masterVolume: 0.7,
defaultAudioOptions: {
locale: "fr",
},
fallbackLocale: "fr",
supportedLocales: ["en", "fr", "ko"],
})
//recommended settings for tracks
RATM.updateAllTracks({ autoPlay: true, allowDuplicates: true })Example using the custom react hook
import React, { useEffect, useRef } from "react"
import {
RATM,
useAudiotracks,
useTrackStream,
} from "react-audio-tracks"
const TestScreen = () => {
// listen to the global state
const state = useAudiotracks()
// listen to the individual track stream state of the track index #0
const [stream, trackInstance] = useTrackStream(0)
const audioRef = useRef<Array<HTMLAudioElement | null>>([])
useEffect(() => {
if (state) {
const { tracks, masterVolume, globalMuted, ...rest } = state
// state of every tracks
tracks.forEach((track, idx) => {
const { queue, isPlaying, volume, muted } = track
// same thing as `trackInstance.getState()`
console.log(
`Track [${idx}] - volume: ${volume} muted: ${muted} is ${
isPlaying ? "playing" : "not playing"
}`
)
})
}
}, [state])
useEffect(() => {
if (stream) {
const { caption, audioItemState, innerAudioState } = stream
if (caption) {
console.log(`Track [#0] is displaying caption: ${caption.text}`)
}
const currentAudioState = audioItemState
// same thing as `trackInstance?.getCurrentAudio()`
const nextAudioState = trackInstance?.getNextAudio()
// state of the AudioItem class
if (currentAudioState) {
const { src, filename, paused, ended, started } = currentAudioState
console.log(
`Track [#0] is currently playing audio item state ${JSON.stringify(
currentAudioState
)}`
)
}
if (nextAudioState) {
const { src, filename, paused, ended, started } = nextAudioState
console.log(
`Track [#0]'s current audio item state ${JSON.stringify(
nextAudioState
)}`
)
}
// state of the inner HTMLAudioElement
if (innerAudioState) {
console.log(
`Track [#0]'s inner HTMLAudioElement state ${JSON.stringify(
innerAudioState
)}`
)
}
}
}, [stream])
const loopAudioOnTrack0 = () => {
RATM.purgeTrack(0)
RATM.registerAudio("/audios/drumline1.mp3", {
trackIdx: 0,
onPlay: () => {
console.log("Drumline part 1 started")
},
onEnd: () => {
console.log("Drumline part 1 ended")
},
})
RATM.registerAudio("/audios/drumline2.mp3", {
trackIdx: 0,
loop: true,
onPlay: () => {
console.log("Drumline part 2 started")
},
})
}
const playAudioOnTrack1 = () => {
RATM.registerAudio("/audios/bassline1.mp3", {
trackIdx: 1,
onEnd: () => {
console.log("Bassline ended")
},
})
}
const playWithoutTrack = () => {
// not assigning any tracks
const audio: HTMLAudioElement = RATM.playAudio(
"/audios/guitar1.mp3",
{
onEnd: () => {
audioRef.current[0] = null
},
}
)
audioRef.current[0] = audio
}
const pauseTrack = () => {
trackInstance?.togglePlay(false)
}
const resumeTrack = () => {
trackInstance?.resumeTrack()
}
const playTrack = () => {
trackInstance?.togglePlay(true)
}
const togglePlayTrack = () => {
trackInstance?.togglePlay()
}
const stopGuitar = () => {
// will stop the audio and leave it to the garbage collector to clean up.
if (audioRef.current[0]) {
audioRef.current[0].dispatchEvent(new Event("ended"))
}
}
const changemasterVolume = RATM.setMasterVolume
return <></>
}Maintenance
I am using changeset to make versioning easier.
pnpm changesetHow it's made
pnpm add react
pnpm add -D typescript tsup @types/react @changesets/cli
git init
pnpm run lint
pnpm run build
pnpm changeset initLicense
React-audio-track is licensed under the MIT License.
