Fullscreen HTML5 Background Video

A fullscreen background video can add a compelling experience to a website if done the right way.

They have become more common on websites over the last few years, with better support for, and overall improved bandwidth to handle streaming of high resolution HTML5 videos. Despite the limitations for touch devices, they can add impact to desktop browsing. I’ve had the opportunity to produce and implement quite a few, and here’s some tips that keep performance and reliability in mind.

Preparing the video

If we want a performant website, we must consider that the resolution, length and bit rate of a video can determine its file size. The motion of a video can play a major role — footage with more movement tends to require more data. Minimal movement requires less. By having a predetermined limit in mind when exporting, or even producing the video, we can make adjustments to any of the above to keep it in check.

In other words, the goal is to keep the resolution, length and bit rate as low as possible without sacrificing adequate quality and what the video is trying to communicate.

Exporting tips

Resolution and Ratio

Since a fullscreen video is designed to cover the entire background of a browser’s viewport, it makes sense to use a typical desktop resolution such as 1920 x 1080. I tend to go a little lower at 1440 x 810 to keep the file size lower.

While this article is written specifically for fullscreen videos, you would apply the same thinking to any container size that a video needs to fill, and export at a ratio that best fits its container. If there are excess areas of the video hidden, then that is wasted data that could be avoided.

If the video needs to stretch when the viewport is bigger than the video, the quality of the video may perceptively decrease, and you may notice pixelation. However, in my experience the constant motion of a video can disguise low quality to a certain extent, especially if the video is meant to be subdued behind content with more focus.

Frame Rate

For best performance, keep it the same as the source. This is usually between 25 and 30 fps. If it’s a screencast, you can sometimes get away with as low as 5 fps due to low motion.

Bit Rate

The file size of the video is ultimately defined by the bit rate. The average U.S. connection speed is currently about 11.9 Mbit/s. Nonetheless the bit rate should be kept between 1500 and 4000 kilobits per second to play it safe. The video will be required to download and play automatically on load, and we have to factor in other assets the website may have.


Besides increasing the size of the video, it can also be an annoying experience to users when sound is played automatically, so audio needs to be unchecked in export!

Format / Exporting

There are two main formats we need to allow the video to work in most modern browsers. H.264 MP4 and WebM. If you need a greater range of support you can include the Ogg format which covers almost the same ground as WebM, but the latter is generally preferred when available, because it provides a better compression to quality ratio and is supported in more browsers. Here’s a great article on the various codecs and their support.

As far as tools go, I’ve found that Adobe Media Encoder or Handbrake do a great job at exporting H.264 MP4. Using a free online converter such as Zamzar is one of easiest ways to convert an MP4 to WebM.

Implementing the video

Once the video is ready in the required formats, we can write some basic HTML and CSS to implement the video.

HTML Setup

First, we need an outer container which will serve as our fullscreen frame, and the video element itself which will ultimately fill all available space within the container.

<div id="video-container">
    <video preload="auto" width="1440" height="810" autoplay loop muted>
        <source src="video.mp4" type="video/mp4">
        <source src="video.webm" type="video/webm">

  • preload="auto" — Browser will download video automatically.
  • autoplay — Plays the video as soon as it's ready.
  • loop — Loops the video forever.
  • muted — Makes sure the audio is muted.
  • source — Provides the mp4 and webm source.
  • width / height — Dimensions should match the source of the video.

CSS Setup

To make our video fullscreen, the outer container is positioned absolute (or fixed) with 100% width and 100% height. The video element can then be stretched 100% width-wise within the container for the time being.

#video-container {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    overflow: hidden;
video {
    display: block;
    width: 100%;
    height: auto;

This gets us some of the way but now we need to make the video completely fill the outer container regardless of its size or ratio. This is where some Javascript comes in handy.

Video cover effect

In CSS, there’s a trick that can be applied to a background image so it’s always filling the available space of the element it’s a background image of — background-size: cover. We want to try and achieve the same kind of effect on a video element.

Firstly, let’s set up some initial variables for the video container, and the video element itself.

var videoContainer = document.querySelector('#video-container');
var videoElem = document.querySelector('#video-container video');

Then, we need to grab the values from the width and height attributes so we can establish the original scale and aspect ratio. And let’s set up a minimum width for the video while we’re at it.

var vidWOrig;
var vidHOrig;

vidWOrig = videoElem.getAttribute('width');
vidHOrig = videoElem.getAttribute('height');

var minW = 320;

Now for the main function itself.

var videoCover = function() {

    // Find the current width and height of the viewport
    var winWidth = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
    var winHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;

    // Resize the video container to match the viewport
    videoContainer.style.width = winWidth + 'px';
    videoContainer.style.height = winHeight + 'px';

    // Find the largest scale factor of horizontal/vertical
    var scaleH = winWidth / vidWOrig;
    var scaleV = winHeight / vidHOrig;
    var scale = scaleH > scaleV ? scaleH : scaleV;

    // Don't allow scaled width to be less than min width
    if (scale * vidWOrig < minW) {
        scale = minW / vidWOrig;

    // Scale the video
    var videoNewWidth = scale * vidWOrig;
    var videoNewHeight = scale * vidHOrig;
    videoElem.style.width = videoNewWidth + 'px';
    videoElem.style.height = videoNewHeight + 'px';

    // Align to middle by scrolling within the container
    videoContainer.scrollLeft = (videoNewWidth - winWidth) / 2;
    videoContainer.scrollTop = (videoNewHeight - winHeight) / 2;


Watch for resize events

It’s important to watch for resize events with a debounce function. David Walsh’s debounce function does the trick.

var updateVideo = debounce(function() {
}, 100);

window.addEventListener('resize', updateVideo);

Providing a fallback image

It is currently impossible to replicate the same results for background video on touch devices. For example, iOS will attempt to impose a play button icon, mess with z-indexing, and not allow autoplay. This is why it’s safer to provide a static fallback image for touch devices. It’s not perfect, but we can use Modernizr to create a simple check to determine ontouchstart. Modernizr will add the class touch or no-touch to the HTML element.

Therefore, we can define whether to display the video and run the Javascript function that fires the cover effect.

if ( htmlTag.classList.contains('no-touch') ) {

With CSS, simply hide the video element and use a fallback image on the background of the outer container:

.touch #video-container {
	background: url(img/fallback-image.jpg) no-repeat;
	background-size: cover;
.touch video {
	display: none;

See it in action

Below is an example of all the above tied together, including a basic content overlay.

See the Pen ojexyo by Christian Miller (@xtianmiller) on CodePen.

By Christian Miller  |  Follow me on Twitter