Building an HTML5 Video Player

Article Featured Image
Article Featured Image

This article appears in the February/March 2012 issue of Streaming Media magazine, the annual Streaming Media Industry Sourcebook.

When I was 6 years old, I had metal-capped front teeth, a lazy eye, an eye patch to correct my lazy eye, thick glasses, plastic tubes in my ears to drain fluids, and a speech impediment. HTML5 video is kind of like me at 6 years old. It has the potential to some day be a fully functioning HTML tag. But right now, it has some issues. In order to work with HTML5, we need to know what its issues are and how to patch them. That said, HTML5 video will some day be the default method developers use for playing video on the web. Apple is holding strong on its decision not to support Flash on iOS, Adobe is cutting development on Flash for other mobile browsers, and more users support HTML5 video every day as Chrome takes over the browser market. That makes this a great time to get a head start on building video experiences with HTML5 video.

So what is an HTML5 video player? Technically, it is the player that’s already built into HTML5 browsers. It allows us to simply use the video tag to embed a video. However, not every user supports HTML5 video yet, and even within HTML5 video, there are bugs and cross-browser inconsistencies we need to handle. So using HTML5 is not quite that easy yet. What we need is an additional JavaScript library that manages the selection of either Flash or HTML5 and fixes those bugs and inconsistencies. This is why you’ll see many JavaScript libraries (including Video.js) referred to as HTML5 Video Players. Additionally, these libraries will often provide a common “skin” so that video controls look the same between browsers, which isn’t the case when just using the video tag.

The Code

When building your own HTML5 video player, your embed code will start with a simple video tag that contains a video source and the controls attribute to make the controls visible. The “src” attribute should have a URL pointing to a video that you’ve already uploaded to the web:

<video src=“video.mp4” controls></video>

It’s pretty simple. That’s already enough to get your video playing in many browsers, depending on the video format you’re using. The next step then is to add the other formats we need to support all HTML5 browsers. The three video formats primarily used in HTML5 video are MP4, WebM, and Ogg. To add them, simply drop the “src” attribute from the video tag and add source tags inside the video tag:

<video controls>
     <source src=“video.mp4” type=“video/mp4”> 
     <source src=“video.webm” type=“video/webm”> 
     <source src=“video.ogv” type=“video/ogg”>

The type attribute of the source tag tells the browser what format the file is; it helps the browser decide which file to play. If you don’t include this, the browser will have to download a portion of each file to test whether or not it can support the format. This can slow down the load process.

Another tip is to always put the MP4 file as the first source. Older versions of iOS would only look at the first source tag, and if it couldn’t play that source, it would just stop instead of looking through the rest of the sources for one it could play.

Finally, for your users who have older browsers that don’t support HTML5 video, you can add a Flash player as a fallback. If you have a Flash player now, it’s pretty easy. You just put the exact same embed code that you use for your Flash player inside the video tag after the source tags. If the browser doesn’t support HTML5 video, it will fall back to that:

<video controls>
     <source src=“video.mp4” type=“video/mp4”>
     <source src=“video.webm” type=“video/webm”>
     <source src=“video.ogv” type=“video/ogg”>
     <object type=“application/x-shockwave-flash” data=“flash.swf”>
     <param name=“movie” value=“flash.swf” />
     <param name=“flashvars” value=“file= video.mp4” />

