Source Code Of Custom Video Player Using HTML, CSS And JavaScript. Learn How To Make Custom Video Player And Get Free Source Code.
Folder Structure
Resources
Prerequisite Sites
Codes
HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Custom Video Player | Cosas Learning</title>
<!-- Stylesheet -->
<link rel="stylesheet" href="style.css">
<!-- Google Icons -->
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Rounded:opsz,wght,FILL,[email protected],100..700,0..1,-50..200" />
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
</head>
<body>
<div class="container show-controls">
<div class="wrapper">
<div class="video-timeline">
<div class="progress-area">
<span>00:00</span>
<div class="progress-bar"></div>
</div>
</div>
<ul class="video-controls">
<li class="options left">
<button title="Backward 10s" class="skip-backward tooltip"><span class="material-icons">rotate_left</span></button>
<button title="Play" class="play-pause tooltip"><span class="material-icons">play_arrow</span></button>
<button title="Forward 10s" class="skip-forward tooltip"><span class="material-icons">rotate_right</span></button>
<button title="Mute" class="volume tooltip"><span class="material-icons">volume_up</span></button>
<input title="Volume" class="tooltip" id="volume_range" type="range" min="0" max="1" step="any">
<div class="video-timer">
<p class="current-time">00:00</p>
<p class="separator"> / </p>
<p class="video-duration">00:00</p>
</div>
</li>
<li class="options right">
<div class="playback-content">
<button title="Speed" class="playback-speed tooltip"><span class="material-symbols-rounded">slow_motion_video</span></button>
<ul class="speed-options">
<li data-speed="0.5">0.5x</li>
<li data-speed="0.75">0.75x</li>
<li data-speed="1" class="active">Normal</li>
<li data-speed="1.5">1.5x</li>
<li data-speed="2">2x</li>
</ul>
</div>
<button title="Miniplayer" class="pic-in-pic tooltip"><span class="material-icons">picture_in_picture_alt</span></button>
<button title="Full screen" class="fullscreen tooltip"><span class="material-icons">fullscreen</span></button>
<button title="New File" class="new-file tooltip tooltiplast"><label for="fileInput"><span class="material-icons">video_file</span></label></button>
<input type="file" id="fileInput" name="fileInput" accept="video/mp4,video/x-m4v,video/*" hidden>
</li>
</ul>
</div>
<video id="myVideo" src="path/to/video.mp4"></video>
</div>
<!-- Script -->
<script src="script.js"></script>
</body>
</html>
CSS
/*----------------- BASE -----------------*/
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
/*----------------- VARIABLES -----------------*/
:root {
/* Colors */
--white_color : rgb(255, 255, 255);
--orange_color : rgb(246, 99, 53);
--black_color : rgb(0, 0, 0);
--background_color : rgb(27, 31, 41);
--grey-color : rgb(128, 128, 128);
}
/*----------------- STYLING -----------------*/
body {
min-height: 100vh;
background: var(--background_color);
}
body, .container, .video-controls, .video-timer, .options {
display: flex;
align-items: center;
justify-content: center;
}
.container {
width: 98%;
user-select: none;
overflow: hidden;
max-width: 56.25em;
border-radius: 0.3125em;
background: var(--black_color);
aspect-ratio: 16 / 9;
position: relative;
box-shadow: 0 0.625em 1.25em rgba(0, 0, 0, 0.1);
}
.container.fullscreen {
max-width: 100%;
width: 100%;
height: 100vh;
border-radius: 0em;
}
.wrapper {
position: absolute;
left: 0;
right: 0;
z-index: 1;
opacity: 0;
bottom: -0.9375;
transition: all 0.08s ease;
}
.container.show-controls .wrapper {
opacity: 1;
bottom: 0;
transition: all 0.13s ease;
}
.wrapper::before {
content: "";
bottom: 0;
width: 100%;
z-index: -1;
position: absolute;
height: calc(100% + 2.1875em);
pointer-events: none;
background: linear-gradient(to top, rgba(0, 0, 0, 0.7), transparent);
}
.video-timeline {
height: 0.4375em;
width: 100%;
cursor: pointer;
padding: 0 1em;
}
.video-timeline .progress-area {
height: 0.1875em;
position: relative;
background: rgba(255, 255, 255, 0.6);
}
.progress-area span {
position: absolute;
left: 50%;
top: -1.5625em;
font-size: 0.8125em;
color: var(--white_color);
pointer-events: none;
transform: translateX(-50%);
}
.progress-area .progress-bar {
width: 0%;
height: 100%;
position: relative;
background:var(--orange_color);
}
.progress-bar::before {
content: "";
right: 0;
top: 50%;
height: 0.8125em;
width: 0.8125em;
position: absolute;
border-radius: 50%;
background: var(--orange_color);
transform: translateY(-50%);
}
.progress-bar::before, .progress-area span {
display: none;
}
.video-timeline:hover .progress-bar::before,
.video-timeline:hover .progress-area span {
display: block;
}
.wrapper .video-controls {
padding: 0.3125em 1.25em 0.625em;
}
.video-controls .options {
width: 100%;
}
.video-controls .options:first-child {
justify-content: flex-start;
}
.video-controls .options:last-child {
justify-content: flex-end;
}
.options button {
height: 2.5em;
width: 2.5em;
font-size: 1.1875em;
border: none;
cursor: pointer;
background: none;
color: var(--white_color);
border-radius: 0.1875em;
transition: all 0.3s ease;
}
.options button span {
height: 100%;
width: 100%;
line-height: 1.7em;
font-size: 1.5625em;
}
.options button:hover span {
color: var(--orange_color);
}
.options button:active span {
transform: scale(0.9);
}
.options #volume_range {
height: 0.3em;
max-width: 5em;
accent-color: var(--orange_color);
display: none;
cursor: pointer;
transition: all 0.9s ease;
}
.options .video-timer {
color: var(--white_color);
margin-left: 0.9375em;
font-size: 0.875em;
}
.video-timer .separator {
margin: 0 0.3125em;
font-size: 1em;
font-family: "Open sans";
}
.playback-content {
display: flex;
position: relative;
}
.playback-content .speed-options {
position: absolute;
list-style: none;
left: -2.5em;
bottom: 2.5em;
width: 5.9375em;
overflow: hidden;
opacity: 0;
border-radius: 0.25em;
pointer-events: none;
background: rgba(255, 255, 255, 0.9);
box-shadow: 0 0.625em 1.25em rgba(0, 0, 0, 0.1);
transition: opacity 0.13s ease;
}
.playback-content .speed-options.show {
opacity: 1;
pointer-events: auto;
}
.speed-options li {
cursor: pointer;
color: var(--black_color);
font-size: 0.875em;
margin: 0.125em 0;
padding: 0.3125em 0 0.3125em 0.9375em;
transition: all 0.1s ease;
}
.speed-options li:where(:first-child, :last-child) {
margin: 0em;
}
.speed-options li:hover {
background: var(--grey-color);
}
.speed-options li.active {
color: var(--white_color);
background: var(--orange_color);
}
.container video {
width: 100%;
}
label {
cursor: pointer;
}
.tooltip {
position: relative;
}
.tooltip:hover::before {
content: attr(title);
white-space:nowrap;
position: absolute;
bottom: 150%;
left: 50%;
transform: translateX(-50%);
padding: 0.3125em;
border-radius: 0.3125em;
background-color: #333;
font-weight: 700;
color: var(--white_color);
font-size: 0.875em;
}
.options:first-child .tooltip:first-child::before {
margin-left: 2em;
}
.tooltiplast:hover::before {
left: 22%;
}
.tooltip:nth-child(5)::before {
font-size: 1.1875em;
margin-bottom: 2.8em;
}
@media screen and (max-width: 540px) {
.wrapper .video-controls{
padding: 0.1875em 0.625em 0.4375em;
}
.options input, .progress-area span {
display: none!important;
}
.options button {
height: 1.875em;
width: 1.875em;
font-size: 1.0625em;
}
.options .video-timer {
margin-left: 0.3125em;
}
.video-timer .separator {
font-size: 0.875em;
margin: 0 0.125em;
}
.options button span {
line-height: 1.875em;
}
.options button span {
font-size: 1.3125em;
}
.options .video-timer, .progress-area span, .speed-options li {
font-size: 0.75em;
}
.playback-content .speed-options {
width: 4.6875em;
left: -1.875em;
bottom: 1.875em;
}
.speed-options li {
margin: 0.0625em 0;
padding: 0.1875em 0 0.1875em 0.625em;
}
.right .pic-in-pic {
display: none;
}
}
JavaScript
// Custom Video Player
// Variables
const container = document.querySelector(".container"),
video = document.getElementById('myVideo'),
fileInput = document.getElementById('fileInput'),
mainVideo = container.querySelector("video"),
videoTimeline = container.querySelector(".video-timeline"),
progressBar = container.querySelector(".progress-bar"),
volumeTitle = container.querySelector(".volume"),
volumeBtn = container.querySelector(".volume span"),
volumeSlider = container.querySelector(".left input"),
currentVidTime = container.querySelector(".current-time"),
videoDuration = container.querySelector(".video-duration"),
skipBackward = container.querySelector(".skip-backward span"),
skipForward = container.querySelector(".skip-forward span"),
playPauseTitle = container.querySelector(".play-pause"),
playPauseBtn = container.querySelector(".play-pause span"),
speedBtn = container.querySelector(".playback-speed span"),
speedOptions = container.querySelector(".speed-options"),
pipBtn = container.querySelector(".pic-in-pic span"),
fullScreenTitle = container.querySelector(".fullscreen"),
fullScreenBtn = container.querySelector(".fullscreen span");
let timer;
// New File
fileInput.addEventListener('change', function() {
const file = this.files[0];
const objectURL = URL.createObjectURL(file);
video.src = objectURL;
mainVideo.play();
});
// Display Volume Range
volumeBtn.addEventListener("mousemove", () => {
volumeSlider.style.display = "block";
volumeSlider.style.marginLeft = "3px";
});
// Hide Controls
const hideControls = () => {
if(mainVideo.paused) return;
timer = setTimeout(() => {
container.classList.remove("show-controls");
volumeSlider.style.display = "none";
}, 4000);
}
hideControls();
container.addEventListener("mousemove", () => {
container.classList.add("show-controls");
clearTimeout(timer);
hideControls();
});
// Time Format
const formatTime = time => {
let seconds = Math.floor(time % 60),
minutes = Math.floor(time / 60) % 60,
hours = Math.floor(time / 3600);
seconds = seconds < 10 ? `0${seconds}` : seconds;
minutes = minutes < 10 ? `0${minutes}` : minutes;
hours = hours < 10 ? `0${hours}` : hours;
if(hours == 0) {
return `${minutes}:${seconds}`
}
return `${hours}:${minutes}:${seconds}`;
}
// Video Timeline
videoTimeline.addEventListener("mousemove", e => {
let timelineWidth = videoTimeline.clientWidth;
let offsetX = e.offsetX;
let percent = Math.floor((offsetX / timelineWidth) * mainVideo.duration);
const progressTime = videoTimeline.querySelector("span");
offsetX = offsetX < 20 ? 20 : (offsetX > timelineWidth - 20) ? timelineWidth - 20 : offsetX;
progressTime.style.left = `${offsetX}px`;
progressTime.innerText = formatTime(percent);
});
videoTimeline.addEventListener("click", e => {
let timelineWidth = videoTimeline.clientWidth;
mainVideo.currentTime = (e.offsetX / timelineWidth) * mainVideo.duration;
});
mainVideo.addEventListener("timeupdate", e => {
let {currentTime, duration} = e.target;
let percent = (currentTime / duration) * 100;
progressBar.style.width = `${percent}%`;
currentVidTime.innerText = formatTime(currentTime);
});
mainVideo.addEventListener("loadeddata", () => {
videoDuration.innerText = formatTime(mainVideo.duration);
});
const draggableProgressBar = e => {
let timelineWidth = videoTimeline.clientWidth;
progressBar.style.width = `${e.offsetX}px`;
mainVideo.currentTime = (e.offsetX / timelineWidth) * mainVideo.duration;
currentVidTime.innerText = formatTime(mainVideo.currentTime);
}
// Volume
volumeSlider.addEventListener("input", e => {
mainVideo.volume = e.target.value;
if(e.target.value == 0) {
volumeTitle.title = "Unmute";
return volumeBtn.textContent = 'volume_off';
}
volumeBtn.textContent = 'volume_up';
volumeTitle.title = "Mute";
});
// Mute & Unmute
const tempVolume = volumeSlider.value;
volumeBtn.addEventListener("click", () => {
if(volumeSlider.value == 0) {
mainVideo.volume = tempVolume;
volumeBtn.textContent = 'volume_up';
volumeTitle.title = "Mute";
volumeSlider.value = mainVideo.volume;
}else if(volumeBtn.innerHTML == 'volume_off') {
mainVideo.volume = tempVolume;
volumeBtn.textContent = 'volume_up';
volumeTitle.title = "Mute";
}else{
mainVideo.volume = 0.0;
volumeBtn.textContent = 'volume_off';
volumeTitle.title = "Unmute";
}
});
// Video Speed
speedOptions.querySelectorAll("li").forEach(option => {
option.addEventListener("click", () => {
mainVideo.playbackRate = option.dataset.speed;
speedOptions.querySelector(".active").classList.remove("active");
option.classList.add("active");
});
});
document.addEventListener("click", e => {
if(e.target.tagName !== "SPAN" || e.target.className !== "material-symbols-rounded") {
speedOptions.classList.remove("show");
}
});
// Full Screen
fullScreenBtn.addEventListener("click", () => {
container.classList.toggle("fullscreen");
if(document.fullscreenElement) {
fullScreenBtn.textContent = 'fullscreen';
fullScreenTitle.title = "Full screen";
return document.exitFullscreen();
}
fullScreenBtn.textContent = 'fullscreen_exit';
fullScreenTitle.title = "Exit Full screen";
container.requestFullscreen();
});
// Buttons Functions
speedBtn.addEventListener("click", () => speedOptions.classList.toggle("show"));
pipBtn.addEventListener("click", () => mainVideo.requestPictureInPicture());
skipBackward.addEventListener("click", () => mainVideo.currentTime -= 10);
skipForward.addEventListener("click", () => mainVideo.currentTime += 10);
mainVideo.addEventListener("play", () => { playPauseBtn.textContent = 'pause'; playPauseTitle.title = 'Pause';});
mainVideo.addEventListener("pause", () => { playPauseBtn.textContent = 'play_arrow'; playPauseTitle.title = 'Play';});
playPauseBtn.addEventListener("click", () => mainVideo.paused ? mainVideo.play() : mainVideo.pause());
videoTimeline.addEventListener("mousedown", () => videoTimeline.addEventListener("mousemove", draggableProgressBar));
document.addEventListener("mouseup", () => videoTimeline.removeEventListener("mousemove", draggableProgressBar));
YouTube Video
Download Source Code
Don’t forget to share this post!
Click Here : To Show Your Support! 😍