Tutorial: How to Build an HTML5 Video Player

UPDATE: This tutorial has been rolled into an open source html5 video player.

This is a tutorial on building an HTML5 video player in Javascript. It’s meant to give you a basic understanding of the different options you have with the new video tag in HTML5, and the javascript needed to create some of the typical video controls you’d find in other players. It’s library agnostic, meaning you don’t need a library like jQuery to create it, however once you understand how everything works it can definitely be simplified/improved on. I’m always open for feedback if you have suggestions.

View the final result.

Setup

Download these movies for testing.
trailer_test.mp4
trailer_test.ogg
(c) copyright Blender Foundation | www.bigbuckbunny.org

To speed things along, copy the source of the following link.

start.html

…and create a file in the same folder as the downloaded movies. The page has the html for the video tag, controls, and some basic javascript variables set up. Hopefully it’s straight forward enough without going into too much more detail. Afterwards you can easily restyle everything to make it look however you want.

HTML5 Video Tag

The <video> tag is basically a new tag introduced in HTML5 that allows you to embed video in a web page without having to use Flash or another embeddable plugin, and instead use a player that’s built into the browser. It’s only supported by advanced browsers like Firefox, Safari, Chrome, and Opera. Not Internet Explorer, though IE9 is supposed to support it. For your video to work in all the advanced browsers, it has to be converted into at least 2 formats: h.264 (Safari and Chrome) and Ogg (Firefox and Opera). VP8, a new codec from On2/Google, may eventually remove the need for multiple versions, but that could take some time.

I’ve already set up the video tag in the example code, and I’m not going to go into the specific attributes of the video tag beyond what’s needed for this tutorial, but if you’re looking for some good resources on using the video tag specifically, start with Kroc Camen’s Video for Everybody.

Ok, getting on to it.

Controller Setup

The controller html has already been created, but it needs to be positioned and revealed. The showController method is simple, but can be expanded on if you want to add effects.

function showController(){
  controls.style.display = "block";
}

The positionController function moves the controller to the bottom left of the video and makes it as wide as the video.

function positionController(){
  controls.style.top = (video.offsetHeight - controls.offsetHeight) + "px";
  controls.style.left = "0px";
  controls.style.width = video.offsetWidth + "px";
  sizeProgressBar();
}

We also want to resize the progress section so the controls fill the width of the video. The numbers being subtracted from the widths are specific to the example design, and will change if you create your own.

function sizeProgressBar(){
  progressControl.style.width = (controls.offsetWidth - 125) + "px";
  progressHolder.style.width = (progressControl.offsetWidth - 80) + "px";
}

Once these functions are created we need to add them to the bodyLoaded function. The show function has to come first for the positioning to work.

var bodyLoaded = function(){
  ...
  showController();
  positionController();
}

Play/Pause

To play and pause the video, we can use the play() and pause() methods of the video element. Create functions that will call these methods.

function playVideo(){
  video.play();
  playControl.className = "pause control";
}

function pauseVideo(){
  video.pause();
  playControl.className = "play control";
}

I’ve also added lines that update the class of the play button to switch it between the play and pause icons.

Next we have to add an event listener for the play button to handle when a user clicks it. We need to add it to bodyLoaded method so it waits for the play button to be available. We’ll use the ‘paused’ attribute to decide which method to call.

playControl.addEventListener("click", function(){
  if (video.paused) {
    playVideo();
  } else {
    pauseVideo();
  }
}, true);

Play Progress

Next we’ll want to show the progress of the video as it plays. To do this we’ll need to set an interval that continually updates the progress bar width. We only want the interval to run when the video is playing however, so we’ll also create a function that stops the interval, that will be called whenever the video is paused.

function trackPlayProgress(){
  playProgressInterval = setInterval(updatePlayProgress, 33);
}

function stopTrackingPlayProgress(){
  clearInterval(playProgressInterval);
}

The updatePlayProgress function uses the video’s current time to total time (duration) ratio, to resize the progress bar based on the progress bar holder’s width. The “- 2″ is to account for the border on the progressHolder.

function updatePlayProgress(){
  playProgressBar.style.width = ((video.currentTime / video.duration) * (progressHolder.offsetWidth - 2)) + "px";
}

Now that we have these functions in place, we need to add them to the play and pause functions to trigger them.

