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
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 inlet playlist = ...
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
<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.
<video id="vVid">
The video tag itself.<button id="vPlay">
Play and pause button.<span id="vNow"></span>
– The current playtime.<span id="vTime"></span>
– Total time of the current video.<input id="vSeek">
Time seek slider bar, this is in seconds.<input id="vVolume">
Volume slider bar, this is a value from 0.0 (mute) to 1.0 (loudest).<div id="vList">
For the video playlist, we will use Javascript to generate it.
STEP 2) INITIALIZE VIDEO PLAYER
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 namename
and sourcesrc
. - (A2) Get all the HTML elements.
- (A3) Loop through
playlist
to generate the HTML. Just take note that clicking on a track will triggervidPlay()
.
STEP 3) PLAY MECHANISM
// (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 inplaylist
.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 inplaylist
. This works by settingvideo.src = playlist[N]["src"]
. Again, take note ofvidStart
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 changevideo.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).
- Basically,
- (B4) When the current video ends,
vidNow++
will play the next one.
STEP 4) PLAY/PAUSE BUTTON
// (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
// (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
andvideo.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
// (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
// (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
// (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
- HTML Media Element– MDN
- HTML Video – WhatWG
- Simple PHP Video Gallery – Code Boxx
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!
Good morning,
it’s possible remove all control and enable the autoplay?
Sorry maybe for the stupid answer, but it’s is possible, I don’t understand how.
Thanks
Of course, but I will not recommend doing so. Autoplay video is tricky. It may not always work on mobile devices, and it depends on the user’s settings.
https://code-boxx.com/responsive-background-video/
Thanks.
Can you make the list y-overflow scroll after say 6 entries ?
(current implement is a space hog…)
(Next and prev buttons would rock, but I don’t wanna push my luckl
#vList { max-height: 123px; overflow-y: auto; }
awesome, found your page on google search
do you happen to know of a way to add a button somewhere to go to fullscreen mode (as well as from within fullscreen mode, go back to normal?)
thanks for sharing your html/css/js code <3
https://code-boxx.com/fullscreen-mode-javascript/
But I won’t bother for 2 reasons – By default, just double-click on the video to go fullscreen. Apple is still not implementing fullscreen mode properly on iPad/iPhone.