A few pages on our website use full-motion backgrounds to catch the eye of our visitors. Our recently redesigned homepage as well as our 404 page use JW Player to load a playlist and cycle through the videos in with smooth transitions in between. I’ll share with you how we do it, and give some tips on best practices for using video for your website’s hero.
Choosing the Right Videos for Your Background
On a typical website, a hero image is used to draw attention to above-the-fold content. As with any background, attention must be directed the content layer on top of it. We’ve found that good background video should have low contrast and soft movement (or preferably both). Our current playlist on the homepage contains three different videos: “Whiskey Smoke”, “Jellyfish”, and “Brooklyn Bridge”.
Why Not Just Use an HTML5 Video Tag?
Using a vanilla
The Steps, in a Nutshell
- Prepare your videos
- Upload and Tag Your Videos
- Set up the Player
- Style Your Content
Prepare Your Videos
The first thing you’ll want to do is get your background video ready. Trim off any blank frames from the beginning and end of the video. Trimming these blank frames will help your videos transition smoothly as the playlist progresses. The next step is to grab the first frame and save that as an image – this will become the video’s poster image. The trick that we’ll be doing later will make the player fade out before finishing, so that the next video in the playlist can fade in after it’s loaded and starts playing. Thirdly, if you don’t plan on making the video interactive, a good idea would be to strip out any audio tracks that may be present. Removing the audio provides two advantages: the final result will have less data to load, and baked-in media controls won’t appear on newer mobile devices. I’ll get into that a little later. Now that you have your fresh-cut videos and first-frame poster images to go along with them, it’s time to dive into the dashboard.
Upload and Tag Your Videos
After uploading your videos, make sure to upload the poster images created earlier. We also recommend tagging your videos so you can easily create a Curated Playlist if you have multiple background videos that you’d like to loop through.
Set up the Player
So now that we have our videos uploaded and a playlist created, let’s look at the different parts that go into the code. Here’s how we want the experience to go:
Use an AJAX call to grab the JSON feed for the playlist
First, we grab the JSON feed data using the JW Player Feeds API. This allows us to store the playlist config and modify it before setting up the player (for instance, we shuffle the playlist order when you load the page).
var fullPlaylist; var mediaId = 'CSh1f2GU'; // this would be your playlist's media ID $.ajax({ url: 'https://content.jwplatform.com/feeds/' + mediaId + '.json', dataType: 'JSON' }).done(function(data) { fullPlaylist = data.playlist; // storing the playlist data for later use setupPlayer(); });
Player Setup
With our playlist object, we then create the player. The player should have a few defaults set so it works best:
- The video should autoplay
- Controls should be disabled
- Stretching should be ‘fill’ so it doesn’t show black bars
The setup might look similar to this:
var videoInstance = jwplayer('hero-video').setup({ autostart: true, controls: false, playlist: fullPlaylist, androidhls: false, mute: true, repeat: true, stretching: 'fill', height: '100%', width: '100%' });
Now that our player is set up, let’s add some event handlers so we can control the experience when certain things happen. One goal is to present a smooth transition between videos in the playlist.Note: These methods are still a good idea even if you’re only using one video. If you use the Feed API with the media ID of a single video, the API will return a playlist that contains just that one video. Since the player behaves the same way for any number of videos, the next steps will provide a good visual experience.What we want to do is eliminate the blank step while the player loads the next video. This is what the poster image is for – we use the “first frame” image as a fading transition between videos. Here’s what we’re going for:
- Fade the video out to transparent with a CSS class (opacity: 0;).
- When the video starts playing, remove the CSS class, which will trigger a transition event.
- When the transition ends and the video is fully opaque, set the background-image of the hero container to the poster image of the next-up video, as designated by the fullPlaylist object.
- When the video is within 2 seconds of finishing, repeat at step 1.
So we’ll need some CSS setup:
.hero-video { opacity: 1; transition: opacity 2s ease-out; } .hero-video.is-hidden { opacity: 0; }
var videoContainer = $('#hero-video-container'); var isFading = false; // This flag makes sure we aren't constantly adding the class when we don't need to videoInstance.on('play', function() { videoContainer.removeClass('is-hidden'); }); videoInstance.on('complete', function() { isFading = false; }); videoInstance.on('time', function(e) { if (e.position >= (e.duration - 2) && !isFading) { isFading = true; videoContainer.addClass('is-hidden'); } }); videoContainer.on('transitionend', function(e) { if (!videoContainer.hasClass('is-hidden')) { setPosterImage(videoInstance.getPlaylistIndex() + 1); } });
The last piece callssetPosterImagewhich grabs the poster image for the next video and sets it to the background of the video container:
function setPosterImage(playlistIndex) { var posterImage; playlistIndex = playlistIndex >= fullPlaylist.length ? 0 : playlistIndex; posterImage = fullPlaylist[playlistIndex]['image']; // create an img tag to preload, then set it as the bg of the videoContainer $('').attr('src', posterImage).load(function() { $(this).remove(); videoContainer.css('background-image', 'url(' + posterImage + ')'); }); }
Style Your Content
At this point you’re probably thinking this is great, but it’s not the hero I was expecting. The rest comes down to HTML structure and CSS styling. The trick is to let the contents of your hero dictate its height and width, instead of using the video (which is configured to take up as much space as possible). Here’s what that may look like:
The Hero We Deserve
.hero { display: flex; align-items: center; justify-content: center; min-height: 60vmin; position: relative; width: 100%; } .hero-video { position: absolute; top: 0; left: 0; width: 100%; height: 100%; }
Bits and Pieces
Going back to why we removed the audio tracks from the videos, the answer is pretty simple. When media has audio on mobile devices, playing that media will pause anything else that’s playing on the device. For example, if you’re listening to music on your phone while browsing the web, auto-playing media will pause your music. I felt this was a frustrating experience, especially when it’s a video used for aesthetic and not for high-focus content. This behavior is almost entirely fixed by removing the audio tracks before uploading the video. One odd case shows up when using HLS on Android – playing HLS content in Chrome will pause other background media and present the system’s media controls regardless of whether or not it has audio. This can be resolved by addingandroidhls: falseto your JW Player config. We also use an overlay on top of the video via an::afterpseudo-element. This lets us modify the video and adjust the contrast so the content on top of the video stands out better. We have a few additional steps that use an accent color, which is added as a custom value on the video. The Feed API returns custom values that you can set in the Dashboard, so you can keep all configuration info in one place, and have it load dynamically with various JW Player API calls and events.
Final Notes
The biggest advantage that JW Player has over building an experience like a background video hero from scratch is that everything needed to get up and running can be referenced dynamically. This means that Content Editors and Publishers can handle all of the metadata, playlists, and custom variables, and Developers can focus on making their ideas stand out. Similar to theInsights Video Experience, all of the content is handled by the content creators, so updating the playlists or video titles doesn’t require any tedious development work. It’s a win-win!