Source Code Of Custom Video Player

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

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!