How to implement a seamless responsive video background using Youtube and WordPress

Published October 1, 2019
.* :☆゚

Today, I wanted to share my technique on how I serve video backgrounds on my WordPress builds. I think it’s a pretty neat strategy I’ve used for a while now that just works, and I really hope it helps you too.

Whether you are for or against video backgrounds, many websites I’ve worked on have specifically requested the feature. Now I am all for a leaner web, but you can’t always get what you want. If used appropriately, video backgrounds can add to the website’s experience so I’m not totally opposed to them.

Yes there are plugins that can spit out a full page video for you in WordPress. I’ve even used one before, but I think I can safely say this technique is much simpler and way faster to load for the user.

And as an aside, although this article is written around WordPress and JQuery, I think this strategy is extensible to other sites too if the JQuery syntax is converted to vanilla JS.

Why Youtube?

If you want to serve a video background reponsively, it’s a good idea to host it on a dedicated video host such as Youtube. Not only will you save bandwidth by not uploading videos to your own site, Youtube is designed to serve videos optimally to users based on a number of factors such as internet speed and device size. Youtube is also able to handle large video uploads and provides a video player that is far more robust than your stock standard html5 video player.

You can also use Vimeo if you wish, but I find Youtube’s API and ecosystem a lot easier to work with.


The pitfalls of embedding videos

Embedding a Youtube video on a website is essentially embedding an iframe that loads a video. Embedding it in a post is one thing. Using it as the “background” of a page is another.

When you resize the browser, regardless of any iframe styles, you’ll notice the video will not resize with the window.

Instead you’ll get black bars, like below:

Aspect ratio black bars
Screenshot from http://therivo.com.au with edits for demonstration purposes

This is due to the fact that videos are restricted by its aspect ratio. That is, the proportion between its width and its height. Most videos filmed these days are 16:9, or 16: 10.

This issue, coupled with the workarounds you need to include for the browser to autoplay videos is why implementing video backgrounds is a lot more finicky than it sounds.

If you are ok with serving a video at 169 resolution on all browser devices, you can simply use the padding bottom trick to do so. If you want it to act as a background of an element however, you’ll need some JS to take care of the resizing for you.

The method below takes care of these pitfalls and allows the video to act similarly to background:cover. The video will be cropped at some points particularly on mobile devices, but it will also adjust to any browser size without showing the ugly black bars or Youtube’s branding.


The code

Navigate to where you want to place the video in your markup. For me, that is probably in header.php where I call a custom template file.

1. Create a custom field to store the Youtube video ID.

I use and love ACF, but you can create this custom field however you wish, as long as it is easily updateable by you and maybe the client too.

2. Store the Youtube video ID in your custom field.

For example, if the Youtube link was https://www.youtube.com/watch?v=8j68_4z7Sbg you would store 8j68_4z7Sbg in the custom field.

3. Paste the following code into your PHP and edit the variables as necessary to suit your own site.

The next step involves a bit of PHP and implementation of the Youtube JS API. I use Youtube’s JS API because although you can embed a video with Youtube’s HTML embed tag, you simply can’t autoplay an embedded video without JavaScript to trigger it.

You may need to paste the following in your code editor and read the comments for further details on how to edit this snippet.

<?php
//pass in the reference to the custom field you made
//earlier and store it as a PHP variable
  $videoid = get_field('feature_video_url', get_the_ID());
  $videourl = 'https://www.youtube.com/embed/' . $videoid;
?>

<!--
.video-bg uses the Youtube ID to get a
hi res thumbnail to serve as an image fallback.
You can change the size of this to suit your needs.
 -->
