Playing with Fire: Organic CSS3 Animation

Tweet
Mythbusters testing the chinese water torture.

Mythbusters testing the chinese water torture in 2008.

A few years back, I saw a great episode of Mythbusters, where they tested the idea of the Chinese water torture. One by one, Adam, Kari, Scotty and Tory were each firmly secured before being subjected to the slow, rhythmic beat of droplets onto their foreheads.

And one by one, each was broken down — in Kari’s case, to the point of tears — in under an hour.

It’s not the most comfortable thing to watch, but it offers an obvious take-away: While some rhythms can be soothing, tight repetition can quickly drive us stark-raving mad.

Of course, as web people, we already know this intuitively!

Who hasn’t experienced the site with the endlessly spinning logo? Who hasn’t been driven to distraction by that industrious little construction worker toiling away behind an “Under Construction” sign?

It raises an obvious question: Is it possible to use animation without introducing these mind-torturing loops?

Let’s see what we can do.

CSS3 Animation Basics

If you haven’t used CSS3 animations yet, here’s a crash course. If you’ve already got some familiarity with CSS3 keyframes, jump to “Working with CSS3 Sprites.”

To keep things breezy to read, I’m going to use the official “unprefixed” W3C syntax (i.e. @keyframe) throughout this article, but you’ll need to use the browser-specific prefixes (i.e. @-webkit-keyframe, @-moz-keyframe, @-ms-keyframe, etc.) to see it working in real-world browsers.

The core concept behind CSS3 animation is to let you transition between CSS properties. Like all animation, CSS3 needs a timeline with a start and a finish — set at 0% and 100% respectively. The code for the simplest of animation timelines would look like this:


