Custom HTML Video Player With Playlist (Step-By-Step Example)

Welcome to a tutorial and example on how to create a custom video player with a playlist. Want to build your own video player and add a playlist to it? Sadly, things are not that straightforward. There are no native implementations of a video playlist in HTML and Javascript. The only way to create a playlist is to build our own – 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 videos are included in the zip file, download your own.
  • Edit video.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 VIDEO PLAYER WITH PLAYLIST

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

 

STEP 1) THE HTML

video.html
<div id="vWrap">
  <!-- (A) VIDEO TAG -->
  <video id="vVid"></video>
 
  <!-- (B) PLAY/PAUSE BUTTON -->
  <button id="vPlay" disabled><span id="vPlayIco" class="material-icons">
    play_arrow
  </span></button>
 
  <!-- (C) TIME -->
  <div id="vCron">
    <span id="vNow"></span> / <span id="vTime"></span>
  </div>
 
  <!-- (D) SEEK BAR -->
  <input id="vSeek" type="range" min="0" value="0" step="1" disabled/>
 
  <!-- (E) VOLUME SLIDE -->
  <span id="vVolIco" class="material-icons">volume_up</span>
  <input id="vVolume" type="range" min="0" max="1" value="1" step="0.1" disabled/>
 
  <!-- (F) PLAYLIST -->
  <div id="vList"></div>
</div>

There are 6 components for this custom video player.

  1. <video id="vVid"> The video tag itself.
  2. <button id="vPlay"> Play and pause button.
  3. <span id="vNow"></span> – The current playtime. <span id="vTime"></span> – Total time of the current video.
  4. <input id="vSeek"> Time seek slider bar, this is in seconds.
  5. <input id="vVolume"> Volume slider bar, this is a value from 0.0 (mute) to 1.0 (loudest).
  6. <div id="vList"> For the video playlist, we will use Javascript to generate it.

 

 

STEP 2) INITIALIZE VIDEO PLAYER

video.js
window.addEventListener("DOMContentLoaded", () => {
  // (A) PLAYER INIT
  // (A1) PLAYLIST - CHANGE TO YOUR OWN!
  let playlist = [
    {name: "Video A", src: "v1.mp4"},
    {name: "Video B", src: "v2.mp4"},
    {name: "Video C", src: "v3.mp4"}
  ];
 
  // (A2) VIDEO PLAYER & GET HTML CONTROLS
  const video = document.getElementById("vVid"),
        vPlay = document.getElementById("vPlay"),
        vPlayIco = document.getElementById("vPlayIco"),
        vNow = document.getElementById("vNow"),
        vTime = document.getElementById("vTime"),
        vSeek = document.getElementById("vSeek"),
        vVolume = document.getElementById("vVolume"),
        vVolIco = document.getElementById("vVolIco"),
        vList = document.getElementById("vList");
 
  // (A3) BUILD PLAYLIST
  for (let i in playlist) {
    let row = document.createElement("div");
    row.className = "vRow";
    row.innerHTML = playlist[i]["name"];
    row.addEventListener("click", () => { vidPlay(i); });
    playlist[i]["row"] = row;
    vList.appendChild(row);
  }
});
  • The first thing we do in Javascript is Captain Obvious. Wait until DOMContentLoaded before proceeding, make sure the HTML is done loading.
  • (A1) The playlist should be self-explanatory. Each video entry should have the track name name and source src.
  • (A2) Get all the HTML elements.
  • (A3) Loop through playlist to generate the HTML. Just take note that clicking on a track will trigger vidPlay().

 

STEP 3) PLAY MECHANISM

video.js
// (B) PLAY MECHANISM
// (B1) FLAGS
var vidNow = 0, // current video
    vidStart = false, // auto start next video
 
