custom video player banner

Build a Simple Video Player with HTML, CSS, and JavaScript

Ever wanted to add dynamic video content to your website, but not thrilled with the look and feel of off-the-shelf video players? Well, get ready to roll up your sleeves and build your own custom video player.

This tutorial will walk you through building a basic video player using web technologies. You'll gain control over the video experience on your website and learn how to implement standard playback controls.

Why build a custom player?

Here are a few good reasons:

  • Boost user engagement: Enhance your web pages with dynamic video content, keeping users hooked and informed.
  • Customization freedom: Craft a player that seamlessly blends with your website's design and brand identity, fostering a cohesive user experience.
  • Interactive control: Implement essential features like play/pause, volume, and even fullscreen mode, allowing users to navigate the video playback at their own pace.

What is a video player?

A video player is a software application that allows users to play and control video files on their computers or within web browsers. It typically includes core functionalities like play/pause, volume adjustment, and a progress bar, and may offer additional features depending on its complexity.

What will you learn?

By following this tutorial, you'll gain the ability to:

  • Construct a functional video player using fundamental HTML elements.
  • Customize the player's visual appearance and user interface with CSS.
  • Implement basic features like play/pause control using JavaScript, adding interactivity to your player.

Here's a sneak peak of how our video player will look.

custom video player

Prerequisites:

This tutorial assumes basic knowledge of HTML, CSS, and JavaScript.

Let's get started!

Step 1: Building the HTML Structure of our video player

In this step, we'll create an HTML page and setup the essential structure of our video player using HTML elements:

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>Document</title>
    <link rel="stylesheet" href="https://use.fontawesome.com/releases/v6.1.1/css/all.css">
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <div class="video-container">
        <video id="video" src="video.mp4"></video>
        <div class="player-progress">
            <input id="seek" type="range" min="0" max="100" step="0.1" value="0">
            <span id="time">00:00 / 00:00</span>
        </div>
        <div class="controls">

            <button id="play-pause" class="play">Play</button>

            <div class="volume-controls">
                <i class="fas fa-volume-up"></i>
                <input id="volume" type="range" min="0" max="1" step="0.1" value="0.6">
                <i class="fas fa-volume-mute" id="mute"></i>
                <!-- button id="mute" class="unmute">Mute</button -->
            </div>

            <div class="speed-controls">
                 <i class="fas fa-running"></i>
                <input id="speed" type="range" min="0.5" max="2" step="0.1" value="1">
                <span id="speed-label">1x</span>
            </div>

            <i class="fas fa-expand" id="fullscreen"></i>
        </div>
    </div>

    <script src="script.js"></script>

</body>
</html>
  1. Create a div with the class "video-container": This container will house the entire video player, including the video itself and the controls.
  2. Embed the video element: Inside the container, add a <video> element with the ID "video" to display the video content. Set the src attribute to the path of your video file (e.g., "video.mp4").
  3. Construct the progress bar: Create a div with the class "player-progress" to hold the progress bar. Within this div, insert:
    • An input element with the ID "seek" and type "range" to serve as the visual progress bar.
    • A span element with the ID "time" to display the current playback time and total duration.
  4. Add control elements: Create a div with the class "controls" to house various player controls:
    • A button with the ID "play-pause" and class "play" to toggle play/pause functionality.
    • A "volume-controls" div containing a volume icon, a range input for volume adjustment, and a mute icon.
    • A "speed-controls" div with a speed icon, a range input for playback speed control, and a label displaying the current speed.
    • An icon for fullscreen mode with the ID "fullscreen".

Note: We've included the Font Awesome library in our project's header. This provides the icons used in our player controls.

This HTML structure establishes the core visual elements of our video player, preparing the groundwork for styling and interactivity.

Step 2: Styling the Player with CSS

Create a style.css file and add the following code:

CSS
                        
.video-container {
  max-width: 600px;
  margin: 0 auto;
}

video {
  width: 100%;
  height: auto;
}

.player-progress {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 10px;
  background-color: rgba(0, 0, 0, 0.5);
}

#seek {
  width: 80%;
}

.controls {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 10px;
  background-color: rgba(0, 0, 0, 0.5);
}

button {
  border: none;
  outline: none;
  cursor: pointer;
  background-color: orange;
  border-radius: 5px;
  padding: 8px 16px;
  color: white;
  font-size: 16px;
  min-width: 90px;
}

input[type="range"] {
  -webkit-appearance: none;
  width: 100px;
  height: 4px;
  background-color: white;
  outline: none;
}

input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background-color: rgb(250, 147, 255);
  cursor: pointer;
}

span {
  color: white;
  font-size: 14px;
  margin: 0 10px;
}

.play {
  background-color: hsl(124, 59%, 54%);
}

i {
  margin: auto 8px;
  color: white;
}

#mute {
  color: lightgray;
}