function playVideo(){
  ...
  trackPlayProgress();
}

function pauseVideo(){
  ...
  stopTrackingPlayProgress();
}

We’ll also add the updatePlayProgress method to the sizeProgressBar method, so whenever the progress bar is resized, the current progress is updated as well.

function sizeProgressBar(){
  ...
  updatePlayProgress();
}

Time Display

Next we’ll want to update the time display (to the right of the progress bar) to show the exact time through the video. I’m using the formatTime method to convert seconds into a MM:SS format.

function updateTimeDisplay(){
  currentTimeDisplay.innerHTML = formatTime(video.currentTime);
  if (video.duration) durationDisplay.innerHTML = formatTime(video.duration);
}

function formatTime(seconds) {
  seconds = Math.round(seconds);
  minutes = Math.floor(seconds / 60);
  minutes = (minutes >= 10) ? minutes : "0" + minutes;
  seconds = Math.floor(seconds % 60);
  seconds = (seconds >= 10) ? seconds : "0" + seconds;
  return minutes + ":" + seconds;
}

After we have these functions, we need to add them to updateTimeDisplay to the updatePlayProgress method, so it’s called at the same time.

function updatePlayProgress(){
  ...
  updateTimeDisplay();
}

Progress Bar Scrubbing

Now that we’re showing the progress through the video, we want to make it so you can click and drag on the progress bar to move around in the video’s timeline. For this we’ll need a few new functions.

The setPlayProgress uses a click X value to tell where on the bar the user clicked or dragged. The Math.max and Math.min functions are used to make sure the video time is never set to a point outside of 0-100% of the duration.

function setPlayProgress(clickX) {
  var newPercent = Math.max(0, Math.min(1, (clickX - findPosX(progressHolder)) / progressHolder.offsetWidth));
  video.currentTime = newPercent * video.duration
  playProgressBar.style.width = newPercent * (progressHolder.offsetWidth - 2)  + "px";
  updateTimeDisplay();
}

The blockTextSelection and unblockTextSelection methods are used so when the user is dragging the mouse, it’s not selecting the elements under it.

function blockTextSelection(){
  document.body.focus();
  document.onselectstart = function () { return false; };
}

function unblockTextSelection(){
  document.onselectstart = function () { return true; };
}

The findPosX function is used to determine an object’s x value in relation to the edge of the page. This is required to compare the user’s click to the location of the progress bar.

function findPosX(obj) {
  var curleft = obj.offsetLeft;
  while(obj = obj.offsetParent) {
    curleft += obj.offsetLeft;
  }
  return curleft;
}

Now that we have these functions, we need to add a couple of event listeners (in the bodyLoaded function) to handle when a user clicks and drags on the progress bar. We’ll use the videoWasPlaying variable to keep track of the status of the video, so we can pause the video while the user is scrubbing, and start it again when they stop if it was playing before.

progressHolder.addEventListener("mousedown", function(){
  stopTrackingPlayProgress();

  if (video.paused) {
    videoWasPlaying = false;
  } else {
    videoWasPlaying = true;
    video.pause();
  }

  blockTextSelection();
  document.onmousemove = function(e) {
    setPlayProgress(e.pageX);
  }

  document.onmouseup = function() {
    unblockTextSelection();
    document.onmousemove = null;
    document.onmouseup = null;
    if (videoWasPlaying) {
      video.play();
      trackPlayProgress();
    }
  }
}, true);

progressHolder.addEventListener("mouseup", function(e){
  setPlayProgress(e.pageX);
}, true);

Volume Control

For the volume, we’ll create a function to set the volume and a function to update the volume display. The setVolume function is similar to the setPlayProgress function. It uses the mouse’s x position over the volume control to calculate the new volume. In this case, instead of using the Math.max and Math.min function to limit the volume number, I’m using a simple if/else.

function setVolume(clickX) {
  var newVol = (clickX - findPosX(volumeControl)) / volumeControl.offsetWidth;
  if (newVol > 1) {
    newVol = 1;
  } else if (newVol < 0) {
    newVol = 0;
  }
  video.volume = newVol;
  updateVolumeDisplay();
}

The volume display used here is specific to this example. It uses bottom borders on <li>s to create the look of bars. The function is used to updated the color of each bar.