Using HTML’s built-in fallback mechanism like this is the easiest way to start supporting HTML5 video. If you’d prefer to lead with your Flash player and only use HTML5 when Flash isn’t supported, you can use JavaScript and SWFObject ( to detect for Flash support and write out either the Flash embed code or the HTML5 video tag as desired.

There’s one more step that’s not required but is in practice with some HTML5 video players today. You can add download links and a fallback image:

<video controls>
     <source src=“video.mp4” type=“video/mp4”>
     <source src=“video.webm” type=“video/webm”>
     <source src=“video.ogv” type=“video/ogg”>
     <object type=“application/x-shockwave -flash” data=“flash.swf”>
     <param name=“movie” value=“flash.swf” />
     <param name=“flashvars” value=“file= video.mp4” />
     <img src=“image.jpg” alt=“Video Title” title=“Can’t play video” />
     <strong>Download Video:</strong> 
     <a href=“video.mp4”>MP4</a>,
     <a href=“video.webm”>WebM</a>, 
     <a href=“video.ogv”>Ogg</a>

This supports those users who can’t play video in either Flash or HTML5 but who might be able to download the video and play it outside of the browser. This includes older handheld devices or older Internet Explorer versions with no Flash plug-in. Flash players don’t do this today, so it’s not totally necessary; also, if your goal is to protect your videos from download, you obviously won’t want to do this.

Whether you use the last step or not, this embed code will get your video playing for just about all your users.

Choosing Video Formats

There are a few strategies to help you choose which video formats to provide. The first option is to create all three HTML5 video formats: MP4, WebM, and Ogg. This allows you to use the HTML5 video version of your player with the highest number of your users. This is important if you are doing something particularly interesting around HTML5 video and want as many users as possible to see it. This is also the only option when using HTML5’s built-in fallback, such as in the previous embed code. Browsers that do support HTML5 video don’t fall back to the Flash player just because there isn’t a source they support. The downside of this is obviously the complexity and cost of encoding every video into three formats. The next two options allow you to encode fewer formats, but they require you to use JavaScript to detect if there’s a format the browser supports in HTML5 video and to dynamically write out either the HTML5 or Flash embed code.

How-to TableThis next strategy provides two formats: MP4 and WebM. These formats are the higher-quality formats of the three. Ogg is a decent open source format, was the first format ever used in HTML5 video, and is actually supported by more users than the other two because it’s been in browsers for a longer period of time. However, WebM is a higher-quality option for the browsers that only support open formats, and over the past year, WebM has gained enough ground on Ogg to place it first (see the figure below). According to StatCounter’s browser version statistics,, Ogg is supported by about 54% of users, while WebM is supported by about 49%. MP4 is supported by about 44% of HTML5 users, but it’s also supported by Flash, which brings us to the last option.

Since MP4 is supported by both iOS devices and Flash, we can provide one format and still support just about all of our users. Users with browsers that don’t support MP4 in HTML5 will simply be delivered a Flash player. For those of you planning to lead with Flash anyway, this strategy is the obvious choice. You can avoid the complexity of multiple formats, but you should also be aware of any royalties you may owe MPEG LA when using the MP4 format in certain cases.


Now that we have the basics set up, we can dig into some of the issues I mentioned at the beginning of the article. These issues are byproducts of HTML5 video being a young technology, and many of these are quickly becoming irrelevant as users upgrade to new browser versions.

Autobuffer or Preload

Originally, the HTML5 video spec told browser developers to include an attribute called “autobuffer” in the video tag. Autobuffer allowed us to tell the browser to start downloading the video as soon as the webpage loaded, as opposed to waiting until the user clicked play. A little later on, after browsers had already implemented this attribute, the spec was changed; the attribute was then called “preload” with the options “none,” “metadata,” and “auto.” This means that some older browser versions (e.g., Firefox 3.5) look for the autobuffer attribute, while newer browser versions look for preload. The simple solution around this, if this is an important feature for you, is to set autobuffer and preload.

Progress or Buffered

Another result of the changing spec is that Firefox versions 3.5 and 3.6 don’t support the “buffered” attribute. This is especially important if you’re going to build custom controls with a loading bar. These Firefox versions would instead fire a “progress” event as the video loaded. The event object includes information about how much has been loaded. To get around this, you can check for the buffered attribute and watch for progress events.

We may still see the spec change when it comes to more advanced features of HTML5 video. But for the most part, it has been locked in and will stay consistent. The browser versions mentioned are very old now, and while they are still somewhat in use, I think we’ll be able to ignore these issues within the year.

iOS 3 Issues

iOS 3 is the version of Apple’s iDevice software that shipped with the first iPad, the iPhone 3, the 3GS, and iPod touches from the same time. Despite the option to upgrade to iOS 4 and 5, some users still have iOS 3.

The first issue is with the poster attribute. If you include a poster attribute, it can cause video on an iOS 3 device to break. If you want to support iOS3 users who have to deal with this, you can simply remove the poster attribute. That’s not really ideal, but it’s an easy way around it. In this case, you could splice in the poster frame as the first frame of the video. When preloading a video, the first frame is shown to the user until the video is played. You could also use JavaScript to remove the preload attribute at page load and dynamically create an image tag with the poster source that is positioned over the video. Both require a little extra work, but that’s how you can get around this if you need the poster to work.

Another interesting issue has to do with including JavaScript on the page. If you include JavaScript in the header of a page, like many of us do with jQuery or other libraries, it can cause video on the iPad to break. If you move that same JavaScript to the end of your file, right before the body tag, the iPad will work, but it can cause video on the iPhone to break. It’s a strange bug that seems to occur because the software can’t handle loading both the JavaScript and the video at the same time. The best way I’ve found to get around this issue is to reset the video tag’s source at page load using JavaScript:

myVideoElement.src = “video.mp4”

Both of these issues were thankfully fixed in iOS 4; let’s hope iOS 3 issues disappear this year as well.

Android Issues

There are quite a few issues with HTML5 video on the Android platform.

First, in Android version 2.2 (which is still very much in use), you can’t even play the video. The only way to get the video to play is to trigger it with JavaScript. Users can’t start it themselves. You can help this by turning the whole video into a play button. Using jQuery, it would look like this:


Second, there is a bug with the type attribute. If you include a type attribute on your source tags (which we did in our embed code), it can break playback on Android devices. As I mentioned earlier, including the type attribute is good for the browsers, but if your Android users complain, you can remove the type attribute and the video will still work. Or you can use the method of resetting the source that fixes the JavaScript issue for iOS as mentioned previously.

Finally, the canPlayType function of the video element doesn’t work. It should return a “maybe” message when you pass in the string of “video/mp4,” but instead, it always returns a blank string, which means that it can’t play the format, even when it can. For Video.js, I created the following snippet to override Android’s canPlayType function with one that can:

var androidVersion = navigator.userAgent
if (androidVersion !== null && androidVer sion[1] < 3) {
     document.createElement(“video”) .constructor.prototype.canPlayType
          = function(type){
     return (type && type.toLowerCase() 
          .indexOf(“video/mp4”) != -1) ? 
          “maybe” : ““;

So there’s definitely a lot of room for improvement with Android. There are later versions of Android available now, but devices are still being created using these older versions.

Next Steps

It’s clear that HTML5 video isn’t quite as easy as promised yet, but I hope that doesn’t discourage you from working with it. If you’ve followed along, you should now have a reliable player that supports more users than Flash or HTML5 alone. You can take your player much further, but I hope this gives you a good base. From here, you might want to build custom controls using the video element API. If that’s the case, there are a number of open source HTML5 video players that you can look at to get a head start. And if you have further questions, there’s an active community in the Video.js forums where you can get your questions answered quickly. Cheers!

To view Steve Heffernan's HTML5 Video Summit presentation on building an HTML5 video player, watch the video below:

Streaming Covers
for qualified subscribers
Subscribe Now Current Issue Past Issues