Video Tutorial:
If you are interested to learn by watching a video tutorial rather than reading this blog post, you can watch the video down below. Also subscribe to my youtube channel where I post new tips, tricks and tutorials every alternate day.
Project Folder Structure:
Before we start coding let us take a look at the project folder structure. We create a project folder called – ‘Custom Video Player’. Inside this folder, we have three files. The first file is index.html which is the HTML document. Next, we have style.css which is the stylesheet. Finally, we have script.js which is the script file.
HTML:
We start with the HTML code. First, copy the code below and paste it into your HTML document.
<!DOCTYPE html> <html lang="en"> <head> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Custom Video Player</title> <!-- Font Awesome Icons --> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.1.1/css/all.min.css" /> <!-- Google Fonts --> <link href="https://fonts.googleapis.com/css2?family=Roboto+Mono&display=swap" rel="stylesheet" /> <!-- Stylesheet --> <link rel="stylesheet" href="style.css" /> </head> <body> <div class="container"> <div class="rotate-container hide"> <div id="rotate-icon"> <i class="fa-solid fa-rotate-left"></i> <p>Rotate for a better experience</p> </div> </div> <div class="video-container" id="video-container"> <video id="my-video" preload="metadata"> <source src="https://dl.dropbox.com/s/l90y72zm97ayzhx/my%20video.mp4?raw=1" type="video/mp4" /> Your browser does not support the video tag </video> <div class="controls" id="controls"> <div class="progress-container flex-space"> <div id="progress-bar"> <div id="current-progress"></div> </div> <div class="song-timer"> <span id="current-time">00:00</span> <span>/</span> <span id="max-duration">00:00</span> </div> </div> <div id="video-controls" class="video-controls flex-space"> <div class="container-1 flex"> <div> <!-- Play video --> <button id="play-btn" class="control-btn"> <i class="fa-solid fa-play"></i> </button> <!-- Pause video--> <button id="pauseButton" class="control-btn hide"> <i class="fa-solid fa-pause"></i> </button> </div> <!-- volume of video--> <div id="volume" class="volume flex"> <span id="high"> <i class="fa-solid fa-volume-high"></i> </span> <span class="hide" id="low"> <i class="fa-solid fa-volume-low"></i> </span> <span class="hide" id="mute"> <i class="fa-solid fa-volume-xmark"></i> </span> <input type="range" min="0" max="100" value="50" id="volume-range" oninput="slider()" /> <span id="volume-num">50</span> </div> </div> <div class="container-2 flex-space"> <div class="playback"> <button id="playback-speed-btn">1x</button> <div class="playback-options hide"> <button onclick="setPlayback(0.5)">0.5</button> <button onclick="setPlayback(1.0)">1</button> <button onclick="setPlayback(2.0)">2</button> </div> </div> <!-- screen size --> <div id="size-screen"> <button id="screen-expand"> <i class="fa-solid fa-expand"></i> </button> <button id="screen-compress" class="hide"> <i class="fa-solid fa-compress"></i> </button> </div> </div> </div> </div> </div> </div> <!-- Script --> <script src="script.js"></script> </body> </html>
CSS:
Next, we style our video player using CSS. For this copy, the code provided to you below and paste it into your stylesheet.
* { padding: 0; margin: 0; box-sizing: border-box; outline: none; color: #ffffff; font-family: "Roboto Mono", monospace; } body { background-color: #2887e3; } .flex { display: flex; } .flex-space { display: flex; justify-content: space-between; } .container { padding: 1em 0; } #my-video { width: 100%; } .rotate-container { top: 0; position: absolute; height: 100%; width: 100%; background-color: rgba(0, 0, 0, 0.3); display: flex; justify-content: center; align-items: center; } #rotate-icon { display: flex; flex-direction: column; color: #dddddd; text-align: center; } .hide { display: none; } .video-container { width: 60%; position: absolute; transform: translate(-50%, -50%); left: 50%; top: 50%; box-shadow: 20px 30px 50px rgba(0, 0, 0, 0.2); } .controls { position: absolute; left: 0; right: 0; bottom: 0; background-color: rgba(35, 34, 39, 0.8); } .progress-container { align-items: center; padding: 0 0.5em; } .video-controls { flex-direction: row; align-items: center; } #progress-bar { position: relative; width: 75%; height: 5px; background-color: #000000; margin: 1em 0; vertical-align: 2px; border-radius: 5px; cursor: pointer; } .song-timer { font-size: 0.8em; width: 25%; text-align: right; } #current-progress { position: absolute; left: 0; display: inline-block; height: 5px; width: 0; background: #2887e3; border-radius: 5px; } #current-progress:after { content: ""; position: absolute; left: calc(100% - 1.5px); top: -2.5px; width: 10px; height: 10px; border-radius: 50%; background-color: #ffffff; } .playback { position: relative; } .control-btn, #screen-expand, #screen-compress { width: 3em; height: 3em; outline: none; border: none; background-color: transparent; } #size-screen { margin-left: auto; } .volume { align-items: center; margin-left: 0.6em; } #volume-range { position: relative; margin: 0 0.5em; cursor: pointer; height: 5px; -webkit-appearance: none; background-color: #000000; border-radius: 5px; outline: none; } input[type="range"]::-webkit-slider-thumb { -webkit-appearance: none; height: 10px; width: 10px; background-color: #2887e3; border-radius: 50%; cursor: pointer; border: none; } .fa-solid { font-size: 1.1rem; } .container-2 { width: 10%; min-width: 70px; align-items: center; } #playback-speed-btn { position: relative; background-color: transparent; border: 1px solid #ffffff; color: #ffffff; font-size: 0.9rem; border-radius: 5px; padding: 0.3em 0.25em; cursor: pointer; } .playback-options { position: absolute; bottom: 0; background-color: #000000; min-width: 5em; box-shadow: 0 8px 16px 0 rgba(0, 0, 0, 0.2); z-index: 1; } .playback-options button { color: #ffffff; border-left: 0; border-right: 0; border-top: 0; width: 100%; background-color: transparent; padding: 1em; text-decoration: none; display: block; } @media all and (display-mode: fullscreen) { .container { padding: 0; } .video-container { width: 100%; margin: 0; } .controls { position: absolute; display: block; bottom: 0; left: 0; width: 100%; z-index: 2; } #progress-bar { width: 80%; } .song-timer { width: 20%; font-size: 1.2em; } .fa-solid { color: #dddddd; } } @media only screen and (max-width: 768px) { .video-container, .controls { width: 100%; } span { display: inline; } #progress-bar { width: 60%; } .song-timer { width: 40%; font-size: 0.9em; } .fa-solid { font-size: 1rem; } .control-btn, #screen-expand, #screen-compress { width: 2em; height: 1.5em; } } @media only screen and (max-width: 768px) and (display-mode: fullscreen) { .video-container { margin-top: 50%; } }
Javascript:
Lastly, we add functionality to our custom video player using Javascript. Once again copy the code below and paste it into your script file.
We do this in fourteen steps:
Create initial references.
Implement slider()
Detect device type.
Implement functionality for the play and pause button.
Hide/Show playback speed options
Function to set playback speed.
Logic to mute video.
Function to set Fullscreen.
Function to exit Fullscreen.
Create a function to format the current time & maximum time.
Create a function to update progress & timer.
Implement a click event on the progress bar.
Function on window load.
let videoContainer = document.querySelector(".video-container"); let container = document.querySelector(".container"); let myVideo = document.getElementById("my-video"); let rotateContainer = document.querySelector(".rotate-container"); let videoControls = document.querySelector(".controls"); let playButton = document.getElementById("play-btn"); let pauseButton = document.getElementById("pauseButton"); let volume = document.getElementById("volume"); let volumeRange = document.getElementById("volume-range"); let volumeNum = document.getElementById("volume-num"); let high = document.getElementById("high"); let low = document.getElementById("low"); let mute = document.getElementById("mute"); let sizeScreen = document.getElementById("size-screen"); let screenCompress = document.getElementById("screen-compress"); let screenExpand = document.getElementById("screen-expand"); const currentProgress = document.getElementById("current-progress"); const currentTimeRef = document.getElementById("current-time"); const maxDuration = document.getElementById("max-duration"); const progressBar = document.getElementById("progress-bar"); const playbackSpeedButton = document.getElementById("playback-speed-btn"); const playbackContainer = document.querySelector(".playback"); const playbackSpeedOptions = document.querySelector(".playback-options"); function slider() { valPercent = (volumeRange.value / volumeRange.max) * 100; volumeRange.style.background = `linear-gradient(to right, #2887e3 ${valPercent}%, #000000 ${valPercent}%)`; } //events object let events = { mouse: { click: "click", }, touch: { click: "touchstart", }, }; let deviceType = ""; //Detech touch device const isTouchDevice = () => { try { //We try to create TouchEvent (it would fail for desktops and throw error) document.createEvent("TouchEvent"); deviceType = "touch"; return true; } catch (e) { deviceType = "mouse"; return false; } }; //play and pause button playButton.addEventListener("click", () => { myVideo.play(); pauseButton.classList.remove("hide"); playButton.classList.add("hide"); }); pauseButton.addEventListener( "click", (pauseVideo = () => { myVideo.pause(); pauseButton.classList.add("hide"); playButton.classList.remove("hide"); }) ); //playback playbackContainer.addEventListener("click", () => { playbackSpeedOptions.classList.remove("hide"); }); //if user clicks outside or on the option window.addEventListener("click", (e) => { if (!playbackContainer.contains(e.target)) { playbackSpeedOptions.classList.add("hide"); } else if (playbackSpeedOptions.contains(e.target)) { playbackSpeedOptions.classList.add("hide"); } }); //playback speed const setPlayback = (value) => { playbackSpeedButton.innerText = value + "x"; myVideo.playbackRate = value; }; //mute video const muter = () => { mute.classList.remove("hide"); high.classList.add("hide"); low.classList.add("hide"); myVideo.volume = 0; volumeNum.innerHTML = 0; volumeRange.value = 0; slider(); }; //when user click on high and low volume then mute the audio high.addEventListener("click", muter); low.addEventListener("click", muter); //for volume volumeRange.addEventListener("input", () => { //for converting % to decimal values since video.volume would accept decimals only let volumeValue = volumeRange.value / 100; myVideo.volume = volumeValue; volumeNum.innerHTML = volumeRange.value; //mute icon, low volume, high volume icons if (volumeRange.value < 50) { low.classList.remove("hide"); high.classList.add("hide"); mute.classList.add("hide"); } else if (volumeRange.value > 50) { low.classList.add("hide"); high.classList.remove("hide"); mute.classList.add("hide"); } }); //Screen size screenExpand.addEventListener("click", () => { screenCompress.classList.remove("hide"); screenExpand.classList.add("hide"); videoContainer .requestFullscreen() .catch((err) => alert("Your device doesn't support full screen API")); if (isTouchDevice) { let screenOrientation = screen.orientation || screen.mozOrientation || screen.msOrientation; if (screenOrientation.type == "portrait-primary") { //update styling for fullscreen pauseVideo(); rotateContainer.classList.remove("hide"); const myTimeout = setTimeout(() => { rotateContainer.classList.add("hide"); }, 3000); } } }); //if user presses escape the browser fire 'fullscreenchange' event document.addEventListener("fullscreenchange", exitHandler); document.addEventListener("webkitfullscreenchange", exitHandler); document.addEventListener("mozfullscreenchange", exitHandler); document.addEventListener("MSFullscreenchange", exitHandler); function exitHandler() { //if fullscreen is closed if ( !document.fullscreenElement && !document.webkitIsFullScreen && !document.mozFullScreen && !document.msFullscreenElement ) { normalScreen(); } } //back to normal screen screenCompress.addEventListener( "click", (normalScreen = () => { screenCompress.classList.add("hide"); screenExpand.classList.remove("hide"); if (document.fullscreenElement) { if (document.exitFullscreen) { document.exitFullscreen(); } else if (document.mozCancelFullScreen) { document.mozCancelFullScreen(); } else if (document.webkitExitFullscreen) { document.webkitExitFullscreen(); } } }) ); //Format time const timeFormatter = (timeInput) => { let minute = Math.floor(timeInput / 60); minute = minute < 10 ? "0" + minute : minute; let second = Math.floor(timeInput % 60); second = second < 10 ? "0" + second : second; return `${minute}:${second}`; }; //Update progress every second setInterval(() => { currentTimeRef.innerHTML = timeFormatter(myVideo.currentTime); currentProgress.style.width = (myVideo.currentTime / myVideo.duration.toFixed(3)) * 100 + "%"; }, 1000); //update timer myVideo.addEventListener("timeupdate", () => { currentTimeRef.innerText = timeFormatter(myVideo.currentTime); }); //If user click on progress bar isTouchDevice(); progressBar.addEventListener(events[deviceType].click, (event) => { //start of progressbar let coordStart = progressBar.getBoundingClientRect().left; //mouse click position let coordEnd = !isTouchDevice() ? event.clientX : event.touches[0].clientX; let progress = (coordEnd - coordStart) / progressBar.offsetWidth; //set width to progress currentProgress.style.width = progress * 100 + "%"; //set time myVideo.currentTime = progress * myVideo.duration; //play myVideo.play(); pauseButton.classList.remove("hide"); playButton.classList.add("hide"); }); window.onload = () => { //display duration myVideo.onloadedmetadata = () => { maxDuration.innerText = timeFormatter(myVideo.duration); }; slider(); };
That’s it for this tutorial. If you face any issues while creating this code, you can download the source code by clicking the ‘Download Code’ button below.
Also, I would love to hear from you guys, so if you have any queries, suggestions, or feedback you can comment below.
Happy Coding!