<div class="feature video">
  <div class="video-bg" style="background-image:url('http://img.youtube.com/vi/<?php echo $videoid; ?>/maxresdefault.jpg')">
  <div id="video" class="video-wrapper">
    <div id="player"></div>
    <!--We inline the JS so the script runs as soon as the page loads.-->
    <script type="text/javascript">
      var viewportWidth = window.innerWidth || document.documentElement.clientWidth;
      /* if the browser width is more than iPad Pro size, render the iframe
      I keep this conditional in here because I don't often use autoplaying
      vides on mobile devices. You can uncomment th conditional below if this
      is not needed in your case.
      */
      if ( viewportWidth > 1024) {
        var tag = document.createElement('script');
        tag.src = "https://www.youtube.com/iframe_api";
        var firstScriptTag = document.getElementsByTagName('script')[0];
        firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
        var player;
        function onYouTubeIframeAPIReady() {
          player = new YT.Player('player', {
            // the ID we stored earlier
            videoId: '<?php echo $videoid; ?>',
            /*
            The following params are part of Youtube's JS API.

            You can view all the parameters here:
            https://developers.google.com/youtube/player_parameters?hl=en

            The ones I've included below produces the most minimal player
            as Youtube allows but feel free to edit these params as you wish.
            Be sure  to leave the mute tag as is (this is necessary
            for autoplaying the video on browsers)
            */
            playerVars: {
              'autoplay': 1,
              'rel': 0,
              'showinfo': 0,
              'modestbranding': 1,
              'playsinline': 1,
              'showinfo': 0,
              'rel': 0,
              'controls': 0,
              'color':'white',
              'loop': 1,
              'mute':1,
              'playlist': '<?php echo $videoid; ?>'
            },
            events: {
              'onReady': onPlayerReady
            }
          });
        }
        function onPlayerReady() {
          player.playVideo();
          //IMPORTANT DO NOT DELETE
          // NO ONE WANTS AUTOPLAYING YOUTUBE AUDIO
          player.mute();
        }
      }
    </script>
   </div>
  </div>
</div>

The most important thing to note in this step is that you must ensure the video loads with muted audio, because it is bad practice to autoplay audio on a website and is a feature most browsers try to discourage.

If you’ve updated your code and your video is not autoplaying at this point, there is likely an error with the JavaScript in which case you should go over your code carefully to check for typos, and consult the API page. Also note that users are able to block all videos from autoplaying through their browser preferences in Safari.

4. Add the SCSS

Add the following styles to your site in whichever way your prefer. Note that the following code is in SCSS syntax.

//this is the outer container wrapper and it contains the video. It can be any height you wish but since I am making a background today, I'm leaving it as 100vh.
.feature.video {
  height: 100vh; 
  width: 100%;
  overflow: hidden;
}

.video-wrapper {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  display: block;
  height: 0;
  width: 100%;
  padding: 0;
  padding-bottom: 56.25%;

  iframe {
    height: calc(100% + 100px);
    width: 100%;
    position: absolute;
    top: 0;
    left: 0;
    bottom: 0;
    pointer-events: none;
    margin-top: -50px;
  }
}
.video-bg {
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
    width: 100%;
    height: 100%;
}

.video-wrapper ensures the iframe is always displayed at 16:9 ratio using the padding-bottom trick, while feature.video wraps around it all to set the height and clip any overflowing video (which will be scaled in the next step)

I also gave the iframe extra height and a negative margin to hide Youtube’s branding. You can’t get rid of the logo etc through the JS API alone, so with a bit of smoke and mirrors we hide it with CSS magic.

Also it is important to note, this code defaults to a 16:9 resolution. If the resolution of your video is not this size, you’ll need to update the padding percentage of .video-wrapper according to the proper aspect ratio.. You can click here for more examples of different aspect ratio padding percentages.

By this point the video should be autoplaying smoothly on all browsers and devices, but the aspect ratio still isn’t right. There’s just one more thing we need to do…

5. Add more JavaScript

This extra snippet is what brings it all together, and is really what makes the video a responsive ‘background’.

Using the browser’s width and height and some handy CSS transforms, we can calculate how much the video should be scaled by its proportion to it’s outer wrapper.

Attaching this to a $(window).resize() event ensures the video’s scale updates as the browser’s size updates.

Paste the following code in your scripts file and hard refresh the page.

function video() {
  let video = $('.video-wrapper'),
      wrapperWidth = $(window).width(),
      videoWidth = video.outerWidth(),
      videoHeight = video.outerHeight();

  //if the browser is less than iPad pro size, add on an extra
  //100 pixels to get around the elastic url bar on browsers like Safari.
  if ($(window).width() < 1024) {
    var wrapperHeight = $(window).height() + 100;
  } else {
    var wrapperHeight = $(window).height();
  }

  //calculate how much the video should scale by comparing the 
  //video's width and height to its parent wrapper
  let scale = Math.max(wrapperWidth / videoWidth, wrapperHeight / videoHeight);

  video.css({
    transform: "translate(-50%, -50%) " + "scale(" + scale + ")"
  });
}

video();

//update the video's scale as the browser resizes
$(window).on('resize', function () {
  video();
});

By this point, if everything worked out well, you should now have a fully functioning autoplaying video background.

It was quite the journey to get here, but hopefully you learned something and can now rely on less plugins to do the same job.

If you’d like to see this technique in action, here are two sites I’ve built.