function updateVolumeDisplay(){
  var volNum = Math.floor(video.volume * 6);
  for(var i=0; i<6; i++) {
    if (i < volNum) {
      volumeDisplay.children[i].style.borderColor = "#fff";
    } else {
      volumeDisplay.children[i].style.borderColor = "#555";
    }
  }
}

Now that we have these, we need to add event listeners to bodyLoaded to handle when a user clicks and drags on the volume control.

volumeControl.addEventListener("mousedown", function(){
  blockTextSelection();
  document.onmousemove = function(e) {
    setVolume(e.pageX);
  }
  document.onmouseup = function() {
    unblockTextSelection();
    document.onmousemove = null;
    document.onmouseup = null;
  }
}, true);

volumeControl.addEventListener("mouseup", function(e){
  setVolume(e.pageX);
}, true);

We’ll also call updateVolumeDisplay directly so the volume display is updated when the page is loaded.

updateVolumeDisplay();

Full Screen (or Full Window) mode

Browsers don’t yet have the capability for us to expand the video to the full screen like you can in Flash video. The best we can get is expanding it to the size of the browser window. You could also potentially resize the window to be larger, but this doesn’t work in all browsers and can be annoying for the user.

The fullScreenOn function changes the video to a ‘fixed’ positioning, puts it at 0,0 in the browser window, and stretches it to the width of the window. It then repositions the controller at the bottom. I’m also adding a “fs-active” class to the button to change the look of the fullscreen icon.

function fullScreenOn(){
  videoIsFullScreen = true;
  videoOrigWidth = video.offsetWidth;
  videoOrigHeight = video.offsetHeight;

  video.style.width = window.innerWidth + "px";
  video.style.height = window.innerHeight + "px";
  video.style.position = "fixed";
  video.style.left = 0;
  video.style.top = 0;
  controls.style.position = "fixed";
  positionController();

  fullScreenControl.className = "fs-active control";
}

The fullScreenOff function resets the video to the original position, width, and height.

function fullScreenOff(){
  videoIsFullScreen = false;
  video.style.width = videoOrigWidth + "px";
  video.style.height = videoOrigHeight + "px";
  video.style.position = "static";
  controls.style.position = "absolute";
  positionController();
  fullScreenControl.className = "control";
}

Next we need to add an even listener to handle clicks on the full screen button.

fullScreenControl.addEventListener("click", function(){
  if (!videoIsFullScreen) {
    fullScreenOn();
  } else {
    fullScreenOff();
  }
}, true);

Conclusion

If you’ve found this useful I’d appreciate a link or a tweet. And if you can expand on or improve this I would love to hear about it. Thanks!

Final code version.

