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!
TABLE OF CONTENTS
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 => {
// (D1-1) 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);
// (D1-2) 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
vVolIco.addEventListener("click", () => {
video.volume = video.volume==0 ? 1 : 0 ;
vVolume.value = video.volume;
vVolIco.innerHTML = (vVolume.value==0 ? "volume_mute" : "volume_up");
});
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, and disables all controls while the player is still loading.
P.S. We can use canplaythrough
to prevent “halfway breaks”, but the loading will take longer, depending on how large the video file is.
DOWNLOAD & NOTES
Here is the download link to the example code, so you don’t have to copy-paste everything.
SORRY FOR THE ADS...
But someone has to pay the bills, and sponsors are paying for it. I insist on not turning Code Boxx into a "paid scripts" business, and I don't "block people with Adblock". Every little bit of support helps.
Buy Me A Coffee Code Boxx eBooks
EXAMPLE CODE DOWNLOAD
Click here for the source code on GitHub gist, just click on “download zip” or do a git clone. I have released it under the MIT license, so feel free to build on top of it or use it in your own project.
EXTRA 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.
BACK/NEXT BUTTONS
I don’t see a need to add these buttons as there is already a playlist. But if you want to add them, here’s a simple Javascript snippet:
function backnext (n) {
if (n) { vidNow++; } else { vidNow--; }
if (vidNow >= playlist.length) { vidNow = 0; }
if (vidNow < 0) { vidNow = playlist.length - 1; }
vidPlay(vidNow);
}
MULTIPLE INSTANCES
Not impossible, but there are several design and technical issues.
- Do you really need multiple playlist players on a single page?
- Isn’t a single playlist easier to manage than multiple playlists?
- So what happens when an instance is already playing? Pause that instance when the user clicks on another one?
- Is playing multiple videos on the same page a good idea for mobile devices and slow connections?
- Or track all the instances, and allow only one to play at a time? Isn’t this as good as a single instance?
- How are you going to squeeze multiple players onto small screens? Good design idea?
- A “single player with multiple different playlists” makes more sense.
So yep, feel free to challenge yourself if you really need “multiple instances”.
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!
This wouldn’t work on Apple devices until I modified the video tag to this:
Sorry, the security filter removed the code snippet. I don’t have half-eaten fruit devices to verify…
I need help putting VAST ads on the playlist as prerolls.
https://code-boxx.com/faq/#help “Help on a deeper level”
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.
Thanks for this great code.
I have things working, but regarding fullscreen, is it possible to do what youtube does with the fullscreen option?
That is, make the current video fullscreen (not the page, which includes the playlist). This would be especially useful on phones.
https://code-boxx.com/fullscreen-mode-javascript/