HTML5 Fullscreen Background Video with CSS and JS (Plugin/Library)

Fullscreen background videos that autoplay right when the webpage loads (above the fold) has become quite a popular trend these days. Personally I think a fullscreen good quality video that autoplays does increases engagement for the users/customers. It should be kept in mind that the story of the video must be relevant to the brand. These days we’re surrounded by loads of videos on social networks like FB and Twitter as well which autoplay (but is muted of course). Analytical studies have also reported higher engagement due to this.

Recently I had to implement the same concept on a website and hence I decided to build an easy-to-use JS based plugin that works over HTML5 video element and does the right job of showing a background video inside a container basically. I’ve put all the code as well as a working demo on Github – Bideo.js. Let’s discuss what all went around in the process of implementation and some learnings that I acquired on the way.

Basic Implementation

You’d think that the implementations is pretty simple. Throw the HTML5 video element inside the container and specify 100% width and height. Well, seems reasonable but that won’t work, check this demo.

You’ll notice that the video gets center-aligned and leaves a lot of blank space on either sides. This happens because when you scale (or resize) a video element, it will change its dimensions proportionately. So if a video is 400×200, then even if you set its dimensions to 500×400, it won’t respect that. It’ll get resized to 500×250 (proportional).

So in this case what happens is, height 100% is achieved first and the width is computed accordingly which won’t be 100% of the parent container.

So this is the first problem that I encountered and I started figuring out the solution by looking at other implementations across different sites. Well I wish the video could be set as the background in CSS and then we could so something like background-size: cover/contain, but nope, that’s not possible (maybe in future). But that is the exact behaviour we require. Some may even think that this code will help:

#container {
  overflow: hidden;
  height: 400px;
  background: #edeae8;
  position: relative;
}

video {
  width: 100%; height: 100%;
  
  position: absolute;
  top: 0; left: 0; right: 0; bottom: 0;
}

But no, it won’t! You’ll notice the same behaviour (grows proportionately) except that this time it’ll be left-aligned, not center (demo).

At this point some of you might be wondering why am I even specifying a fixed height because that won’t be the case practically. Well, your container (whether a div, or the body itself) will have some specific generated/computed height basis the contents and the video will most probably not match those dimensions. So you’ll have to make sure that the video fits in dynamically because you won’t cross check the dimensions of the container and then create the video (or the video might just come from a different team or your website design/dimensions/layout might just change).

The Solution

It seemed like JavaScript is the only solution to this problem. The idea is to get the container’s computed dimensions (width and height), the video’s dimensions and basis some basic math, scale the video up or down ensuring that it’ll cover the entire container. Of course due to proportionate re-sizing, one of the dimension might actually be bigger than the container but that’s fine. In practise when one of the edges get cut by some amounts, it doesn’t look bad at all. The behaviour I’m talking about is exactly what background-size: cover does to images where the image covers entire width or height maintaining aspect ratio which means one of the dimensions will get clipped if the container’s dimensions are not proportional to that of the image.

So building upon the same demo, if we have to resize the video, ensuring full covering of the container, then this will be the code:

<!-- HTML -->
<div id="container">
  <video autoplay muted loop>
    <source src="source.mp4" type="video/mp4">
  </video>
</div>

Some CSS (notice how I vertically as well as horizontally center-align the video with top, left and transform):

/* CSS */
* {
  margin: 0; padding: 0;
}

#container {
  overflow: hidden;
  height: 400px;
  background: #edeae8;
  position: relative;
}

video {
  position: absolute;
  
  /* Vertical and Horizontal center*/
  left: 50%; top: 50%;
  transform: translate(-50%, -50%);
}

Finally, the JS magic that auto-resizes the video on load as well as when the window size changes:

// JS
var video = document.querySelector('video')
  , container = document.querySelector('#container');

