Smooth state animations with animation-play-state

When a CSS animation is applied from the beginning of the page load, things are easy. You just use the animation property with appropriate parameters, and you’re done. However, what if the animation is applied on a certain state, e.g. :hover, :active, :focus or a JS-triggered class change?

A naïve approach would be to try something like this:

However, this means that when you hover out of the element, it abruptly snaps to its original state (no rotation). In many cases, it would be a more desirable to have it freeze in the last shown frame, until we hover over it again. To achieve that, we can apply the animation from the beginning, with animation-play-state: paused; and just change it on :hover to animation-play-state: running;. This is what happens then:

I figured this out when I was recently helping my good friend Julian with his one page website*. When you hover over the figure, it starts scrolling, but when you hover out of it, it doesn’t snap back to its original position, which would’ve looked awful.

*Beware it’s still a bit rough around the edges, e.g. the result has some rendering bugs on Firefox & IE plus some unsupported features messing it up (e.g. baseline-shift in SVG), but those are for another day as I had work to do and this ended up taking longer than the few hours I expected. Beyond the animation, you might want to explore the CSS-only buttons (see what I did there?) or the leather figure frame. Credits to Laura Kalbag for the tweed background & color scheme. I also experimented with SASS on this one and found it much smoother to work with than LESS, so I might stick with it for those cases where I need a preprocessor.


  • Would be better if the animation do a turn and then stop. Is this possible?

    • You mean to keep turning while you’re hovering and when you hover out do a full turn max, then stop? Can’t think of a way right now, but that doesn’t mean it doesn’t exist. Perhaps via changing animation-iteration-count from infinite to 1. But then it will make 1 spin in the beginning as well… Need to think more about this, thanks for the challenging question!

      • For simple animations adding a “transition” to the default state can at least prevent an abrubt end of the animation and make it almost look like the animation is completing itself?

        • Obviously, if you can do what you want with a transition, it’s always better to do it with a transition.

        • vinaynb

          Do we have any the solution to the question problem suggested by Edmundo now ?
          Stuck with a similiar situation

        • vinaynb

          Got a solution 😀

  • Christian Heilmann

    This is very, very cool. One thing that always annoys me about this kind of animating though is that the boundary of the element rotates, so it is very simple to leave the element and stop the animation without wanting to. That’s why I always apply the hover to the parent element so that the interactive boundary box for the hover never changes, no matter what the transformation of the element is.

  • Small observation, on my iPad mini the first example spins when putting my finger over it and holding but it’s pretty hard to interact with the text. The second example doesn’t spin, but I’m able to interact with the text.

    Kind of a cool progressive enhancement. Although if I wanted to copy the text from the first one on a desktop I’d be out of luck.

    • The spinning is just a really obvious animation, for demonstration purposes. The point of this post is not spinning animations, neither should you have such a hover effect on a real website.

      • Totally agree, I was just observing how the interaction of the content in the div varied.

  • AFAIK, It’s still kind of hacky to make the animation play in reverse when the mouse leaves the element, I wish there was an easier way…

  • Capotea

    Try stylus. All the features sass has and more, freedom in what kind of syntax you prefer, and holy shit transparent mixins! And it’s in JS! I honestly don’t know why people use sass when you have stylus.

  • Ariel Infante

    Thank you for the great tip.

  • Pingback: Tweet Parade (no.2 Jan 2014) - Best Articles of Last Week | gonzoblog()

  • Pingback: Weekly Recap |

  • new property … hihihi
    ty ty

  • Ilincescu Mihai

    Maybe is a silly question but I don’t get it why on the website the body has it position: relative; ? I’m just curios 🙂

    • Click on “Talks”, then toggle position:relative on the body through the dev tools and you’ll see why.

  • discoliam

    Would there be a way of adding an ease in when you start the animation again on hover? I’m playing with a similar rotation and can only get it to ease at the beginning/end of a full turn, not when you hover in/out.

  • Pingback: CSS Masters Israel | איך לשמור על STATE באנימציות של CSS3()

  • Pingback: Gestire gli stati delle animazioni con animation-play-state | Laboratorio CSS()

  • KyleBavender

    1. Thank you for this demonstration, Lea! Great technique and I plan to use. However…
    2. Chrome v33, OSX (current stable) is playing the animation at load despite “`animation-play-state: paused“`. Shoot.
    3. Further examination: My own usage plays at load, your dabblet example plays at load, but Julian’s image gets the pause. Now that’s all kinds of confusing.

    Do you have any ideas? Am I thinking about this wrong or missing something?

    • Steven

      Chrome requires the -webkit- prefix for animation play state.

  • Lionel

    @LeaVerou:disqus once again thank you for your teachings! I just used this approach in a little project I made. CSShake:

  • maxw3st

    Julian’s site is a great demo for paused/interpolated animation & image masking. The buttons work great also as intuitive ui elements.

  • Joy

    I’ve read a couple of your posts today, and I just see a white box for the end result (second example above) using the latest release of SeaMonkey, which is 2.30. Normally everything Firefox does is fine in SeaMonkey, since they have the same basic rendering engine.
    Also, on Julian’s site, the dashed border around the figure:after show as solid. When I use Firebug to disable the border-radius, it looks dashed.

    • No idea about the first problem, but the second is a known Firefox bug with border-radius and dashed/dotted borders: The curved part shows up as a solid border. :/

  • Pingback: All the way up remix()

  • Pingback: opulence for life winter vee()

  • Pingback:

  • Pingback: Term Life Insurance()

  • Pingback: ankara escort()

  • Pingback: leagal steroids()

  • Pingback: locker codes()

  • Pingback: buy steroids in canada legally()

  • Pingback: hampton ceiling fans()

  • Pingback: game subway()

  • Pingback: kalyan matka()

  • Pingback:

  • Pingback: Life Insurance No Medical Exam()

  • Pingback: handmade furniture()

  • Pingback: trenadex acetate 100()

  • Pingback: injectable steroids()

  • Pingback: drostanolone propionate()

  • Pingback:

  • Pingback: more()

  • Pingback: èíôî()

  • Pingback: what is an hcg diet()

  • Pingback: information()

  • Pingback: 1 test cypionate()

  • Pingback: abbott healthcare pvt ltd indi()

  • Pingback: buy steroids()

  • Pingback: anabolic()

  • Pingback: more info()

  • Pingback: rohm labs()

  • Pingback:

  • Pingback: buy steroids new zealand()

  • Pingback: child pornography steroids()

  • Pingback: anavar for women()

  • Pingback: steroids()

  • Pingback: Stephani Mitchel()

  • Pingback: ostarine kopen()

  • Pingback: antibiotika augmentin alkohol()

  • Pingback: ectomorfo antes y despues()