The CSS code defines styles for various elements:

  • .video-container: Sets a maximum width for responsive behavior and centers it horizontally.
  • video: Ensures the video fills the container's width while maintaining its aspect ratio.
  • .player-progress, .controls: Applies flexbox properties for horizontal layout, alignment, and background color for a subtle visual separation.
  • #seek: Adjusts the width of the progress bar.
  • button: Defines button appearance with consistent styling for all buttons.
  • input[type="range"]: Customizes the appearance of range inputs (progress bar & volume slider).
  • span: Sets the color and font size for text elements.
  • .play: Uses a different background color for the play button.
  • i: Styles the icon elements for consistent spacing and color.
  • #mute: Adjusts the color of the mute icon to indicate its inactive state.

These styles provide a basic visual foundation for the player, enhancing the user experience and aesthetics.

Adding functionality with JavaScript

Having established the visual structure and styles, we now tackle the magic: adding interactivity and functionality using JavaScript.

  • Event Listeners: We attach event listeners to the player's control elements (buttons, range inputs) and the video element itself. These listeners will detect user clicks and changes, triggering the appropriate functions.
  • Event-Driven Actions: When a user interacts with an element (e.g., clicks the play/pause button), a specific function will run.

The following functions will drive our video player:

togglePlayPause()

JavaScript
                        
function togglePlayPause() {
  if (video.paused) {
    video.play();
    playPause.textContent = "Pause";
    playPause.className = "pause";
  } else {
    video.pause();
    playPause.textContent = "Play";
    playPause.className = "play";
  }
}

The togglePlayPause function plays a central role in controlling video playback. It first checks if the video is currently paused. If so, it initiates playback, updates the button text to "Pause," and modifies the button's class to reflect the paused state. Conversely, if the video is playing, the function pauses it, updates the button text to "Play," and adjusts the class again to represent the playing state. This function essentially acts as a switch, toggling the video's playback state and keeping the play/pause button visually synchronized with the current playback status.

changeVolume()

JavaScript
                        
function changeVolume() {
  video.volume = volume.value;
  if (volume.value == 0) {
    mute.textContent = "Unmute";
    mute.className = "mute";
  } else {
    mute.textContent = "Mute";
    mute.className = "unmute";
  }
}

The changeVolume function adjusts the video's volume and updates the mute button's state based on the user's input:

  1. It sets the video's volume level directly based on the current value of the volume slider (volume.value).
  2. It then checks the volume value:
    • If the volume is set to 0 (muted), it updates the mute button text to "Unmute" and assigns the class "mute" to style the button visually.
    • If the volume is greater than 0 (not muted), it updates the mute button text to "Mute" and assigns the class "unmute" for appropriate styling.

This function effectively controls the volume based on user interaction, and synchronizes the mute button's behavior and appearance based on the current volume setting.

toggleMute()

JavaScript
                        
function toggleMute() {
  if (video.muted) {
    video.muted = false;
    volume.value = video.volume;
    mute.textContent = "Mute";
    mute.className = "unmute";
  } else {
    video.muted = true;
    volume.value = 0;
    mute.textContent = "Unmute";
    mute.className = "mute";
  }
}

The toggleMute function mutes/unmutes the video and updates the volume slider & mute button accordingly. It checks the current mute state:

  • Muted: Unmutes, sets volume, updates button text ("Mute"), and assigns "unmute" class.
  • Not muted: Mutes, sets volume to 0, updates button text ("Unmute"), and assigns "mute" class.

This keeps everything in sync and provides clear visual feedback.

changeSpeed()

JavaScript
                        
function changeSpeed() {
  video.playbackRate = speed.value;
  speedLabel.textContent = speed.value + "x";
}

This function updates the video's playback speed based on the value of the speed slider ( video.playbackRate = speed.value).

It also changes the speed label to reflect the current speed ( speedLabel.textContent = speed.value + "x").

changeSeek()

JavaScript
                        
function changeSeek() {
  video.currentTime = seek.value * video.duration / 100;
}

Adjusts the current playback position in the video based on the value of the seek slider ( video.currentTime = seek.value * video.duration / 100).

updateSeek()

JavaScript
                        
function updateSeek() {
  seek.value = (video.currentTime / video.duration) * 100;
  time.textContent = formatTime(video.currentTime) + " / " + formatTime(video.duration);
  if (seek.value === seek.max) {
    playPause.textContent = "Play";
    playPause.className = "play";
  }
}

The updateSeek function Synchronizes the seek slider's position with the current playback time ( seek.value = (video.currentTime / video.duration) * 100).

It also updates the time label to display both the current time and total duration ( time.textContent = formatTime(video.currentTime) + " / " + formatTime(video.duration)).

If the video has reached the end ( seek.value === seek.max), it resets the play/pause button to its initial "Play" state ( playPause.textContent = "Play"; playPause.className = "play").

updateDuration()

JavaScript
                        