@keyframes mysimpleanimation {
    0% {background-color:#f00;}
    100% {background-color:#00f;}
}

As you might guess, the above animation is designed to change a red background to blue. However, by itself, this code won’t do anything until we apply it to an HTML object on screen. We also need to set the time length of the animation (in seconds).

Here’s how to include a time length within your CSS:

#example {
  ...
  animation: mysimpleanimation 4s infinite;
}

Here’s that simple example in action.

We’re free to create as many points along the timeline as we’d like — for instance we can change our animation to green (#0f0) at the halfway point by adding the following line:


@keyframes mysimpleanimation {
    0% {background-color:#f00}
50% {background-color:#0f0}
    100% {background-color:#00f}
}

There are a slew of other well-documented attributes and settings, but we’ve covered the fundamentals you’ll need for now.

Working with CSS3 Sprites

Note: For all the demos in this article, I’m going to be using Lea Verou’s super-lovely Dabblet webapp. Apart from looking luscious and being hard-wired straight into Github, Dabblet will let you fiddle with the CSS here and see the results in real-time. The other great feature of Dabblet is that it let’s you write plain, unprefixed CSS3 while magically adding the browser-specific code in the background (using Lea’s “-prefix-free“).

This is brilliant for keeping the code examples simple to read, but be aware that you’ll need to add the “-moz,” “-webkit,” and “-ms” prefixes to make this code work outside of Dabblet.

So, despite only really having one good trick — transitioning between CSS properties along a timeline — CSS3 keyframes give you many of the basic building blocks of animation including:

  • Squash and stretch (in CSS: width, height)
  • Rotation (transform:rotate)
  • Position in space (left, right, top & bottom)
  • Color and opacity changes (color, background-color, opacity)
File a Bugs report?

Traditional cel animation (or perhaps the first Bugs report? #dadjoke)

This covers a lot of potential animation scenarios, from bouncing balls to spinning tires to launching rockets.

However, it’s not so obvious how you would, for instance, manipulate CSS properties to make:

  • a wriggling snake
  • flapping wings
  • a spinning earth
  • a running man

Classic cel (or sprite) animation is one common answer to that problem. We’ve probably all seen the old Disney or Warner Brothers hand-drawn “cartoon cels.” Each moment in the timeline is drawn as a standalone image. Then each cel is displayed rapidly in succession to give the illusion of movement.

So, how do we do that with CSS3?

Let’s begin with a simple cel animation that we can develop into something more sophisticated. I’m starting with single .png showing a hand-drawn flame over three frames. It won’t be hard to make this image loop seamlessly.

3 frame flame

Our 3 frame flame cel

Each flame cel is 136px high by 100px wide.

The Setup

Here’s the basic HTML framework that I’m starting with:

I’m using a container <div> (#logfire) to hold the background graphic and a placeholder <div> (#flamegroup) that will hold our flames in position. Inside the placeholder I’ve nested a third <div> (.flame) that will become our flame.

I’ve embedded the flame graphic into the background-image, but we’re cropping off most of the image, only allowing the left-hand flame to show.

  .flame {
     width:100px;
     height:136px;
     background:url(flame.png) left top no-repeat}
The basic scene

The basic static scene

Here’s our starting code. 

Setting Up Our Timeline

To make a seamless loop, we’ll need to create a 4-frame loop — that is, we’ll show frames 1,2,3, and 2 again, and repeat. We could represent that sequence along our invisible timeline as positions 0%, 25%, 50%, 75% and 100% (i.e. back to frame 1). At each stage we reposition our background at the next frame, so our CSS might look something like this:

@keyframes flicker { /* flame pulses */
  0% {
    background-position:0px 0px;
  }
  25% {
    background-position:-100px 0px;
  }
  50% {
    background-position:-200px 0px;
  }
  75% {
    background-position:-100px 0px;
  }
  100% {
    background-position:0px 0px;
  }
}​

Then, we could attach our “flicker” animation to our flame class and set it to loop every 1.1 seconds:


.flame {
  ...
  animation: flicker 1.1s infinite;
}

Fantastic!  Except for the part where it doesn’t look quite right.

As the above example shows, it animates too nicely, gracefully sliding between frames. Just like those little page-flick animations you used to make in the corners of book pages, the magic lies in snapping each new frame into place so quickly that you can’t see the in-between state.

Our new jagged timeline now looks like this:


@keyframes flicker { /* flame pulses */
  0% {
    background-position:0px 0px;
  }
  25% {
    background-position:0px 0px;
  }
  25.1% {
    background-position:-100px 0px;
  }
  50% {
    background-position:-100px 0px;
  }
  50.1% {
    background-position:-200px 0px;
  }
  75% {
    background-position:-200px 0px;
  }
  75.1% {
    background-position:-100px 0px;
  }
  100% {
    background-position:-100px 0px;
  }
}​

As you can see, our first cell stays motionless from 0%-25%. Then suddenly at 25.1% along the timeline, we crank the background 100 pixels to left. Again we leave it there till the 50% mark before cranking it left again at 51.1%.

I think you get the idea.

If we plug that CSS into our example we now get a pleasantly wiggling flame.

Add a Pulse

Flames don’t just move, they vary mildly in intensity. Since we already have the timeline set up, why not tweak the opacity a little to give it a subtle pulse?


@keyframes flicker { /* flame pulses */
    0% {
        background-position:0px 0px;
        opacity:.8
}
    25% {
        background-position:0px 0px;
}
    25.1% {
        background-position:-100px 0px;
}
    50% {
        background-position:-100px 0px;
        opacity:.4
}
    50.1% {
        background-position:-200px 0px;
}
    75% {
        background-position:-200px 0px;

}
    75.1% {
        background-position:-100px 0px;
}
    100% {
        background-position:-100px 0px;
        opacity:.8
}
}

Here’s the result.

A decent result, but let’s face it: We could get something pretty close to this with a good animated .gif file. Let’s ratchet things up a little.

Fan those Flames

Circling back to our original HTML, we’re going to add more flame elements — lucky we have that handy holder <div>, huh?

Let’s add two more <div> elements with the “flame” class to our “flamegroup.” We’ll also give each <div> it’s own ID so we can target them separately.


We now have three flame elements, but since they’re dancing in perfect unison, they still look like a single flame. Let’s delete the animation from the “flame” class and add it to our new IDs.


#fl1 {
   -webkit-animation: flicker .7s infinite;
}
#fl2 {
   -webkit-animation: flicker .7s infinite;
}
#fl3 {
   -webkit-animation: flicker .7s infinite;
}

Now we are free to call the same animation (i.e. “flicker”), but we’ll give each one a different timing to keep things interesting. Riffing on the prime number idea that we used in the cicada principle article, I’m going to set the flame elements to loop in 0.7, 1.1 and 1.3 seconds respectively (yes, I know they’re not primes).

Finally, rather than stacking them precisely on top of each other, I’m going to offset each flame slightly (10-20px) from its siblings. This is mainly so we can see the edges mingle and interact.

Here’s the CSS:


#fl1 {
animation: flicker 1.3s infinite;
top:-10px; /* offset */
left:10px /* offset */
}
#fl2 {
animation: flicker .7s infinite;
top:10px; /* offset */
left:20px /* offset */
}
#fl3 {
animation: flicker 1.1s infinite;
}

And here’s the final demo in action.

A screenshot — although the static image doesn't do it justice.

A screenshot — although the static image kinda defeats the purpose.

Our flames jump, dance and weave in a soft, less “GIF-loopy” manner. Sometimes you think you have the pattern nailed but then it seems to shift. Dabblet lets you tinker, so feel free to play with the timing. Obviously the slower the loop, the longer between exact pattern repeats.

There are a few points to be aware of:

  • As I mentioned earlier, the Dabblet examples use “-prefix-free” to let us write clean, unprefixed CSS3, but you’ll need to add the “-moz,” “-webkit,” and “-ms” prefixes to make this code work outside of Dabblet.
  • I wanted to keep things relatively simple for the example, but we could easily spice things up a little more by setting background-size height to 100%, and then animating the height of our flame <div>. This would make our flames leap and dive more energetically.
  • Obviously, older browsers won’t support CSS3 animation, but Modernizr.com allows you to target those browsers with a fallback. For older browsers, rendering the flames statically seems to be a perfectly acceptable option. In fact, it’s hard to imagine how an IE8 user would ever become aware that other users were seeing an animation.
  • You may sometimes see a slight “jog” or flicker randomly in the animation. It seems to be a browser buffer overflow bug, but so far I haven’t found a way to eliminate it, although slower computers seem to make it worse.

We’ve also seen some evidence of our processor cycles spiking during *some* larger CSS3 animations. It seems to be linked to particular types of animation, but I haven’t isolated the key problem areas yet. Suffice to say, if you hear your CPU fan kick into overdrive, you’ve probably hit on the same issue.

Summing Up…

I think this idea of weaving together animation timings has some interesting potential, so watch this space in the coming weeks for some more experiments. Subtle sunlight, water, and clouds effects come to mind.

So, what do you think?

And if you have any ideas of your own, make a Dabblet and share them here.

UPDATE: March 1st, 2012 

Reading up on the CSS animation spec, I’ve found there’s a better way to control the sprite switching. The @keyframe syntax has a reasonably rarely-used attribute called ‘step’ that allows you to control how many ‘inbetween’ animation states are generated by the browser. It looks like this in it’s unprefixed state:

 animation: animation_name 1.1s steps(3) infinite;

This code would generate three stages in-between the steps that we’ve manually set out in our @keyframe code.When using sprites, we don’t want show ANYTHING between changing sprites so we just set our steps to 1.

animation: flicker 1.1s steps(1) infinite;

This allows us to greatly simplify our flame timing loop. Here’s an updated Dabblet showing it working.

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.

  • Andreas Becker

    hihi so much code compared to flash!

  • Reno

    Andreas, I agree this is a LOT of code for a simple fire animation, which can be quickly done in Flash, plus 95% of all browsers (regardless of browser version) has flash installed.

    Where CSS3 and Internet Explorer doesn’t mix, no surprise there really.
    Nevertheless, CSS3 leaves way more options with overlapping content and placing animations on top each other, stack them etc. where Flash is limited by the container.

    And the days of having a complete site in Flash are clearly over.

    Good article, keep ‘em coming.

  • http://trailsventures.com Soyeb Ahmed

    Very Good Article.

    i agree that here is lot of code for a simple animation.

    but Good News is that we can animate sprite like flash in CSS3.

    your article is rocking Alex. i like that.

  • http://alexmwalker.mp/ Alex Walker

    @andreas and @reno: Fair comments, but for me personally, the time of Flash for this type of thing has passed — for a handful of reasons.

    Firstly, I know not everyone does this, but personally I block Flash by default. Too many ads networks abuse it, so I’ll manually enable it on a site-by-site basis.

    Secondly I agree with your point about the limitations of the Flash container interacting with the rest of the page elements. In fact it has the same design issues as IFRAMEs.

    Lastly, the audience of iPads, tablets and phones become bigger and more critical everyday and you’re not touching that market with Flash. I can’t make CSS3 work in older IEs but I can make it fallback elegantly. It’s much harder making flash degrade elegantly in IOS.

    Going forward, if it comes down to iPad vs IE6, which way are you going to go?

  • Michel Cote

    Great article Alex – thanks for the thorough description.

    I thought IE9 was supposed to support CSS3 – your sample flames don’t work on my 2 win 7 machines with IE 9 (latest updates installed). Would this be a dabblet issue or IE9?

  • http://alexmwalker.mp/ Alex Walker

    @Michel: AFAIK the -ms-animation stuff has only been supported since the IE10 betas.

    IE9 brought support for tonnes of CSS3 stuff previously ignore by Microsoft, but there were some holes — text-shadow was a big one, and keyframes was another.

  • http://trailsventures.com Soyeb Ahmed

    Yeah Alex, we can find more holes for CSS3 properties support in IE9 version.

    http://www.impressivewebs.com/css3-support-ie9/

  • Andrew Nwani

    Cool!

  • http://herozon.com Zarel

    Wohoooooooo… CSS3 in action! Yeahhhh!

    I Love it!

    Thanks, Alex! :)

  • Oswald
  • kay

    Awesome!
    That’s what I need for my small mobile app. I am a NuB though.

    Could you provide a real world example for Mobile Safari and Android Browser? Thank you