14 Responses to “Tutorial: How to Build an HTML5 Video Player”

  1. Watch Hulu and Sling in Costa Rica (and Elsewhere) | Ethel The Frog said:

    Apr 15, 10 at 10:10 pm

    [...] How to Build an HTML5 Video Player | Steve Heffernan’s Blog [...]

  2. zcorpan said:

    Apr 16, 10 at 12:31 am

    Hmm, I don’t see the controls in Opera. Any idea why?

  3. Jason said:

    Apr 16, 10 at 4:36 pm

    Thanks for the article. It’s amazing how in its simplicity (if you know javascript), its so easy to use and understand. Thanks for breaking it down.

  4. JeanPhi said:

    Apr 18, 10 at 6:59 am

    Hi Steve,

    Kroc Carmen sent me here to understand how I could customize the Video For Everybody code player.

    At first, I was just willing to change the progress bar color (as my website colors will be white, black and red I thought that a red progress bar would be better than a blue one on Chrome).

    But now that I’ve been reading this article, I do think that’s preferable to have no player showing at all.

    My only concern now is how to autoplay the video when someone lands on my video page? I changed the value to ‘true’ but it doesn’t start automatically.

    Am I missing something there do you think?

    And, by the way, if you could just explain me how to have this porgress bar in red, that would be awesome!

    Many thanks and congrats for the good work,

    JeanPhi

  5. Steve said:

    Apr 20, 10 at 1:03 pm

    @ zcorpan – Honestly I was just assuming the video tag worked in Opera, though now I can’t find any examples around the web where Opera does support it. I am using the Mac version. Maybe you need the PC version, or a nightly build?

    @ Jason – Thanks Jason, I’m glad you found it easy to understand. I know there’s some open source players out there, but they’re definitely complicated.

    @ JeanPhi – As far as the progress bar color, all colors are controlled by the CSS. If you change the background color for “#progress_control #progress_bar”, it will change the progress bar from white to whatever you want. Same for the controls background color.

    As far as autoplay, if you have the autoplay attribute in the video tag, the video should start playing by itself. If it doesn’t, then I couldn’t tell you exactly what’s happening. Autoplay is part of the functionality that should be built into the browser, and isn’t controlled by the javascript. However, once you get autoplay working, you would need to have the progress bar start tracking without the user pressing the play button. To do this you’ll need to add an event listener to the “play” event on the video. I could probably build this into the tutorial, but a quick fix would look like this:

    video.addEventListener(“play”, function(){
    trackPlayProgress();
    playControl.className = “pause”;
    })

    You would add that to the bodyLoaded function. Hope that helps.

  6. Clint said:

    May 13, 10 at 9:50 pm

    I would love to see and example of a horizontal volume bar slider. How to do it?

  7. Hector Jaime said:

    May 15, 10 at 8:50 pm

    Hi,

    Great script! I’d like to add it to a site I’m working on, but the only problem is that the tag is locked down and not editable. Is there a way to add the onLoad command somewhere else? I barely know javascript, so sorry if this seems like a dumb question.

    Thanks,
    Hector

  8. SilentViper said:

    May 16, 10 at 12:55 pm

    this is great! i love this thanks for the Tutorial, but i would like to make one request: is it possible for this to be updated to work properly on iPad ( fullscreen controls disappear) or disable it altogether for iPad

    thanks again

  9. Ribrob said:

    May 18, 10 at 1:25 am

    Hi there, this is lovely!

    Couple of quick questions…

    1. Is there anyway of hiding (or preventing them from appearing) the player controls when the player doesn’t support HTML5 Video?

    2. Is it possible to hide the player controls after a number of seconds unless the mouse pointer is still over the video?

    r.

  10. Alex said:

    May 19, 10 at 12:24 pm

    I’m testing out your player for my site which is still in development. I wanted to ask if there is a way to hide the controller so it only shows up when you roll-over the video.

    Also for some reason everything looks fine on my mac but when i test it on Firebox 3.0.15, Netscape 9.0b2 in WindowsXP. It just shows the Quicktime default player with a big black box set to the dimension I set in the video_box CSS. The control graphics are at the top of that box. In IE the box is white. Any reason for this problem happening in XP?

  11. Accessibility Within Photography Multimedia said:

    May 19, 10 at 12:28 pm

    [...] I have tested and observed various UI skins that is easy compiled using CSS to overlay the current player that web browsers are using such as OIPlayer and various others. [...]

  12. Steve said:

    May 19, 10 at 3:09 pm

    To everyone, I’ve rolled this tutorial into an open source project. http://videojs.com

    @Clint – If you can find a tutorial on creating a slider bar, it shouldn’t be difficult to figure out how to swap it in for the current volume control.

    @Hector – you could move the contents of the onLoad function to a script tag that comes after the video tag on the page. Does that help?

    @SilentViper – I don’t have an iPad, but I’m assuming it works the same as the iPhone, in that the built in video player handles the video off the page. Does that sound right?

    @Ribrob – I’m going to build in hiding in other browsers into videojs.com. And hiding when hovering. That shouldn’t be too difficult. Feel free to beat me to it. :)

    @Alex – If Quicktime comes up, then that means the browser can’t find the right source to play. Not sure what’s going on in IE, but IE doesn’t support HTML5. Check videojs.com for a version that falls back to flash.

  13. Ethan said:

    Jun 03, 10 at 8:10 pm

    This is a great tutorial! Thanks!

  14. zcorpan said:

    Jun 14, 10 at 4:27 am

    “@ zcorpan – Honestly I was just assuming the video tag worked in Opera, though now I can’t find any examples around the web where Opera does support it. I am using the Mac version. Maybe you need the PC version, or a nightly build?”

    Opera does support video (from version 10.5x), and I can play it (on Mac) if I click ‘play’ from the context menu. But the controls don’t appear — maybe it’s some problem with your script?


Leave a Reply