Video is a bigger part of the web experience than ever before. With native browser support for HTML5 video elements freeing us from the tyranny of plugins, and the availability of faster internet connections to the workplace, home and mobile networks, it’s now pretty straightforward to publish video in a way that can be consumed in all sorts of ways on all sorts of different web devices.
I recently worked on a project where the client had shot some dedicated video shorts to publish on their site. They also had some five-second motion graphics produced to top and tail the videos with context and branding. This pretty common requirement is a great idea on the web, where a user might land at your video having followed a link and be viewing a page without much context.
Known as bumpers, these short introduction clips help brand a video and make it look a lot more professional.

Adding bumpers to a video
The simplest way to add bumpers to a video would be to edit them on to the start and end of the video file itself. Cooking the bumpers into the video file is easy, but should you ever want to update them it can become a real headache. If the branding needs updating, for example, you’d need to re-edit and re-encode all your videos. Not a fun task.
What if the bumpers could be added dynamically? That would enable you to use the same bumper for multiple videos (decreasing download time for users who might watch more than one) and to update the bumpers whenever you wanted. You could change them seasonally, update them for special promotions, run different advertising slots, perform multivariate testing, or even target different bumpers to different users.
The trade-off, of course, is that if you dynamically add your bumpers, there’s a chance that a user in a given circumstance might not see the bumper. For example, if the main video feature was uploaded to YouTube, you’d have no way to control the playback. As always, you need to weigh up the pros and cons and make your choice.
HTML5 bumpers
If you wanted to dynamically add bumpers to your HTML5 video, how would you go about it? That was the question I found myself needing to answer for this particular client project.
My initial thought was to treat it just like an image slideshow. If I were building a slideshow that moved between images, I’d use CSS absolute positioning with z-index to stack the images up on top of each other in a pile, with the first image on top. To transition to the second image, I’d use JavaScript to fade the top image out, revealing the second image beneath it.

Now that video is just a native object in the DOM, just like an image, why not do the same? Stack the videos up with the opening bumper on top, listen for the video’s onended event, and fade it out to reveal the main feature behind. Good idea, right?
Wrong
Remember that this is the web. It’s never going to be that easy. The problem here is that many non-desktop devices use native, dedicated video players. Think about watching a video on a mobile phone – when you play the video, the phone often goes full-screen in its native player, leaving the web page behind. There’s no opportunity to fade or switch z-index, as the video isn’t being viewed in the page. Your page is left powerless. Powerless!

