Meaningful Motion with Action-Driven Animation
How do we animate interfaces in ways that are not just beautiful, but meaningful? When we add motion to interfaces, we want to in one way or another improve the user experience, be it through aiding the comprehension of a concept, setting the mood, enhancing the perception of speed, or directing attention. Regardless of the intent of the animation, when animations fail to be meaningful, a common cause is that they simply tween between their hidden and visible states, rather than visualizing the actions that triggered the change of state. A window rarely just closes or opens; a message is sent, a draft is discarded, an item is used.
We can call these two ways of animating state-driven animation and action-driven animation. By applying action-driven animation you can catch yourself in the act of creating an animation that’s not as meaningful as it could be. Are you simply transitioning between states, or are you visualizing actions? Meaningful motion is about clear and engaging storytelling, and we can apply action-driven animation to remind ourselves when we’re straying from that path.
Let’s take a look at a basic example of state-driven animation vs. action-driven animation: interacting with a modal. This is a simulation of state-driven animation being applied to a modal:
As the modal appears, it fades in. Whatever button is pushed, it fades out again. What’s wrong with this animation? Fading between hidden and visible doesn’t communicate what’s happening on screen, other than emphasizing that an object is being shown or hidden. The user is also triggering an action. Rather than only morphing between states, we can use motion to reinforce what action is being triggered.
Here’s is a simulation of action-driven animation being applied to the same modal:
Try to ignore for the moment the aesthetics of the two: what does the second animation convey that the first one does not? In the first example, we use two different animations with state-driven animation: fading in and fading out. Clicking Cancel and clicking Do it both trigger the fade-out animation. In other words, the only thing we’re differentiating between is the states of the modal: hidden vs. visible.
On the other hand, in the second example, we’ve got three different animations, and we differentiate between the two actions by playing different animations depending on the chosen option. On Cancel, we clearly show that the modal is being canceled by scaling it down and fading it out, sending it back to where it came from. When the affirmative action is selected (Do thing), we do the opposite: scale it up and fade it out, bringing it closer to the user. In other words, we’re not only differentiating between states but how you travel between those states, i.e., what actions are being performed.
It’s common to think of apps as a series of views or states, and animations as ways to travel between those states. Take this Email app and two of its states:
It’s easy to forget to show how these states are connected and always use the same transition to animate between states, regardless of the action being taken. Rule of thumb: if your methods are called something like
hideWindow(), or if you’re only animating
opacity, you’re leaving your users in the dark to figure out exactly what happened. They’ll see that something changed, but not what caused that change. It’s sort of like leaving a party without saying goodbye; the host won’t know if you hated the party or just had to run home to do your laundry.
Action-driven animation elevates the connections between the views to become the plots of the storytelling arhcs. In other words, what took you between state and A and state B? In our Email app, you can get between state A (composing visible) and state B (composing hidden) in at least two different ways: sending an email or discarding an email and closing the window.
You’ll make your app easier to understand by visualizing whatever caused the change of state. Creating a unique animation for every action can help users differentiate between different action paths and help them intuitively gain a deeper understanding of the events unfolding on screen.
When state-driven animation fails
A while back, just before giving a talk on UI animation at a conference in Prague, I was stumped by an animation used by Tumblr. Before continuing, I want to emphasize that I have a deep respect for the Tumblr design team—they’ve produced some admirable designs. I have featured their work as examples to be inspired by on several occasions. That said, something went wrong in this specific instance.
I had just signed up for a new Tumblr account and was getting ready to make my first post. But for some reason, whatever I posted kept disappearing as I hit Post:
Or, as you can see towards the end of the clip, that’s not at all what happened. Whatever option I selected, Close and Post both triggered the same animation (except for a spinner showing up briefly inside the Post button when I selected Post). Nothing hinted that I had successfully posted something. The post section just disappeared.
What’s gone wrong here? At first sight, this animation looks meaningful and helpful. But again, we’re just morphing between two states rather than visualizing the actions unfolding on the screen. Cancelling a post and successfully posting are two fundamentally different actions, yet they share the same animation. In this instance, state-driven animation doesn’t just fail to be meaningful—it’s causing confusion and obfuscating what’s happening.
To be fair to Tumblr, the issue is in this instance is being amplified by the Dashboard onboarding that’s floating just below the navigation, pushing down new posts below the fold. But regardless of the unlucky circumstances, we can improve the animation by moving from state-driven animation to action-driven animation. Rather than only transitioning between the two states of the posting section (visible and hidden), the action of posting could be associated with an animation that clearly transitions between you writing a post, to the post being published.
Applying action-driven animation
So how do you go about using action-driven animation to improve the animations of your product? I’ve mocked up a dummy email app (shown previously in the email illustration above). Let’s look closer at the actions related to working with a message and move from state-driven animation to action-driven animation together.
Let’s list all important actions in the part of the app we’re working with, rather than all views. There are only two views involved in working with a message in our Email app, but four different actions. You can:
- Create a new message
- Send the message
- Discard the message
- Close the message and save it as a draft
If we just faded between the different views, this is what it would look like working with the app (going through the different actions in the order above):
To create more meaningful animations, we need to look critically at every fade and ask ourselves: what actions are being taken?
Choosing a storytelling axis
Before we create a unique animation for every action, let’s settle on a primary axis to weave our storytelling around. If you’re interacting with something that will affect yourself or the user directly (e.g., a modal asking you if you want to continue), the Z-axis is usually the best choice. Suppose we weave our storytelling around the Z-axis as we did in our modal example earlier. In that case, we can make the modal move closer towards you as you choose an affirmative action (scaling up the modal, as if it lifts off the screen and travels towards you), and we can make it move away from you as you dismiss it. The Z-axis reinforces that the modal relates to you.
When you’re interacting with something that relates to someone else, like an email message that you’re about to send off to someone, your best choice of a primary axis is usually the Y-axis. By moving something rapidly upwards along the Y-axis and making it exit the screen while still accelerating away from you, we reinforce the concept of you sending off something to someone else. It’s almost as if you’re taking an envelope and sending it off to its next destination. Or as if you’re using a salmon cannon:
Since we’re working with an email message here that we’ll eventually send off to someone else, let’s pick the Y-axis as our primary axis. What that in mind, let’s break down every action and animation before reviewing it all together.
Creating a new email
Rather than only fading in a new message once you create it, we can animate the window to make it appear like we’re summoning a new message from below:
This transition is not much more meaningful than only fading in and out, but it starts to get interesting as we add the other animations that are clearly different but connected to the same storyline.
Discarding a message
Once you discard an email, we can make it fall back down, returning it to where it came from. Here I’m also applying a slight tilt to the window on its way down to illustrate that you’re throwing it away (rather than storing it somewhere). As if you’re dropping a piece of paper, rather than putting it away neatly:
Sending a message
Once you hit send, let’s shoot it off, away from us (and hopefully towards a recipient):
With those three actions visualized in distinctly different ways, we’re now telling a clear story about how messages are created, discarded, and sent off. Even though you might not infer the meaning of all the animations at first sight (they’re not necessarily intuitive), they’re clearly different and connected to an action each, and you can at the very least learn how to tell the different animations apart.
Saving as a draft
We’ve got one more action to cover. Saving a draft is crucially not the same thing as closing the window and discarding an email. Again, we’re dealing with actions, not states. It’s not a matter of showing or hiding objects; it’s a matter of visualizing the events unfolding on screen.
If we try to close the new message window while writing a message, we’re asked if we want to discard the email or save it as a draft. Given that someone chooses “Save as draft,” what’s important to convey? At least two things, 1) that the message is being saved as a draft somewhere, and 2) exactly where that somewhere is. We want users of our app to understand where they can find that draft later so that they can send it when the time is right:
This animation clearly shows how your message gets stored in your outbox, and it even tells you where your outbox is located! We’ve restricted ourselves to animating along the Y-axis up to this point to enforce the concept of creating something and sending it off. Now that we’re deviating from that storyline and storing a message, we can drop the axis restriction and shrink the message while we move it towards the outbox icon.
To make it clear that we’re storing the message inside the outbox (and not just shrinking and hiding the message window), we’re adding another effect: as the message window arrives at the outbox icon, we make the outbox icon grow with a slight bounce effect (as if it swallows the message), and we add a small counter indicating that your number of saved drafts just increased from 0 to 1. This animation doesn’t just tell you what events are being triggered: it teaches you how the app works and how to navigate it.
Review: before & after
Before we started applying action-driven animation, we faded views in and out. These animations looked a bit better than simply popping views in and out of view, but the animations didn’t add much value in terms of helping users understand which events were unfolding on screen. This is an example of state-driven animation, and it looked like this:
After critically reviewing every animation and asking ourselves what each animation should convey and how best to do so, we iterated on the experience and created a set of animations that each conveys what events are unfolding on screen. We did so with the help of action-driven animation, which helps us identify the number of animations that we need to create through its focus on possible action paths rather than on possible states. After applying action-driven animation, this is what our prototype looks like:
We divided UI animations into two different models: state-driven animation and action-driven animation. I argued that the first model tends to produce animations that lack any significant meaning; they are not helpful. I presented an example from Tumblr to illustrate how state-driven animation can, even when it looks beautiful, miscommunicate and cause confusion.
I argued that a better way to work with animations is to focus on the actions or events that are being triggered, which I call action-driven animation. We reviewed how this technique can be applied to a modal and an email app and how it aids users in understanding what actions and events are unfolding before them, but also how the app works and how to navigate it.
Action-driven animation is a way of thinking about motion that you can apply to review the interface animations of an app or product to create more meaningful animations. I’ve found it useful to use in my work, and I hope you will, too.
Although no code has been shared in this post, all of the examples that I created are animated with the lovely anime.js library (by Julian Garnier). If you want to play around with the email prototype and its animations, feel free to download all the example code and have a look around. All icons are from Google’s Material.