The WDS Title Sequence Explained: HTML5 and CSS3 in Action

Tweet

I’ve just returned from Web Directions South in Sydney, where I saw some inspiring speakers geeking out on stage, and mingled with a bunch of other web-minded individuals. But the most inspiring thing I saw happened in the first ten minutes of the conference: Cameron Adams’s opening title sequence. At first sight, it’s just an animation revealing the names of all the speakers with some cool effects, played to a remix of the soundtrack to the film Inception. Then you learn that it’s created using HTML5, CSS3, and JavaScript in the browser.

Start by checking it out; you’ll have to view it using Chrome. For it to work correctly, you’ll need to adjust your screen resolution to 1024 by 768, load the page, switch to full-screen mode, and then refresh the page to see it from the start in the correct resolution.

Although it’s based on a soundtrack from Inception, Cameron’s demo doesn’t rely on the cast of characters explaining to us over three hours what’s going on. For that reason, I figured I’d dive into the source to gain a glimpse of the magic behind the scenes. The demo is made up of over 800 lines of CSS and 2,400 lines of JavaScript (not counting jQuery and a few monkey patches to add CSS3 support to it), so I’ll just be skimming the surface to explain a few of the cooler effects. If you’re more interested in a top-level overview of what was involved (and some of the weird bugs encountered along the way), check out Cameron’s blog post.

Music to Your Ears

The first thing you’ll notice when you view the source of the demo page is that there’s only one HTML element inside the <body> tags — an audio element:

<audio id="music" src="Music.mp3" oncanplaythrough="start();" preload="auto"></audio>

This, of course, is the new HTML5 element for native, in-browser audio.

The key here is the oncanplaythrough attribute. Like any on-something attribute, it refers to an event firing in the browser. In this case, the canplaythrough event fires when the element has loaded enough of the media file to be able to play it all the way to the end without buffering. Cameron uses it to call the start() method in his JavaScript to get his animation underway.

Timing is Everything

One of the trickiest aspects to an animation like this is the timing of various effects to the audio track. When those circles are pulsing around the first few names, they’re timed to coincide exactly with the haunting piano notes in the music.

How is this possible? Cameron’s JavaScript code begins with a big array of objects called TIME_CODE. Each object in the array contains a time code and a function name. The time code is either a number (in seconds from the beginning of the track), or a string like "+4", which just means “4 seconds after the last time code.” As you look through the array, you’ll see several time codes specified as multiples of a BEAT constant. That constant is the exact interval between the piano notes in the first few seconds of the music: 1.79 seconds.

The start() method, which is triggered as soon as the audio is ready to play (as we saw earlier), looks like this:


function start()
{
  $("body").unbind("click");

  stageWidth = $(window).width();
  stageHeight = $(window).height();

  startDate = new Date();

  var music = document.getElementById("music");
  music.play();

  setInterval(tick, 10);
}

The most important lines are the last two. First, he starts playing the music; then, he sets up an interval to run the tick method every ten milliseconds. The tick method is the cornerstone of the whole script:


function tick()
{
  var music = document.getElementById("music");

  if (TIME_CODE.length > 0)
  {
    var time = 0;

    if (typeof TIME_CODE[0].time == "string")
    {
      time = lastTime + parseFloat(TIME_CODE[0].time.replace(/+/, ""));
    }
    else
    {
      time = TIME_CODE[0].time;
    }

    if (music.currentTime >= time)
    {
      TIME_CODE[0].func();
      lastTime = time;
      TIME_CODE.splice(0, 1);
    }
  }
}

This is still fairly straightforward: the code grabs the first element in the TIME_CODE array; then checks to see whether the time property of that object is absolute or relative (that is, whether it’s a string or a number). If it’s relative, the code adds it on to the lastTime variable for the current time code. Then it gets the currentTime of the music; this is a built-in property of the HTML5 audio element. If the time code we’re waiting for has arrived, the code fires the function associated with it, sets lastTime, then chops out that first element of the TIME_CODE array using splice.

These allow the code to run each function at a very specific time, with a temporal resolution of ten milliseconds. As it turns out, this is close enough for our brains not to tell the difference, and the events on screen seem to be perfectly timed with the music.

Transitions and Transforms and Animations, Oh My!

The first function in TIME_CODE is presents(), but I’ll skip most of that one, because all it’s really doing is iterating over all the letters of the string “Web Directions Presents: South 2010″ and fading them in and out. What’s interesting, though, is that the fading is taking place in CSS, not in the JavaScript. The JavaScript code is simply setting each letter’s opacity to either 0.9 or 0, which would normally look like letters flicking on or off. However, in the CSS you’ll see rules like this:

-webkit-transition: all 3s ease-in;

This means that whenever any property of the element targeted is changed (whether by JavaScript or in the CSS itself with a change of pseudo-class selector), it will be animated over the period of three seconds using the ease-in timing function. For more on timing functions and other available options, check out the W3C spec on the CSS transition module. The end result is that each letter will appear to fade smoothly in and out.

Now let’s take a look at a more interesting effect: those pulsing circles I was talking about earlier. Each circle is actually a div with a border-radius set to half its size. Those pulses of light that emanate from each circle are simply identical divs with some CSS animation applied. Here’s the relevant CSS:


._1pulse
{
  position: absolute;
  background: -webkit-gradient(radial, 50% 50%, 0, 50% 50%, 30, from(rgba(255,255,255,0)), to(rgba(255,255,255,1)));
  opacity: 0.4;
  -webkit-animation-name: _1pulseAnim;
  -webkit-animation-duration: 1.85s;
  -webkit-animation-timing-function: ease-out;
}

@-webkit-keyframes _1pulseAnim
{
  0% {opacity: 0.4; -webkit-transform: scale(1)}
  100% {opacity: 0; -webkit-transform: scale(5)}
}

That code contains two separate bits of CSS3 magic. First of all, the pulse has a background radial gradient applied with -webkit-gradient. Second, and most interesting, is the animation. CSS3 animations require first setting up a number of keyframes using the @keyframes declaration (or in this case, @-webkit-keyframes). The set of keyframes is given a name (_1pulseAnim), which can then be referred to from a CSS rule. Each keyframe is given a selector: a percentage that tells the browser which point in the animation the keyframe sits at. In this very simple case, Cameron has just two keyframes: one for the initial state (0%), and one for the final state (100%). Only two properties are animated: opacity and scale. Those keyframes are then assigned to a given element’s animation by including them in a -webkit-animation-name declaration, like the one in the code above.

The end result is that when the pulse div is appended to the document, the animation runs. It scales up to five times the size of the central circle while fading away.

For His Next Trick …

There are tons more animations in this page: a network of lines stretching between those circles, followed by a scrolling wall of letters that pauses to highlight other presenters’ names. I don’t have space here to go over how each is done, but feel free to dive into the code yourself if you’re curious. However, I do want to briefly touch on what is, for me, the most impressive animation: the white boxes that swing around the screen to reveal names behind them. Believe it or not, almost this entire animation is performed by adding a single class to a single div. Cameron first creates all the names, and then sets a timeout to fade each one in at a given time. He then takes a giant div containing all the rounded rectangular boxes, and adds the class fly:


$("#_13blocks").addClass("fly")

This innocuous line of code triggers the following CSS animation:


#_13blocks.fly
{
  -webkit-animation-name: fly, flyOrigin;
  -webkit-animation-duration: 19.7s, 19.7s;
  -webkit-animation-timing-function: ease-in-out, ease-in-out;
  -webkit-animation-fill-mode: forwards, forwards;
}

@-webkit-keyframes fly
{
  0% {-webkit-transform: rotate(0deg) scale(1);}
  8.3% {-webkit-transform: rotate(-90deg) scale(3);}
  16.6% {-webkit-transform: rotate(-180deg) scale(4.3);}
  24.9% {-webkit-transform: rotate(-270deg) scale(5.6);}
  33.2% {-webkit-transform: rotate(-360deg) scale(7.5);}
  41.5% {-webkit-transform: rotate(-450deg) scale(10);}
  49.8% {-webkit-transform: rotate(-540deg) scale(13);}
  51.5% {-webkit-transform: rotate(-540deg) scale(13);}
  58.1% {-webkit-transform: rotate(-630deg) scale(13);}
  59.6% {-webkit-transform: rotate(-630deg) scale(13);}
  66.4% {-webkit-transform: rotate(-720deg) scale(13);}
  67.9% {-webkit-transform: rotate(-720deg) scale(13);}
  74.7% {-webkit-transform: rotate(-810deg) scale(13);}
  76.2% {-webkit-transform: rotate(-810deg) scale(13);}
  95.0% {-webkit-transform: rotate(-990deg) scale(1);}
  96.0% {-webkit-transform: rotate(-990deg) scale(1); -webkit-animation-timing-function: ease-in;}
  100% {-webkit-transform: rotate(-990deg) scale(15);}
}