So what can we do? What can we control?
Those of us with particularly long memories might recall a time before CSS, when we’d have to use JavaScript to perform image rollovers. As CSS background images weren’t a practical reality, we would use lots of <img> elements, and perform a rollover by modifying the src attribute of the image.
Turns out, this old trick of modifying the source can help us out with video, too. In most cases, modifying the src attribute of a <video> element, or perhaps more likely the src attribute of a source element, will swap from one video to another.
Swappin’ it
Let’s take a deliberately simple example of a super-basic video tag:
<video src="mycat.webm" controls>no fallback coz i is lame, innit.</video>
We could very simply write a script to find all video tags and give them a new src to show our bumper.
<script>
var videos, i, l;
videos = document.getElementsByTagName('video');
for(i=0, l=videos.length; i<l; i++) {
videos[i].setAttribute('src', 'bumper-in.webm');
}
</script>
View the example in a browser with WebM support. You’ll see that the video is swapped out for the opening bumper. Great!
Beefing it up
Of course, we can’t just publish video in one format. In practical use, you need a <video> element with multiple <source> elements containing your different source formats.
<video controls>
<source src="mycat.mp4" type="video/mp4" />
<source src="mycat.webm" type="video/webm" />
<source src="mycat.ogv" type="video/ogg" />
</video>
This time, our script needs to loop through the sources, not the videos. We’ll use a regular expression replacement to swap out the file name while maintaining the correct file extension.
<script>
var sources, i, l, orig;
sources = document.getElementsByTagName('source');
for(i=0, l=sources.length; i<l; i++) {
orig = sources[i].getAttribute('src');
sources[i].setAttribute('src', orig.replace(/(w+).(w+)/, 'bumper-in.$2'));
// reload the video
sources[i].parentNode.load();
}
</script>
The difference this time is that when changing the src of a <source> we need to call the .load() method on the video to get it to acknowledge the change.
See the code in action, this time in a wider range of browsers.
But, my video!
I guess we should get the original video playing again. Keeping the same markup, we need to modify the script to do two things:
- Store the original
srcin adata-attribute so we can access it later - Add an event listener so we can detect the end of the bumper playing, and load the original video back in
As we need to loop through the videos this time to add the event listener, I’ve moved the .load() call into that loop. It’s a bit more efficient to call it only once after modifying all the video’s sources.
<script>
var videos, sources, i, l, orig;
sources = document.getElementsByTagName('source');
for(i=0, l=sources.length; i<l; i++) {
orig = sources[i].getAttribute('src');
sources[i].setAttribute('data-orig', orig);
sources[i].setAttribute('src', orig.replace(/(w+).(w+)/, 'bumper-in.$2'));
}
videos = document.getElementsByTagName('video');
for(i=0, l=videos.length; i<l; i++) {
videos[i].load();
videos[i].addEventListener('ended', function(){
sources = this.getElementsByTagName('source');
for(i=0, l=sources.length; i<l; i++) {
orig = sources[i].getAttribute('data-orig');
if (orig) {
sources[i].setAttribute('src', orig);
}
sources[i].setAttribute('data-orig','');
}
this.load();
this.play();
});
}
</script>
Again, view the example to see the bumper play, followed by our spectacular main feature. (That’s my cat, Widget. His interests include sleeping and internet marketing.)
Tidying things up
The final thing to do is add our closing bumper after the main video has played. This involves the following changes:
- We need to keep track of whether the
srchas been changed, so we only play the video if it’s changed. I’ve added themodifiedvariable to track this, and it stops us getting into a situation where the video just loops forever. - Add an
elseto the event listener, for when theorigis false (so the main feature has been playing) to load in the end bumper. We also check that we’re not already playing the end bumper. Because looping.
<script>
var videos, sources, i, l, orig, current, modified;
sources = document.getElementsByTagName('source');
for(i=0, l=sources.length; i<l; i++) {
orig = sources[i].getAttribute('src');
sources[i].setAttribute('data-orig', orig);
sources[i].setAttribute('src', orig.replace(/(w+).(w+)/, 'bumper-in.$2'));
}
videos = document.getElementsByTagName('video');
for(i=0, l=videos.length; i<l; i++) {
videos[i].load();
modified = false;
videos[i].addEventListener('ended', function(){
sources = this.getElementsByTagName('source');
for(i=0, l=sources.length; i<l; i++) {
orig = sources[i].getAttribute('data-orig');
if (orig) {
sources[i].setAttribute('src', orig);
modified = true;
}else{
current = sources[i].getAttribute('src');
if (current.indexOf('bumper-out')==-1) {
sources[i].setAttribute('src', current.replace(/([w]+).(w+)/, 'bumper-out.$2'));
modified = true;
}else{
this.pause();
modified = false;
}
}
sources[i].setAttribute('data-orig','');
}
if (modified) {
this.load();
this.play();
}
});
}
</script>
Yo ho ho, that’s a lot of JavaScript. See it in action – you should get a bumper, the cat video, and an end bumper.
Of course, this code works fine for demonstrating the principle, but it’s very procedural. Nothing wrong with that, but to do something similar in production, you’d probably want to make the code more modular to ease maintainability. Besides, you may want to use a framework, rather than basic JavaScript.
The end credits
One really important principle here is that of progressive enhancement. If the browser doesn’t support JavaScript, the user won’t see your bumper, but they will get the main video. If the browser supports JavaScript but doesn’t allow you to modify the src (as was the case with older versions of iOS), the user won’t see your bumper, but they will get the main video.
If a search engine or social media bot grabs your page and looks for content, they won’t see your bumper, but they will get the main video – which is absolutely what you want.
This means that if the bumper is absolutely crucial, you may still need to cook it into the video. However, for many applications, running it dynamically can work quite well.
As always, it comes down to three things:
- Measure your audience: know how people access your site
- Test the solution: make sure it works for your audience
- Plan for failure: it’s the web and that’s how things work ‘round these parts
But most of all play around with it, have fun and build something awesome.


