Skip to Content
Course content

225: Building a Custom Video Player Interface

Click on the "Edit" button in the top corner of the screen to edit your slide content.

When you first start building a custom video player, the instinct is usually to treat it like a standard web form. You build the buttons, you link them to the video element, and you assume the UI will just "know" when the video is moving. I see this mistake constantly: a developer builds a beautiful progress bar, but when they hit play, the bar just sits there at 0% while the video rolls on.

// The "It should just work" approach (Broken)
const video = document.querySelector('video');
const progressBar = document.querySelector('.progress-bar');

function updateProgress() {
  const percentage = (video.currentTime / video.duration) * 100;
  progressBar.style.width = percentage + '%';
}

// I'll just call this once when the page loads... 
// or maybe in a setInterval?
updateProgress(); 

The Frozen Progress Bar

If you run the code above, you'll notice the progress bar never moves. Why? Because updateProgress is only called once. Even if you tried to wrap it in a setInterval, you'd be fighting the browser's internal clock, leading to stuttering movement or unnecessary CPU usage. The video element is its own state machine; it knows exactly where it is in the timeline, but it doesn't automatically push those updates to your custom HTML elements.

Syncing State with the timeupdate Event

To fix this, we need to stop trying to "guess" when the video has moved and instead listen to the video element itself. The HTML5 Video API provides a specific event called timeupdate. This event fires continuously as the playback position changes.

const video = document.querySelector('video');
const progressBar = document.querySelector('.progress-bar');

// Instead of a timer, we listen for the video's own heart-beat
video.addEventListener('timeupdate', () => {
  const percentage = (video.currentTime / video.duration) * 100;
  progressBar.style.width = `${percentage}%`;
});

Now the UI is reactive. The moment currentTime increments, the browser triggers the event, and your bar moves. I usually recommend using template literals (the backticks) for the width string—it's cleaner and prevents those annoying string concatenation bugs.

Handling the "Seek" Interaction

Moving the bar visually is only half the battle. You also want the user to be able to click the bar to jump to a specific part of the video. This is where a lot of people get tripped up by "infinite loops." If you update the video time when the slider moves, and the slider moves because the video time updated, you can end up with a feedback loop that makes the video jump erratically.

The trick is to separate the visual update (driven by the video) from the user input (driven by the mouse). If you're using an <input type="range">, you should listen for the input event to update the video's currentTime.

const seekSlider = document.querySelector('#seek-slider');

seekSlider.addEventListener('input', () => {
  // Calculate the new time based on the slider's current value
  const time = (seekSlider.value / 100) * video.duration;
  video.currentTime = time;
});

One last pro tip: never try to access video.duration the millisecond the page loads. The video metadata (like how long the file is) often loads asynchronously. If you try to calculate a percentage before the metadata is ready, you'll get NaN because you're dividing by NaN. Always wrap your initial setup in a loadedmetadata event listener if you're doing calculations on startup.




📋 Practical Task

Build a Synchronized Volume and Mute Controller

Using the concepts of event listeners and the Video API, extend a custom video player by adding a volume control system. Your task is to implement the following functionality:

  • Create a volume slider (an <input type="range">) that adjusts the video.volume property in real-time.
  • Create a "Mute/Unmute" button that toggles the video.muted boolean.
  • The Challenge: Ensure that when the user adjusts the volume slider, the video is automatically unmuted. Conversely, if the user clicks the "Mute" button, the volume slider's visual position should update to reflect that the volume is effectively zero, without actually changing the stored volume level (so when they unmute, it returns to the previous volume).
Rating
0 0

There are no comments for now.

to be the first to leave a comment.