var setVideoDimensions = function () {
  // Video's intrinsic dimensions
  var w = video.videoWidth
    , h = video.videoHeight;
  
  // Intrinsic Ratio
  // Will be more than 1 if W > H and less if W < H
  var videoRatio = (w / h).toFixed(2);
  
  // Get the container's computed styles
  //
  // Also calculate the min dimensions required (this will be
  // the container dimentions)
  var containerStyles = window.getComputedStyle(container)
    , minW = parseInt( containerStyles.getPropertyValue('width') )
    , minH = parseInt( containerStyles.getPropertyValue('height') );
  
  // What's the min:intrinsic dimensions
  //
  // The idea is to get which of the container dimension
  // has a higher value when compared with the equivalents
  // of the video. Imagine a 1200x700 container and
  // 1000x500 video. Then in order to find the right balance
  // and do minimum scaling, we have to find the dimension
  // with higher ratio.
  //
  // Ex: 1200/1000 = 1.2 and 700/500 = 1.4 - So it is best to
  // scale 500 to 700 and then calculate what should be the
  // right width. If we scale 1000 to 1200 then the height
  // will become 600 proportionately.
  var widthRatio = minW / w
    , heightRatio = minH / h;
  
  // Whichever ratio is more, the scaling
  // has to be done over that dimension
  if (widthRatio > heightRatio) {
    var newWidth = minW;
    var newHeight = Math.ceil( newWidth / videoRatio );
  }
  else {
    var newHeight = minH;
    var newWidth = Math.ceil( newHeight * videoRatio );
  }
  
  video.style.width = newWidth + 'px';
  video.style.height = newHeight + 'px';
};

video.addEventListener('loadedmetadata', setVideoDimensions, false);
window.addEventListener('resize', setVideoDimensions, false);

Demo time!

See the Pen wGLjwK by Rishabh (@rishabhp) on CodePen.

If you read the comments in the JS code, they do a pretty good job at explaining the basic math behind scaling up/down the video size.

Note: Although in this case we specified the video source right there in the HTML, ideally you should add source tags into the video via JS because at times if the video is fetched from cache or you’re testing locally, then loadedmetadata won’t be fired. This is because browsers load the video as soon as possible.

Overlays

Once the video is in place and is looking all nice and jazzy, one common requirement is to show an overlay right above the video so that the content (headings, subtext, etc.) over the video is fairly visible and readable for the users. This is fairly simple with HTML/CSS which is why I did not implement this as a part of my plugin. All you have to do is, add an overlay element to your container and style it like this:

#overlay {
  position: absolute;
  top: 0; right: 0; left: 0; bottom: 0;
  
  background: rgba(0,0,0,0.5);
}

Check out this live example and feel free to play with the colors of the overlay.

Video Cover

Another simple thing that can be achieved with HTML/CSS. All you need is have an element right after the video:

<div id="video_cover"></div>

and then add some basic CSS:

#video_cover {
  position: absolute;

  width: 100%; height: 100%;

  background: url('video_cover.jpeg') no-repeat;
  background-size: cover;
  background-position: center;
}

The new element with this background will show over the image. Two important things to notice are:

  • We’ve used background-size: cover which is exactly what our code that resizes the video does. Our logic mimics the behaviour of cover as it makes the video cover the full width or height of the container and whichever dimension exceeds that of the container is clipped.
  • We’ve used background-position: center because we also centered the video both horizontally and vertically. Assuming that video_cover.jped represents the first frame of the video, it is important that it masks the video properly in terms of position so that as soon as we hide it and play the video there’s no jerk due to positional changes of various elements in the image/video.

This cover can be hidden as soon as the video loads. Bideo.js accepts an onLoad callback which is where you can do this. This callback executes in the canplay event.

Network Speed

It will be desirable to not play a video on slow network connections. Now one might think that the ideal way to do this is to detect the network speed using JavaScript and then not play the video (instead show an image). Sure, this should be the ideal way only if there was a JS browser API (currently) to give us the user’s internet speed or a reliable way to detect the user’s connection speed. Unfortunately detecting the internet speed of a user’s connection is a tough task and most of the times not reliable.

