Understanding web animations: a half-baked history
A long-winded and uninformed walk-through of web animations through the ages.
Picture the scene: you like your site, but it’s a bit bland. You want it to POP. You start by adding a few simple CSS animations to your buttons. They look pretty good. Then you decide you want to have nice page transitions. No problem, there are lots of libraries to help with that — you pop one in. Maybe you should animate your landing page? In fact, why not have each element animate at different times. Hmmm, might need some JavaScript for that. OK, well that works but let’s try it out on my phone …
Soon it’s 3a.m. and you’re knee-deep in the Chrome Dev-Tools trying to make sense of your pathetic 3fps page-load experience. You haven’t seen your friends or family in days.
The point is, once you get beyond simple fade-ins, animations on the web are hard.
The goal of this post is to try give you the context as to why they’re hard, as well as a brief history and run-down of the available approaches to tackling animation.
This post won’t give you a detailed explanation of how each approach works, but it will give you the understanding you need to be able to handle animation on the web.
Overview
To get a good overview of where we currently stand with browser animations, here’s what we’re going to look at:
- Flash 😱
- SMIL (“smile”)
- CSS Transitions and Animations
- JavaScript animations
- Web Animation API (WAAPI)
- 3rd party animation frameworks and libraries
For context, let’s begin with the most infamous (and now defunct) approach.
Flash
Back when browsers were still at war, there weren’t many cross-platform standards to point to. The web was young and experimentation was rife. Developers wanted to be able to create interactive websites and games, whatever the cost.
Flash was the answer. It was a proprietary, closed-source application (eventually owned by Adobe) that allowed developers to build these interfaces and games and then dump them into the browser via the <object>
tag and a browser plug-in. The browser could interact with them but it was the responsibility of the installed Flash plug-in to run everything. Essentially the browser was outsourcing its job to Flash.
While Flash was a great technology, it went against the core principle of the web; to be open and accessible. As the web matured, a slew of new standards and specs began to take shape introducing the ability to do many of the things Flash could do, but natively via the browser. At the same time, smartphones came along. Apple famously didn’t want to support Flash in their iPhone and the writing was on the wall.
To cut a long story short, Flash support has been deprecated on all major browsers and Adobe will be putting it out to pasture in 2020. RIP.
So how do we animate on the web without Flash?
SVGs and SMIL
Flash provided the ecosystem for creating animations that could be plugged into the web, but the core component at the heart of these animations was vector graphics.
Vector graphics are resolution independent image files. In contrast to raster image formats such as pngs
jpgs
and gifs
, vector graphics are made up of paths and shapes instead of pixels. These resolution-independent elements are perfect for icons and illustrations, particularly on todays high-resolution screens.
While Flash was devouring the web in the 2000s, the SVG standard was being built to support open vector graphics in the browser. Although these weren’t well supported until the 2010s, they provided the bedrock for high quality graphics and animations via the .svg
file format and <svg>
HTML element.
You are probably familiar with SVGs. They are based on XML, so they look very like our regular HTML:
<svg>
<g>
<rect ...>
<path .. >
<line ...>
<circle ...>
</g>
</svg>
Around the same time SVGs were being standardised to handle vector graphics, we were also looking for ways to standardise multimedia on the web in general. Propriety plug-ins were handling almost all of the audio (remember RealPlayer?), video and animation on the web and this was becoming a closed-source quagmire.
To handle this, the SMIL (“Synchronised Multimedia Integration Language” 🤷🏻♂️) standard was developed. Pronounced smile, this was an ambitious standard aimed at providing the framework for delivering video, audio and animations in a more open, user-friendly and accessible way. If HTML allowed you to deliver a document to the browser, SMIL would allow you to deliver and coordinate multimedia to the browser.
Included in the SMIL standard were the tools needed to animate SVGs. To do so it augmented SVGs with animation-specific elements to be used alongside the regular shapes and paths:
<animate>
<animateMotion>
<animateTransform>
- …
These allowed you to do complex and coordinated animations within a single SVG file.
In summary, SVGs and SMIL give you the toolset to create Flash-like animations in the browser using an open standard. But what are its shortcomings with regard to animations?
- Firstly it’s a huge and complex specification. It’s scope is much larger than just animations so getting started is hard.
- It’s obviously limited to animating SVG elements. You can’t use it to animate other elements on a page.
- Although SMIL still has good support, there are rumblings of it being phased out in most browsers.
So while it’s still around in 2020, it’s probably best to look beyond SMIL for animating on the web.
The good news is that you can still animate SVGs without SMIL using the other APIs and technologies that we’re going to discuss below.
CSS Transitions, Animations & Transforms
As the web found its feet in the mid-2000s, one of the big leaps forward was the introduction of both HTML5 and CSS3 (~2005). These remain the latest releases of the HTML and CSS standards and they introduced a slew of new features that helped unite web technologies across browsers.
One of the most anticipated features was the joint-introduction of CSS animations, transitions and transforms via the following properties:
transform: ...
transition: ...
animate: ...
+@key-frames
These three additions paved the way for native animations in the browser and are the go-to for most developers these days that want to get started with browser animations.
Because they were introduced at the same time, they are often assumed to all be required when animating but this is not the case. It’s vital to understand these as 3 distinct (albeit related) features.
Let’s have a look at them in a bit more detail.
CSS Transforms
The transform
property allows you change the appearance of an element. For example you can:
- Scale
- Skew
- Translate
- Rotate
Just like you would an image in Photoshop.
Here’s the important part: on its own, a transform
has absolutely nothing to do with animation. Transforms can be animated but they have nothing to do with animation themselves.
CSS Transitions
The transition
property is the baby-sibling in the CSS animation family, distinct from its older siblings animate
and @key-frames
.
Let’s drop the metaphor and be clear: transitions and animations are separate features to be used in different circumstances.
Transitions allow you to define an animation that runs when a particular property on an element changes, for example:
.truck {
transition: transform 1s ease-in;
}
.truck.drive {
transform: translate3d(100%, 0, 0);
}
Here, when we add the .drive
class to our truck the browser applies a transform
that moves the truck 100% to the right.
- How is this transition triggered? For the transition to begin, our element needs to have the
.drive
class applied. We could use pseudo-selector like:hover
in our transitions, but usually these triggers are applied via JavaScript. Either way, transitions require a trigger to run. - What if we want this to run more than once (i.e. loop)? We can’t do this with transitions. Transitions only run once, in response to their trigger.
- What if we want intermedia “stages” to our opacity transition? For example, maybe fade-in to
.2
opacity, hold there, then finish fading-in to1.0
opacity. We can’t control that with transitions. Transitions only have two “states”.
The implication of this is that transitions are useful when you want to animate something that has a definite start and end-state and only needs to run once when triggered.
CSS Animations
The animate
property and @keyframes
at-rule address some of the questions left unanswered by transitions, but remember that they are a distinct feature meant for different things.
While transitions are good for one-off animations these are used for creating multi-stage animations that run more than once.
What does an animation look like?
.wheels {
animation-name: spin;
animation-duration: .3s;
animation-iteration-count: infinite;
animation-timing-function: linear;
animation-direction: normal;
}
@keyframes spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
- In contrast to a transition, this animation begins immediately when the page loads. We could add the
animation
property to a triggered class or pseudo-selector, but we don’t have to. In other words, animations can run without a trigger. - The next thing to note is that our animation here can have >2 states. We can add as many states as we want in our
@keyframes
rule to give us really fine-grained control over how the animation progresses. Animations can have multiple states - We can also control how many times this animation is run. In our example it will spin indefinitely as we’ve indicated
infinite
as the interaction count. Animations can run repeatedly - Finally it’s worth understanding that we could write an animation that does exactly what our transition does. It would be a bit more laborious but you can write all transitions as animations (but not visa-versa).
The bottom line here is that CSS transitions and animations are complimentary features to be used in different scenarios.
CSS transitions allow you to write quick animations that run in response to a trigger while CSS animations give you more powerful control over the progression of an animation as well as how it executes.
Now that we’ve looked at what CSS transitions and animations can do, let’s look at what they can’t do.
Timelines, Control and Coordination
OK, so what if we want to do more than just one-off or repeating animations? How can we control and coordinate with CSS animations? Spoiler: this is where the wheels come off.
How do we sequence multiple animations along some sort of timeline? There arenative events for animations and transitions, like animationend
and transitionend
but they are limited and we have to write our own JavaScript to time everything.
How do we dynamically play or pause animations? We canuse the animation-play-state
CSS property to wrestle some control over state, but again we have to write the skeleton code.
How do we get information about an animation’s current state? We can use the animation-play-state
as mentioned above, but what about finding out exactly what timing the animation is currently at? No can do.
How do we change our animations in response to user input? With great hardship and hackery.
How do we seek to a particular part of an an animation?
You get the point — when it comes to creating more complex animations there are many limitations with the CSS approach that leave us short of a well-rounded solution.
JavaScript Animations
To deal with these limitations, let’s take a radical step and abandon CSS animations and transitions altogether for the time being. We’ll go back in time to the early days of the web and look at how we build animations manually in JavaScript.
So where do we start? To be able to animate with JavaScript we need timing functions … lot’s of timing functions.
Using setTimeout
and setInterval
JavaScript offers two functions that help us create animations:
window.setTimeout
window.setInterval
These let us set individual timers and repeating timers. We can use these functions as the building blocks of our animations.
Here is the same fade-in animation using CSS and JavaScript:
.square {
opacity: 0;
}
.square.fade-in {
opacity: 1;
transition: opacity 30ms linear;
}
var play = function() {
var el = document.getElementById("square");
var opacity = 0;
var timer = setInterval(fadeIn, 30);
var fadeIn = function() {
if (opacity > 1) {
clearInterval(id);
} else {
opacity += .1;
el.style.opacity = opacity;
}
};
}
play();
Although these do the same thing, they look very different. To begin with, it’s much harder to read the JavaScript animation at a glance and there’s much more code involved. Why?
Well with JavaScript we are telling the browser exactly how to perform the animation — we’ve written the implementation ourselves. With the CSS transition, we outlined what our animation should do but we didn’t say how to actually do it — we left that to the browser to actually implement behind-the-scenes.
This is an important distinction that begins to illustrate a fundamental difference between CSS and JavaScript animations that we’ll come back to in a bit.
Control?
So far, we’re not doing any better using JavaScript than native CSS transitions and animations. We want a timeline, we want synchronisation and coordination, WEWANTCONTROL.
By default, JavaScript doesn’t give us any of the abstractions we need to control our animations but that’s not to say we can’t create them ourselves.
We could use JavaScript to start developing our own API:
class Animation() {
constructor() {}
play() {}
pause() {}
reverse() {}
}
class Timeline() {
add() {}
remove() {}
}
OK, this is looking good, but it’s going to be a huge amount of work to actually develop this out.
Thankfully there are many people who have blazed a trail before us. Many mature 3rd party libraries already exist that provide entire animation ecosystems that we can use on our project.
We’ll touch on some of these options later, but the point here is that JavaScript isn’t great at allowing us to define animations (they become hard-to-read and tedious to write) but it certainly gives us the tools to control them once they are up and running.
The Big Issue: CSS vs JavaScript
Hopefully you’re starting to see the friction arising here between CSS and JavaScript.
CSS is a great place to define all of our animations but it’s not the place to control animations. JavaScript on the other hand isn’t a great place to define our animations but it is a great place to control them.
Why is this friction arising? Essentially it’s because:
- CSS is declarative, JavaScript is imperative
- CSS is concerned with presentation, JavaScript is concerned behaviour.
What does this mean? In JavaScript we focus on telling the browser how to do things: “if the user clicks this, do that”. This is imperative: we give the browser the set of steps and control logic that it needs to carry out our tasks. In CSS however we focus on telling the browser what to do: “Make this <div>
blue (but I don’t care how you do it)”. This is declarative.
A full animation solution needs to be both declarative AND imperative. It should be concerned with presentation AND behaviour.
This is the fundamental reason animations in the browser seem so complex. Animation sits right in the middle of where CSS and JavaScript are designed to overlap. For the perfect solution you need to be able to easily define your animations but you also need to able to intuitively control them. This should happen through the same API, not using a cocktail of CSS and JavaScript.
Thankfully there is a newishAPI that does exactly this: the Web Animation API or “WAAPI”.
Web Animations API (WAAPI)
WAAPI is a relatively new browser standard created to address the friction we’ve encountered with each of the animation approaches we’ve discussed so far.
The intention of this standard is to encapsulate all the above approaches and give us a common language to describe and control animations in the browser:
“CSS Transitions, CSS Animations, and SVG all provide mechanisms that generate animated content on a Web page. Although the three specifications provide many similar features, they are described in different terms. This specification proposes an abstract animation model that encompasses the common features of all three specifications. This model is backwards-compatible with the current behavior of these specifications such that they can be defined in terms of this model without any observable change.”
It gives us the ability to declaratively define our animations in a CSS-like fashion, while providing the abstractions, functions and methods we need to properly control our animations in JavaScript.
At a high level, the WAAPI distils everything we’ve discussed so far into three concepts:
Timeline
the ability to sequence animationsAnimation
the ability to control animationsEffects
the actual animations themselves
These concepts are backwards compatible, so they should conceptually map onto CSS transitions and animations as well as SMIL.
In practise, the API introduces the following objects and methods to the browser ecosystem:
- A
document.timeline
object - An
animate
method on DOM elements Animation
objects (returned from the aboveanimate()
method) that have control methods such asplay()
andpause()
Effect
objects that allow you define your requested animations
What does this mean for us, the developer, trying to get our head around web animations?
- We no longer have to work in the declarative world of CSS — we have moved our animations out of the presentation layer, into the behaviour layer.
- We gain the performance benefits of working directly with the browser’s animation engine
- We can stop/start animations
- We can check animation state
- We now have a timeline
- We can group and sequence animations
So this new standard gives a fully fledged, native animation framework to work with, without having to go to a 3rd party library for emotional support.
What’s the bad news? The bad news is that this is a relatively new standard and therefore doesn’t have full support. Furthermore, only some of the API has been built-out so far, so some of the more advanced features are still a little while off (animation motion along a path for example).
But it’s a great start for unifying all of the animation concepts into a native browser standard that we can all understand.
Animation Frameworks
With the Web Animations API still in the works, for complex animation we may want to fall back to tried-and-tested 3rd party libraries.
For this there are many options. Some are niche and cover particular requirements while others are broad, giving you the framework and tools to do what you want.
Here are a couple of popular examples:
- GreenSock — The de-facto animation library for complex animation. Gives you everything and the kitchen sink.
- Animé.js — Another fully featured “engine” that provides most of what you need for complex animation.
- Velocity.js — Lightweight library for more basic jQuery-like animations.
- Popmotion — Another feature rich full animation environment.
- ScrollReveal — An example of a niche animation library for animating on window scroll
The important thing to note is that all these libraries use one or more of the approaches we’ve mentioned: fully custom JavaScript, CSS transitions/animations, WAAPI or a mix of all three.
The obvious downside of using these libraries is that they introduce a dependency into your project and a conceptual overhead. On the upside, they add a common abstraction on top of all the approaches we’ve mentioned today, making it easier to get started.
Summary
Whew. In this post we’ve gone through a brief history of animations on the web as well as outlining the pros and cons of each of the currently recommended approaches. More importantly we’ve identified why animation is difficult in the browser. We’ve also looked at some of the libraries available to you if you want to build on the shoulder of giants and finally we’ve gone through some of the more advanced APIs you might want to make use of when pushing the browser’s boundaries.
Hopefully this has helped untangle animations in your mind and given you a solid footing going forward.
Resources
SMIL
- An interesting historical look at SMIL from A List Apart in 2000
- A second piece from 2000 regarding SMIL
- Mozilla’s practical guide to animating with SMIL
- A good overview of SMIL via CSS Tricks
- Another good guide to actually creating SMIL animations
- The actual SMIL spec itself
- A guide to moving away from SMIL via CSS Tricks
CSS Transitions and Animations
- Mozilla Guide to Transitions
- Slightly one-sided “CSS vs Javascript animations from the author of GreenSock”
- Controlling CSS animations
- CSS animation performance tips