Custom Audio Player With Playlist (Step-By-Step Example)

Welcome to a tutorial and example on how to create a custom audio player with a playlist. Looking to build your own audio player and add a playlist to it? Sadly, there are no native implementations of a playlist as at the time of writing. So the only way to build a playlist is to create an array of songs, and feed it into the player – Read on for an example!

ⓘ I have included a zip file with all the source code at the start of this tutorial, so you don’t have to copy-paste everything… Or if you just want to dive straight in.

 

 

TABLE OF CONTENTS

Download & Notes Custom Player Useful Bits & Links
The End

 

DOWNLOAD & NOTES

Firstly, here is the download link to the example code as promised.

 

QUICK NOTES

  • No songs are included in the zip file, download your own.
  • Edit player.js and set your playlist in let playlist = ...
If you spot a bug, feel free to comment below. I try to answer short questions too, but it is one person versus the entire world… If you need answers urgently, please check out my list of websites to get help with programming.

 

 

EXAMPLE CODE DOWNLOAD

Click here to download all the example source code, I have released it under the MIT license, so feel free to build on top of it or use it in your own project.

 

 

CUSTOM AUDIO PLAYER WITH PLAYLIST

All right, let us now get into the example of creating a custom audio player with a playlist.

 

STEP 1) THE HTML

player.html
<div id="aWrap">
  <!-- (A) PLAY/PAUSE BUTTON -->
  <button id="aPlay" disabled><span id="aPlayIco" class="material-icons">
    play_arrow
  </span></button>
 
  <!-- (B) TIME -->
  <div id="aCron">
    <span id="aNow"></span> / <span id="aTime"></span>
  </div>
 
  <!-- (C) SEEK BAR -->
  <input id="aSeek" type="range" min="0" value="0" step="1" disabled/>
 
  <!-- (D) VOLUME SLIDE -->
  <span id="aVolIco" class="material-icons">volume_up</span>
  <input id="aVolume" type="range" min="0" max="1" value="1" step="0.1" disabled/>
 
  <!-- (E) PLAYLIST -->
  <div id="aList"></div>
</div>

There are only 5 components for this custom audio player.

  1. <button id="aPlay"> – Play/pause button.
  2. <span id="aNow"></span> – The current playtime. <span id="aTime"></span> – Total time of the current track.
  3. <input id="aSeek"> Time seek slider bar, this is in seconds.
  4. <input id="aVolume"> Volume slider bar, this is a value from 0.0 (mute) to 1.0 (loudest).
  5. <div id="aList"> The playlist, generated with Javascript in the next step.

 

 

STEP 2) INITIALIZE PLAYER

player.js
window.addEventListener("DOMContentLoaded", () => {
  // (A) PLAYER INIT
  // (A1) PLAYLIST - CHANGE TO YOUR OWN!
  let playlist = [
    {name: "Sugar Plum Fairy", src: "S1.mp3"},
    {name: "If I Had A Chicken", src: "S2.mp3"},
    {name: "Run Little Chicken", src: "S3.mp3"}
  ];
 
  // (A2) AUDIO PLAYER & GET HTML CONTROLS
  const audio = new Audio(),
        aPlay = document.getElementById("aPlay"),
        aPlayIco = document.getElementById("aPlayIco"),
        aNow = document.getElementById("aNow"),
        aTime = document.getElementById("aTime"),
        aSeek = document.getElementById("aSeek"),
        aVolume = document.getElementById("aVolume"),
        aVolIco = document.getElementById("aVolIco"),
        aList = document.getElementById("aList");
 
  // (A3) BUILD PLAYLIST
  for (let i in playlist) {
    let row = document.createElement("div");
    row.className = "aRow";
    row.innerHTML = playlist[i]["name"];
    row.addEventListener("click", () => { audPlay(i); });
    playlist[i]["row"] = row;
    aList.appendChild(row);
  }
});
  • Captain Obvious to the rescue. We wait until DOMContentLoaded before proceeding, to prevent things from screwing up.
  • (A1) The playlist, this is an array of objects. Each entry should have the track name name and source src. Quick advice – It is more reliable to use an absolute URL here. I.E. http://site.com/songs/mysong.mp3 instead of mysong.mp3.
  • (A2) Create a new audio object, get all the HTML controls.
  • (A3) Loop through playlist to generate the HTML. Just take note that clicking on a track will trigger audPlay().

 

STEP 3) PLAY MECHANISM

player.js
// (B) PLAY MECHANISM
// (B1) FLAGS
var audNow = 0, // current song
    audStart = false, // auto start next song
 
// (B2) PLAY SELECTED SONG
audPlay = (idx, nostart) => {
  audNow = idx;
  audStart = nostart ? false : true;
  audio.src = playlist[idx]["src"];
  for (let i in playlist) {
    if (i == idx) { playlist[i]["row"].classList.add("now"); }
    else { playlist[i]["row"].classList.remove("now"); }
  }
};
 
// (B3) AUTO START WHEN SUFFICIENTLY BUFFERED
audio.addEventListener("canplay", () => { if (audStart) {
  audio.play();
  audStart = false;
}});
 
// (B4) AUTOPLAY NEXT SONG IN THE PLAYLIST
audio.addEventListener("ended", () => {
  audNow++;
  if (audNow >= playlist.length) { audNow = 0; }
  audPlay(audNow);
});
 