function updateDuration() {
  time.textContent = formatTime(video.currentTime) + " / " + formatTime(video.duration);
}

This function updates the time label to display both the current time and total video duration ( time.textContent = formatTime(video.currentTime) + " / " + formatTime(video.duration)).

toggleFullscreen()

JavaScript
                        
function toggleFullscreen() {
  if (document.fullscreenElement) {
    document.exitFullscreen();
    fullscreen.className = "fas fa-expand";
    fullscreen.classList.remove("fas fa-compress");
  } else {
    video.requestFullscreen();
    fullscreen.className = "fas fa-compress";
    fullscreen.classList.remove("fas fa-expand");
  }
}

The toggleFullscreen function switches the video between fullscreen and normal mode. It checks the current state:

  • Fullscreen: Exits fullscreen and updates the button icon to "expand".
  • Not fullscreen: Requests fullscreen and updates the button icon to "compress".

formatTime()

JavaScript
                        
function formatTime(secs) {
  let minutes = Math.floor(secs / 60);
  let seconds = Math.floor(secs % 60);
  if (minutes < 10) {
    minutes = "0" + minutes;
  }
  if (seconds < 10) {
    seconds = "0" + seconds;
  }
  return minutes + ":" + seconds;
}

The formatTime function takes seconds (either current time or total duration) and converts them to a user-friendly format with leading zeros for single-digit minutes and seconds (e.g., "02:35"). It helps display time information clearly in the player's time label.

Now let's put it all together

Our combined JavaScript code will look like this:

JavaScript
                        
// Get the elements from the HTML
const video = document.getElementById("video");
const playPause = document.getElementById("play-pause");
const volume = document.getElementById("volume");
const mute = document.getElementById("mute");
const speed = document.getElementById("speed");
const speedLabel = document.getElementById("speed-label");
const seek = document.getElementById("seek");
const time = document.getElementById("time");
const fullscreen = document.getElementById("fullscreen");

// Add event listeners to the elements
playPause.addEventListener("click", togglePlayPause);
volume.addEventListener("input", changeVolume);
mute.addEventListener("click", toggleMute);
speed.addEventListener("input", changeSpeed);
seek.addEventListener("input", changeSeek);
video.addEventListener("timeupdate", updateSeek);
video.addEventListener("durationchange", updateDuration);
fullscreen.addEventListener("click", toggleFullscreen);

// Define the functions for the event listeners
function togglePlayPause() {
  if (video.paused) {
    video.play();
    playPause.textContent = "Pause";
    playPause.className = "pause";
  } else {
    video.pause();
    playPause.textContent = "Play";
    playPause.className = "play";
  }
}

function changeVolume() {
  video.volume = volume.value;
  if (volume.value == 0) {
    mute.textContent = "Unmute";
    mute.className = "mute";
  } else {
    mute.textContent = "Mute";
    mute.className = "unmute";
  }
}

function toggleMute() {
  if (video.muted) {
    video.muted = false;
    volume.value = video.volume;
    mute.textContent = "Mute";
    mute.className = "unmute";
  } else {
    video.muted = true;
    volume.value = 0;
    mute.textContent = "Unmute";
    mute.className = "mute";
  }
}

function changeSpeed() {
  video.playbackRate = speed.value;
  speedLabel.textContent = speed.value + "x";
}

function changeSeek() {
  video.currentTime = seek.value * video.duration / 100;
}

function updateSeek() {
  seek.value = (video.currentTime / video.duration) * 100;
  time.textContent = formatTime(video.currentTime) + " / " + formatTime(video.duration);
  if (seek.value === seek.max) {
    playPause.textContent = "Play";
    playPause.className = "play";
  }
}

function updateDuration() {
  time.textContent = formatTime(video.currentTime) + " / " + formatTime(video.duration);
}

function toggleFullscreen() {
  if (document.fullscreenElement) {
    document.exitFullscreen();
    fullscreen.className = "fas fa-expand";
    fullscreen.classList.remove("fas fa-compress");
  } else {
    video.requestFullscreen();
    fullscreen.className = "fas fa-compress";
    fullscreen.classList.remove("fas fa-expand");
  }
}

// Define a helper function to format the time in mm:ss format
function formatTime(secs) {
  let minutes = Math.floor(secs / 60);
  let seconds = Math.floor(secs % 60);
  if (minutes < 10) {
    minutes = "0" + minutes;
  }
  if (seconds < 10) {
    seconds = "0" + seconds;
  }
  return minutes + ":" + seconds;
}

Conclusion

Congratulations! By following these steps, you've successfully built a basic video player using simple HTML, CSS, and JavaScript. Now you can:

  • Embed videos: Easily integrate video content into your website using HTML.
  • Style the player: Customize the look and feel of your player with CSS.
  • Add core controls: Implement essential playback controls like play/pause, volume, and progress bar using JavaScript.