// (B2) PLAY SELECTED VIDEO
vidPlay = (idx, nostart) => {
  vidNow= idx;
  vidStart = nostart ? false : true;
  video.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
video.addEventListener("canplay", () => { if (vidStart) {
  video.play();
  vidStart = false;
}});
 
// (B4) AUTOPLAY NEXT VIDEO IN THE PLAYLIST
video.addEventListener("ended", () => {
  vidNow++;
  if (vidNow >= playlist.length) { vidNow = 0; }
  vidPlay(vidNow);
});
 
// (B5) INIT SET FIRST VIDEO
vidPlay(0, true);
  • (B1) We use 2 flags to control the player.
    • vidNow keeps track of which video is currently playing in playlist.
    • vidStart will be used to control autoplay in (B3).
  • (B2) vidPlay() will play the selected video. For example, vidPlay(1) will play the second video in playlist. This works by setting video.src = playlist[N]["src"]. Again, take note of vidStart here.
  • (B3) Start playing once the current video is sufficiently buffered, but only if vidStart is set.
    • Basically, canplay can be triggered via 2 possible means. When we change video.src = NEW VIDEO, or upon recovering from a bad connection.
    • When the user has a bad Internet connection, we don’t want to force a patchy playback.
    • Thus the existence of vidStart, to make sure autoplay only applies after clicking on a playlist item (or when auto-starting the next video in the list).
  • (B4) When the current video ends, vidNow++ will play the next one.

 

 

STEP 4) PLAY/PAUSE BUTTON

video.js
// (C) PLAY/PAUSE BUTTON
// (C1) AUTO SET PLAY/PAUSE TEXT
video.addEventListener("play", () => {
  vPlayIco.innerHTML = "pause";
});
video.addEventListener("pause", () => {
  vPlayIco.innerHTML = "play_arrow";
});
 
 // (C2) CLICK TO PLAY/PAUSE
vPlay.addEventListener("click", () => {
  if (video.paused) { video.play(); }
  else { video.pause(); }
});
  • (C1) Automatically update the play/pause icon as the video play/pause. By the way, this is using Google’s Material Icons. Links below.
  • (C2) Click to play if the video is paused, click to pause if the video is playing.

 

STEP 5) TRACK TIME

video.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
video.addEventListener("loadedmetadata", () => {
  vNow.innerHTML = timeString(0);
  vTime.innerHTML = timeString(video.duration);
});
 
// (D3) UPDATE TIME ON PLAYING
video.addEventListener("timeupdate", () => {
  vNow.innerHTML = timeString(video.currentTime);
});
  • (D1) In Javascript, the video has 2 very convenient properties – video.duration and videdo.currentTime. Problem is, they are in seconds. Thus we need a support function to format a “nice time string”.
  • (D2) Update the total playtime on receiving the metadata.
  • (D3) Update the current time while the video is playing.

 

 

 

STEP 6) TIME SEEK BAR

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

  // (E3) UPDATE SEEK BAR ON PLAYING
  video.addEventListener("timeupdate", () => {
    if (!vSeeking) { vSeek.value = Math.floor(video.currentTime); }
  });
});
  • (E1) Set the max time of the seek bar on receiving the video metadata.
  • (E2) Update video.currentTime when the user changes the seek bar.
  • (E3) Update the seek bar as the video plays.
  • (E2 & E3) The vSeeking flag is kind of confusing. But basically, we don’t want (E3) to update while the user is manually dragging the bar.

 

STEP 7) VOLUME

video.js
// (F) VOLUME
vVolume.addEventListener("change", () => {
  video.volume = vVolume.value;
  vVolIco.innerHTML = (vVolume.value==0 ? "volume_mute" : "volume_up");
});

Self-explanatory.

 

 

STEP 8) ENABLE/DISABLE ALL CONTROLS

video.js
// (G) ENABLE/DISABLE CONTROLS
video.addEventListener("canplay", () => {
  vPlay.disabled = false;
  vVolume.disabled = false;
  vSeek.disabled = false;
});
video.addEventListener("waiting", () => {
  vPlay.disabled = true;
  vVolume.disabled = true;
  vSeek.disabled = true;
});

This final bit enables all controls when the player can play, disables all controls while the player is still loading.

P.S> We can use canplaythrough to prevent “half-way breaks”, but the loading will take longer, depending on how large the video file is.

 

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 video player 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 *