// (B5) INIT SET FIRST SONG
audPlay(0, true);
  • (B1) 2 flags are used to control the player.
    • Remember that playlist is an array? audNow keeps track of which song is currently playing.
    • audStart is used to control autoplay in (B3).
  • (B2) audPlay() will play the selected song. For example, audPlay(2) will play the third song in playlist. This works very simply by setting audio.src = playlist[N]["src"]. Also, take note of setting audStart here.
  • (B3) Automatically start playing when the current track is sufficiently buffered, but only if audStart is set.
    • Basically, canplay can be triggered via 2 possible means. When we change audio.src = NEW SONG, or upon recovering from a bad connection.
    • We don’t want to piss the users by forcing a patchy playback when they have a bad connection.
    • Thus the use of the audStart flag, to make sure autoplay is only triggered after clicking on a playlist item (or when auto-starting the next song in the list).
  • (B4) When the current song ends, we do a audNow++ and play the next song.

 

 

STEP 4) PLAY/PAUSE BUTTON

player.js
// (C) PLAY/PAUSE BUTTON
// (C1) AUTO SET PLAY/PAUSE TEXT
audio.addEventListener("play", () => {
  aPlayIco.innerHTML = "pause";
});
audio.addEventListener("pause", () => {
  aPlayIco.innerHTML = "play_arrow";
});
 
 // (C2) CLICK TO PLAY/PAUSE
aPlay.addEventListener("click", () => {
  if (audio.paused) { audio.play(); }
  else { audio.pause(); }
});
  • (C1) Automatically set the play/pause icon. This is Google’s Material Icon by the way. Links below.
  • (C2) Self-explanatory? If the audio is paused, we click to play. If the audio is playing, we click to pause.

 

STEP 5) TRACK TIME

player.js
// (D) TRACK PROGRESS
// (D1) SUPPORT FUNCTION - FORMAT HH:MM:SS
var timeString = (secs) => {
  // HOURS, MINUTES, SECONDS
  let ss = Math.floor(secs),
      hh = Math.floor(ss / 3600),
      mm = Math.floor((ss - (hh * 3600)) / 60);
  ss = ss - (hh * 3600) - (mm * 60);
 
  // RETURN FORMATTED TIME
  if (hh>0) { mm = mm<10 ? "0"+mm : mm; }
  ss = ss<10 ? "0"+ss : ss;
  return hh>0 ? `${hh}:${mm}:${ss}` : `${mm}:${ss}` ;
};
 
// (D2) INIT SET TRACK TIME
audio.addEventListener("loadedmetadata", () => {
  aNow.innerHTML = timeString(0);
  aTime.innerHTML = timeString(audio.duration);
});
 
// (D3) UPDATE TIME ON PLAYING
audio.addEventListener("timeupdate", () => {
  aNow.innerHTML = timeString(audio.currentTime);
});
  • (D1) Both audio.duration and audio.currentTime are in seconds. Thus the need for this support function to create a “nice-looking time string”.
  • (D2) Update the HTML total track time on receiving the metadata.
  • (D3) Update the HTML time while the track is playing.

 

 

STEP 6) TIME SEEK BAR

player.js
// (E) SEEK BAR
audio.addEventListener("loadedmetadata", () => {
  // (E1) SET SEEK BAR MAX TIME
  aSeek.max = Math.floor(audio.duration);
 
  // (E2) USER CHANGE SEEK BAR TIME
  var aSeeking = false; // USER IS NOW CHANGING TIME
  aSeek.addEventListener("input", () => {
    aSeeking = true; // PREVENTS CLASH WITH (E3)
  });
  aSeek.addEventListener("change", () => {
    audio.currentTime = aSeek.value;
    if (!audio.paused) { audio.play(); }
    aSeeking = false;
  });

  // (E3) UPDATE SEEK BAR ON PLAYING
  audio.addEventListener("timeupdate", () => {
    if (!aSeeking) { aSeek.value = Math.floor(audio.currentTime); }
  });
});
  • (E1) Set the max of the seek bar on receiving the metadata.
  • (E2) When the user drags the seek bar, we update audio.currentTime accordingly.
  • (E3) Update the seek bar as the track plays.
  • (E2 & E3) Take note of the aSeeking flag here. Basically, we don’t want (E3) to update while the user is manually dragging the bar.

 

 

STEP 7) VOLUME

player.js
// (F) VOLUME
aVolume.addEventListener("change", () => {
  audio.volume = aVolume.value;
  aVolIco.innerHTML = (aVolume.value==0 ? "volume_mute" : "volume_up");
});

Don’t think this needs any explanation…

 

STEP 8) ENABLE/DISABLE ALL CONTROLS

player.js
// (G) ENABLE/DISABLE CONTROLS
audio.addEventListener("canplay", () => {
  aPlay.disabled = false;
  aVolume.disabled = false;
  aSeek.disabled = false;
});
audio.addEventListener("waiting", () => {
  aPlay.disabled = true;
  aVolume.disabled = true;
  aSeek.disabled = true;
});

Enable all controls when the player can play, disable all controls while the player is still loading. We can use canplaythrough to prevent “half-way breaks”, but the initial loading will take longer.

 

USEFUL BITS & LINKS

That’s all for the tutorial, and here is a small section on some extras and links that may be useful to you.

 

COMPATIBILITY CHECKS

This audio player will obviously not work with the ancient browsers. But it should work well across all “Grade A” modern browsers.

 

LINKS & REFERENCES

 

THE END

Thank you for reading, and we have come to the end. I hope that it has helped you to better understand, and if you want to share anything with this guide, please feel free to comment below. Good luck and happy coding!

Leave a Comment

Your email address will not be published. Required fields are marked *