Each keyframe specifies a rotation and a scale (most of which are very high in order to make the div several times larger than the browser’s viewport). The result: the div flies around the screen, stopping with a white area aligned to display a line of text. Each line will fade in at exactly that moment, thanks to the timeouts specified earlier in the JavaScript. The container then swings round again to display the next name. The ease-in-out timing function makes the animation seem smooth and natural, with a little bit of bounce. The overall effect is a sweeping demonstration of the power of CSS3 animations.

Haters Gonna Hate

The naysayers among you will now be crying out, “This only works at a specific resolution in a specific browser, and uses a ton of vendor prefixes! It’s useless in the real world!” The point is that it works in the context it was designed for: it played in the browser, projected onto the screen, and blew us all away. It’s not even close to the easiest way of creating that animation, but it does serve to show us where we’re going, and what’s just around the corner for web developers. With IE9 supporting a number of CSS3 features (though unfortunately not the animations that created this demo), the browser wars are back on, except that this time, they’re being fought over standards.

I’ve barely touched on a third of the cool animations and effects in the demo, so be sure to pop open the source and have a play if you really want to understand what’s going on.

Hats off to Cameron for the amazing work. I can’t even imagine how much work was involved in painstakingly lining up all those elements, not to mention the audio, to create such a seamless sequence. I’d also like to thank Jetstar who, by delaying my flight, enabled me to spend a few hours sitting on the floor of the departure lounge reading all that code.

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • EastCoast

    “but it does serve to show us where we’re going, and what’s just around the corner for web developers.”

    An impressive feat of coding, but can’t help feel that for every new whizz bang animated html5 intro we see lauded, we’re seeing an exhumed grave from era 1995 of some of the worst excesses of flash skip-intro, with many of those clamouring to show off these follies hypocritically ignoring how they’ve spent the last 5 years flaming against flash for exactly these sort of fripperies.

    “Welcome to Skip Intro 2.0 – now with twice the cpu, and half the compatibility”..?

    • Louis Simoneau

      Couldn’t disagree with you more (well, I probably could, care to try me?)

      This was an opening sequence for a conference, so the parameters and user expectations are vastly different from a web page. If people start using HTML5 and CSS3 for site intros a la 1995, then yeah, your point stands. But so far all I’ve seen is demos of the cool potential. When it gets down to using this stuff on a site, I think the full body of knowledge we’ve acquired over the last 15 (holy crap) years about how users interact with sites will come into play. Even most flash sites don’t use “skip intro” style animations anymore.

    • jmansfield

      I agree EastCoast. I haven’t seen it but it certainly sounds like we are excited about something that has been possible for quite a while with Flash, it’s just being done with a different technology. At the end of the day most users don’t want gratuitous animations, they want content or to complete tasks.

      • Dale Hurley

        The exciting thing about this is the ability to mash up multiple elements into one screen.

        Lets say you have a product that is a highly emotive product, you could do a mash up of rich sound, animation, etc in an accessible, rich standards way.

        Plus people need to do the far out possibles to find spin offs of practices. If we all did the practical solution we would still be in caves.

      • ringobob

        Not all animations are gratuitous. This is an amazing demonstration of the possible, and its up to developers to be responsible with its use. You’re not going to see Amazon’s homepage flooded with CSS3 transforms.

        Undoubtedly you’ll see people abusing this stuff. That’s the nature of the beast when the barrier to entry with web design is so low as to be virtually non-existent. But, as Louis said, for anyone that’s been around for the past several years, the things we’ve learned won’t just fly out the window.

  • Deadmau5

    What is the name of the soundtrack please?

    • Louis Simoneau

      Cameron linked to it in his blog post, which I’ve linked to at the top.

  • PetitPaul

    It also works in Safari which is using webkit like Chrome does.

  • evo

    well… latest public version of chrome, as the site says, just get a crazy recursive loop of errors… so, awesome.

    And while these things are cool as experiments, they should stay as experiments.

  • maboa

    Great demo, thinking of doing something similar myself. I’d like to ask, is there any reason why you didn’t use the <audio>’s timeupdate event?

    There are some reports that it is unreliable : http://developer.palm.com/distribution/viewtopic.php?f=11&t=8036

    but I’m not sure whether they have resolved these issues in more recent browser releases.

    • Louis Simoneau

      Check out Cameron’s blog post about the demo, linked at the top. Apparently timeupdate is unreliable and only fired about once every 300 milliseconds…

      • maboa

        Yup, I did a bit of experimenting and came to the same conclusion. Thanks for the heads-up though.

  • Q.E.D.

    You know, most of those effects were possible in IE6 using VML and JavaScript. (If only I had the time to emulate it.) Any VML-gurus out there care to give it a shot?

  • rnystrom

    That was pretty great. Google’s Chrome Experiment with Arcade Fire (http://www.thewildernessdowntown.com/) is what I show my friends to get them introduced to HTML5. I’ll have to start sending this link around.
    The animation of all of the letters, where the names were highlighted, became a bit choppy for me, which destroyed a bit of the immersion. I had dev tools open while I was watching and saw a massive amount of divs on the page at once. It looked like text though, not images, so I’m not sure why everything got bogged down. My best guess is too many objects being animated at once. I’m surprised they weren’t all contained in a parent element, that should have eased up on the performance.
    I love that people are starting to pick up on the fact that you just don’t need images anymore to create good looking 3d or even bent effects. This intro did a great job by not even having to use any elements!
    I’m with you on the swinging boxes, the combinations of keyframes/transitions were fantastically executed. Makes me wish I had a graphic/digital designer who could come up with stuff like this to work with, just for fun. I could even use an audio designer now!

  • empiricist

    Thanks for the article!

    It’s unfortunate that browser providers are trying to be proprietary; it completely negates the grass roots wish to improve web design through use of more versatile code. There have been loads of neat code over the years, but until they becomes standardized I’m not touching it.

    Widespread use of CSS3 in websites may be years away; until a CSS3 effect works on all browsers, most of us aren’t going to waste our time creating huge complex onion-style websites – I know my clients won’t fork out the money for all the extra work, and I can’t be bothered with the frustration of constantly hacking solutions for little or no client satisfaction.

    It’s like being in an F1 race car, but stuck in first gear. In most other areas of computer development, standardized improvements have evolved much more rapidly.

  • Ks2

    I’m not going to say: “This only works at a specific resolution in a specific browser, and uses a ton of vendor prefixes! It’s useless in the real world!”
    Because it doesn’t *even* work in that browser, at least for me.

    I just updated to the latest Chrome version to make sure the failure of this demonstration to get any video [audio's there, but the screen is otherwise black, except for the leave fullscreen link when the fullscreened] were not due to an older version, but, nope.

    This, combined with other failures of HTML5-vapor code to run are, to me, boldly underlining the notion that the 800 pound gorilla’s push to get everyone to adopt HTML5 is not just self-serving but ridiculously premature.

    Of course, we need to continue developing, stabilizing, and standardizing HTML5 going forward — but it is an under-ripe fantasy that it is anywhere close to ready for prime time.

  • http://www.sky-web.net/ Dr John

    Can’t say I’m impressed with a plain black screen and a sound track after having to reset the screen resolution. Three times I tried, three times it was just the sound track. A lot of effort for nothing.

    And will everyone who using this technique, to do what ever it did, always have to give instructions to site visitors telling them to reset their screen resolution? If so, I can’t see it being used much, can you?

  • JohnAllsopp

    Hi Empiricist,
    currently all shipping browsers other than IE support more or less all of what Cam used to build this. IR lacks support for CSS transitions/animations, but these can in theory be emulated in JS.
    So, this, while experimental is far from a pipe dream – I expect it to run well in pretty much all browsers, on pretty much any device, within a year – indeed, with some resources it could be made to do so right now.
    As the “client” for this work, I can tell you I am way beyond satisfied with what Cam achieved.

  • jhoye

    Thanks for sharing this Louis.

    In the last 10 years we’ve come to expect the web browser to be a lot more than a text reader. HTML has always been lacking in its ability to work with multimedia “natively”; some additional client-side engine has always been needed to do the heavy lifting (Applets, Shockwave, Flash, Silverlight…). JavaScript can finally be used elegantly for this sort of thing.

    Cameron’s code is ripe for the semanticist haters’ critique, but it’s obvious that his point was to showcase what the new stuff can do instead of making the code perfect from every angle of criticism.

  • linda

    FYI: I clicked on the link to that presentation and saw the titles before I realized I had to turn on my speakers (at work, so I needed my headset). Wanting to experience it from the beginning, I tried it a second, third and fourth time and heard the sound but never saw the titles again. Each time, I closed the old window and clicked the link to open a new window.