Here's a simple vanilla example:
const url = "https://upload.wikimedia.org/wikipedia/en/a/a9/Webern_-_Sehr_langsam.ogg";
const audio = new Audio(url);
const playBtn = document.querySelector("button");
const progressEl = document.querySelector('input[type="range"]');
let mouseDownOnSlider = false;
audio.addEventListener("loadeddata", () => {
progressEl.value = 0;
});
audio.addEventListener("timeupdate", () => {
if (!mouseDownOnSlider) {
progressEl.value = audio.currentTime / audio.duration * 100;
}
});
audio.addEventListener("ended", () => {
playBtn.textContent = "▶️";
});
playBtn.addEventListener("click", () => {
audio.paused ? audio.play() : audio.pause();
playBtn.textContent = audio.paused ? "▶️" : "⏸️";
});
progressEl.addEventListener("change", () => {
const pct = progressEl.value / 100;
audio.currentTime = (audio.duration || 0) * pct;
});
progressEl.addEventListener("mousedown", () => {
mouseDownOnSlider = true;
});
progressEl.addEventListener("mouseup", () => {
mouseDownOnSlider = false;
});
button {
font-size: 1.5em;
}
<button>▶️</button>
<input type="range" value="0" min="0" max="100" step="1">
The approach is to use an input[type="range"]
slider to reflect the progress and allow the user to seek through the track. When the range changes, set the audio.currentTime
attribute, using the slider as a percent (you could also adjust the max
attribute of the slider to match the audio.duration
).
In the other direction, I update the slider's progress on timeupdate
event firing.
One corner case is that if the user scrolls around with their mouse down on the slider, the timeupdate
event will keep firing, causing the progress to hop around between wherever the user's cursor is hovering and the current audio progress. I use a boolean and the mousedown
/mouseup
events on the slider to prevent this from happening.
See also JavaScript - HTML5 Audio / custom player's seekbar and current time for an extension of this code that displays the time.