The problem
I'm trying to loop some audio in a React app created with create-react-app
using useEffect
. I've got it working but there's a short delay between the audio ending and re-starting. I want to use the audio as backing tracks to play guitar to so I need it to loop perfectly smoothly. I'm confident that the track length is correct, I recorded it myself and exported exactly 8 bars, and it loops fine in iTunes.
Current code
Thanks to the accepted answer in this question, my audio player function works fine, and currently looks like this:
import React, { useState, useEffect } from 'react'
const useAudio = audioPath => {
const [audio] = useState(new Audio(audioPath))
const [playing, setPlaying] = useState(false)
const toggle = () => setPlaying(!playing)
useEffect(() => {
playing ? audio.play() : audio.pause()
},
[playing, audio]
)
useEffect(() => {
audio.addEventListener('ended', () => {
audio.currentTime = 0
audio.play()
setPlaying(true)
})
}, [audio])
return [playing, toggle]
}
const Player = ({ audioPath }) => {
const [playing, toggle] = useAudio(audioPath)
return (
<div>
<button onClick={toggle}>{playing ? 'Pause' : 'Play'}</button>
</div>
)
}
export default Player
The audioPath
is passed in and is just a relative path, that loads fine. It plays fine, it pauses fine, it does loop, just with a tiny delay between loops.
What I've tried
As you can see from the code, I've been trying to hijack the audio ended
event and setting the audio back to the start of the track but obviously this isn't instant - I'm not really sure how to handle this. I've tried in my first useEffect
function checking the time of the audio and if it's within say 500ms of the end of the track setting the time back to 0 but I couldn't get that working, and it seems very hacky and unreliable anyway. Ideally I'm after a proper solution that will work with any tracks as I want to add more.
Demo
Go to the very bottom of the GitHub pages site where this is hosted, expand the very bottom panel and hit play.