DocsJavaScriptMotion reference

Tween animations

A tween animates between a start and end value over a set duration, shaped by an easing curve.

A tween is an animation defined by a start value, an end value, a duration and an easing curve. Give a runtime those four things and it generates every frame in between, which is exactly where the name comes from: "tween" is short for "in-between".

animate(element, { opacity: 0 }, { duration: 0.5, ease: "easeOut" })

That's a tween. It's also, give or take the syntax, every CSS transition you've ever written.

Where the name comes from

Hand-drawn animation had a division of labour. Senior artists drew the keyframes, the poses that carried the story: the character crouched, the character mid-leap, the character landed. Assistants then drew the frames that joined those poses together. The work was called in-betweening, and the people doing it were in-betweeners, which everyone quite reasonably shortened to tweening.

Web animation inherited the vocabulary and the division of labour along with it. You still draw the keyframes. The runtime does the in-betweens, considerably faster and without ever asking for a pay rise.

The anatomy of a tween

Every tween, in every library and on every platform, is made of the same three parts.

Keyframes are the values you're animating between. Usually two: where the value is now, and where you want it to end up.

Duration is how long the animation takes. This is the defining property of a tween. You declare the time up front, so you always know exactly when it will be over.

Easing decides how progress is distributed across that duration. Linear easing covers equal distance every frame, which is why it looks mechanical. Real movement accelerates and decelerates, and easing is how you describe that. Motion has a full set built in.

The mechanism underneath isn't complicated. Each frame, the runtime works out how much of the duration has elapsed as a number between 0 and 1, passes that through the easing function, then mixes the keyframes by the result. Halfway through a linear tween from 0 to 100, you get 50. That's the whole idea.

Tweens are a platform concept

This isn't a library invention, and it certainly isn't Motion's. Tweens are how the web animates.

Every CSS transition is a tween:

.box {
  transition: transform 0.4s ease-out;
}

Property, duration, easing curve: the same three parts in the same order. CSS @keyframes animations are tweens too, as is every animation created with the Web Animations API's element.animate().

If the word itself feels like GSAP vocabulary, that's because GSAP is largely why we all say it. Its core unit is literally a Tween: gsap.to() returns one, and a Timeline is a way of arranging them. It's a brilliant library that taught a generation of us to think in tweens and timelines, and that "tween" is the everyday word for this is mostly down to it.

Tweens and springs

Nearly every animation you write is one of two things, and the difference is where the duration comes from.

A tween is time-based. You declare the duration, and the easing curve shapes the movement inside it.

A spring is physics-based. You declare stiffness, damping and mass, or simply a bounce, and the duration falls out of the simulation. A spring also carries the current velocity of the value into the new animation, so it knows how fast the thing was already moving.

Reach for a tween when timing is the point:

  • Choreographed sequences, where several animations have to land together.

  • Anything synchronised to an external clock: audio, video, a scroll position, a stagger.

  • A designer has handed you a specific cubic bezier and expects to see it on screen.

Reach for a spring when the animation is going to be interrupted:

  • Gestures. Anything that follows a finger or a cursor.

  • Values that get re-targeted mid-flight, like a menu the user closes while it's still opening.

That last one is the honest difference between them. Interrupt a tween and it starts a new one from wherever the value happened to be, with a fresh clock and a fresh curve, which reads as a small stutter. Interrupt a spring and the velocity carries through, so the change of direction looks like something that was always going to happen.

Neither is correct. A tween is easier to reason about, easier to coordinate and easier to hand back to a designer. A spring is harder to time and better at feeling alive. Most interfaces want some of each, which is why Motion will choose for you if you let it: transforms like x and scale default to a spring, everything else defaults to a tween of 0.3 seconds, and anything with more than two keyframes defaults to a tween of 0.8.

Set any transition option of your own and that per-value default steps aside. So a duration on x gives you a tween, not a duration-based spring.

Tweens in Motion

The animate function creates a tween whenever you give it a duration or an easing curve.

import { animate } from "motion"

animate(element, { x: 200 }, { duration: 0.6, ease: "easeInOut" })

duration is in seconds. ease takes the name of a built-in easing function, an array of four numbers for a custom cubic bezier, or your own function mapping 0-1 to 0-1.

A tween isn't limited to two values. Pass an array of keyframes and the animation runs through all of them:

animate(
  element,
  { x: [0, 200, 100] },
  { duration: 1, times: [0, 0.7, 1] }
)

Keyframes are spread evenly across the duration by default. times overrides that, positioning each one as a fraction of the total, so here x spends 70% of the second travelling out to 200 and the remaining 30% settling back to 100. ease accepts an array too, one curve per pair of keyframes.

In Motion for React the same settings go in the transition prop, where type: "tween" is the explicit form:

<motion.div
  animate={{ x: 200 }}
  transition={{ type: "tween", duration: 0.6, ease: "easeInOut" }}
/>

You rarely need to write type at all, because a duration or an ease implies a tween already. It's worth writing when you want the intent to be unmissable in a file where the other transitions are springs. Every option is listed in the transitions guide.

Visual editing for IDEs.

Edit and preview Motion and CSS transitions live in your code. Tune ease curves, springs, and durations without leaving your editor.

Part of Motion+. One-time fee, lifetime access.

What a tween costs

A tween itself is cheap. Working out a progress value, running it through a curve and mixing two numbers is nothing next to what the browser then does with the result.

What it costs depends on the property you're tweening. A tween of transform or opacity can be handed to the compositor, the cheap end of the rendering pipeline. A tween of height or top makes the browser recalculate layout on every frame, and layout is the expensive end. The animation code is identical. The frame budget is not.

So when a tween feels janky, the duration and the easing curve are almost never the culprit. Look at what you asked it to animate.