In order to detect the speed, we’ll have to download a fairly large file (at least 5-10mb) which in itself is a downside for obvious reasons (wait for the download to complete in order to start the video, unnecessary bandwidth consumption, can deteriorate the webpage performance, etc.). Even if we do that, factors like DNS lookup, packet switching and the number of hops between the user and destination server, etc. will skew the result (speed detected).

Instead of detecting the user’s connection speed, if we could even implement an optimised playback where we buffered the video for a set time – say 5s – play that much and then stop again to buffer the same amount and re-play, that’d be nice. Otherwise on slower connections what can happen is the video can buffer and play at super short intervals leading to a bad user experience/perception. The problem is even that is hard because of the way different browsers implements the the video element and behaviour around it (including triggering of the events).

Even if we implemented a logic for chrome “how many seconds does it take to load 5s of data” to decide an acceptable loading rate, it might not work according to your expectations as I’ve noticed that chrome (v50) on slower connections will still stall the playback to buffer more even when there’s enough buffered to be played (about 10-15 more seconds).

Oh also at the moment there’s no way to figure out the content size of the amount of video that has been loaded in a given period of time (just for the sake of detecting speed and eventually set an acceptable loading rate to play the video).

So basically if you experiment different logics you’ll figure at least with the current APIs (video attributes, methods and events) it is hard to find a solution. Hopefully, this should become easier in the future.

HTML5 Video Options

There are several options (or configurations) not supported by the plugin I’ve written because they are well supported by the HTML5 video tag in itself. I’m basically referring to options like volume, muted, loop, playbackRate, autoplay, etc. You can find an exhaustive list of audio/video methods, events and properties here.

Considerations

Do ensure to keep certain things in mind while integrating a video in this format on your website:

  • Video quality should be good and use one that is relevant to your brand.
  • You’ll most probably autoplay your video but play no sound as that can be really annoying for the user. Have an unmute button if you want.
  • I’d recommend to use a super compressed video (for obvious reasons) with a length of about say 30-60 seconds. If the length is too short then it can feel weird as it loops (unless you stop it once played which might also be weird – a short video stopping too soon).

Conclusion

So implementing a fullscreen background video above the fold on your home or in any other specific container is super simple now. Just use Bideo.js and implement what you want in a few minutes. Do let me know what you think about the plugin or your usage in the comments below.

Advertisement

Author: Rishabh

Rishabh is a full stack web and mobile developer from India. Follow me on Twitter.

17 thoughts on “HTML5 Fullscreen Background Video with CSS and JS (Plugin/Library)”

  1. Hi, how about we give the video the following styles
    width: auto;
    height: auto;
    min-width: 100%;
    min-height: 100%;
    and center it with position absolute

    It seems to work without any js.

    1. That may not give you the best result (and the user the best experience). I’ll update the article to explain that.

      As mentioned by @Corey, object-fit is the way to go. I’ll update the article on this too. Although do note that the re-sizing logic still helps with IE as well as Edge as they still don’t support object-fit.

    1. For ios and some mobile platforms we need a new class such as -mobile-view and css-oriented code.Whatever that technologies are still imperfect

  2. I believe you could use:

    #container {
    display: flex;
    align-items: center;
    justify-content: center;
    }

    video {
    min-width: 100%;
    min-height: 100vh;
    width: auto;
    height: auto;
    }

    Am I missing something here?

    1. Works but not the best way, especially with HD videos. This won’t do “re-sizing” but cut the edges of the video when the screen is smaller than the intrinsic dimensions of the video. Imagine a cropped area in an image/video. Hence user won’t be able to see the entire video (scaled down) which is not a great experience.

  3. Hi,
    I think we just need the CSS only to make the video full screen.

    How about this code below?

    video {
    position: absolute;

    /* Vertical and Horizontal center*/
    left: 50%; top: 50%;
    transform: translate(-50%, -50%);
    width: 100%; height: auto;
    }

    1. Check my answers to the similar comments above. Will also update the post to include the reason as well as more information about object-fit.

Leave a Reply

Your email address will not be published. Required fields are marked *

*