Comments
Comments are ordered by helpfulness, as indicated by you. Help us pick out the gems and discourage asshattery by voting on notable comments.
Got something to add? You can leave a comment below.
01/12/2012
Hooray, a new year! Nice article, good to see non-frameworked JavaScript :-)
One regex gotcha:
* “.” means any character except newline, and w is alphanumeric+underscore – so if your source src attribute was “my-cat.mp4”, then the first line that sets the src attribute would end up putting in “bumper-in.cat”. If you don’t know what your filenames will be apart from the extension, plus you only care about the extension, probably better just to use something like /.(w+)$/
And a couple of regex typos:
* The “w” character classes all appear to be missing their “”, unless you only want to match wwwww.www ;)
* The last regex has “([w]+)” which also should presumably be w+
Hope that’s helpful.
Vote Helpful or Unhelpful
01/12/2012
Thanks, Drew. This seems like a great solution. Unfortunately, none of the examples work here on my iPhone 4 running iOS 5.
First few examples don’t play, and the bumper examples play the first bumper, then reload the first bumper and pause. If you press play, it plays a second of the bumper and pauses, an repeats that infinitely. Never loads the main video.
Just an FYI. :)
Vote Helpful or Unhelpful
02/12/2012
Avangelist – we don’t try to ‘sell’ clients on technical implementation details. They hire us to do a job and we get on with it to the best of our ability.
Vote Helpful or Unhelpful
01/12/2012
Awesome tutorial, but like Jason, this doesn’t work on my iPhone. I have iOS 5.1.1 and iPhone 3GS.
Vote Helpful or Unhelpful
01/12/2012
Great start to 24 Ways 2012.
What’s the performance like on loading 2 videos? I know Firefox is quite keen when loading assets. I’m assuming the main video loads in the background as its requested on page load. Any issues with the browser downloading the main video and the front bumper at the same time?
Worked great here but the videos are short.
Vote Helpful or Unhelpful
04/12/2012
Cool, i have a feeling this is going to be used for evil (commercials :P ).
When hitting “play” again though the outro bumper plays again.
Vote Helpful or Unhelpful
01/12/2012
This is great thanks Drew; really interesting approach. I’d worked with bumpers for clients using Brightcove and then tried to replicate similar functionality for another client using the onended event to pass in a second video to the player. I hadn’t however taken the next step as you have here, so thanks for sharing :)
Cheers,
K
Vote Helpful or Unhelpful
01/12/2012
Another great article to start the new season!
I think this solution is elegant, and it responds to two main issues: SEO and browsers without javascript.
Vote Helpful or Unhelpful
01/12/2012
This is awesome!! Thanks Drew. Good example of progressive enhancement.
I am just beginning to dive deeper into JS & jQuery, but I am wondering if we could add fade ins or fade outs or even cross fades. That would improve the transition between the bumper and main video, allowing the bumper to seem baked in.
Also, I use Vimeo for all my video embeds. Can you place an href or external url inside a <source> tag?
Vote Helpful or Unhelpful
01/12/2012
Unlike your cat in the video i am very impressed by this tutorial. Works fine on desktop! However as stated above have some issues on iPhone4 IOS5: doesn’t work using opera mini, on chrome for mobile and safari i get only the first bumper. Will try on IOS6 too. Thank you very much for the great opener.
Vote Helpful or Unhelpful
06/12/2012
Awesome piece, but I can’t help but feel the transition between bumper and video is quite a jarring effect. Is there any way to get around this so the bumper almost melts into the video seamlessly?
Vote Helpful or Unhelpful
06/12/2012
Awesome piece, but I can’t help but feel the transition between bumper and video is quite a jarring effect. Is there any way to get around this so the bumper almost melts into the video seamlessly?
Vote Helpful or Unhelpful
05/12/2012
I get a 403 error: “…it appears the server is denying access to the file” for examples 2 thru 4.
The code and idea however; great stuff. I too like the use of actual JavaScript versus frameworks.
Vote Helpful or Unhelpful
28/12/2012
A nice way to add a bumper, or a video ad, before starting the right video. It’s easy to add a skip button too.
Vote Helpful or Unhelpful
01/12/2012
Nice one.
I remember trying different things like this ten years ago using SMIL, sometimes running two or more files concurrently (separate audio/video files), adding interactivity (e.g., nav inside the presentation), etc. Maybe with Flash (mostly) out of the picture now SMIL could find some footing again. If nothing else, it was fun to play with. (Nostalgia.)
Vote Helpful or Unhelpful
01/12/2012
Matthew – thanks! I think some of the JavaScript has been eaten by Textile. Hopefully the examples are ok (even if the example regexps are a little bit brittle). We’ll check it over.
Vote Helpful or Unhelpful
01/12/2012
Again a very good start this year. Very helpfull.
With this simple technique we can use the same product movie on each different webshop for the same product, with only a different branding bumper.
When example 2, 3 and 4 I got a 403 error (using Chrome, IE was no problem). Example 1 was no problem. Is this only with my particular computer or can’t it be used with Chrome? Because a lot of people use Chrome these days.
Vote Helpful or Unhelpful
03/12/2012
Lovely start to my favorite December tradition! Following along in Chrome, I found that what was missing was a way to replay the video. One more round of load with the first bumper?
Vote Helpful or Unhelpful
03/12/2012
Thanks Drew. Good to find a nice read on such enhanced approach. A sure thing to put my nose in :)
Vote Helpful or Unhelpful
02/12/2012
Very impressive thought process with a fantastic suggestion for utopia.
How are you going to sell that to your client? You found a way for them to be able to maintain their branding on their videos forever…. As long as the viewing is part of the percentile that supports technologies your client doesn’t understand.
Sounds like ao tough sell to me.
Vote Helpful or Unhelpful
01/12/2012
But why wouldn’t you simply manipulate three separate video elements? This way you will be able to fade the bumpers into the main video for example or use some other effect. But in any case, the main point is, that this will open opportunity for more flexible and smooth transitions. With less javascript.
Vote Helpful or Unhelpful
01/12/2012
You know Christmas is coming when 24 Ways starts posting – only 24 more sleeps!!!
Vote Helpful or Unhelpful
Impress us