04 Weight 04 Weight 04 Weight 04 Weight
A circle weighs nothing until it moves. Then its timing and spacing say how heavy it is.
Draw a bowling ball and a beach ball and you get two circles. Nothing in the drawing says which is which. The motion does. A heavy thing is slow to get going and hard to stop, and it doesn’t linger in the air. A light thing leaps, hangs, drifts, and bounces.
So weight isn’t something you draw. It’s timing (chapter 2) and spacing (chapter 3), read by an eye that has spent a lifetime lifting, dropping, and catching things. This chapter puts the two together in one object.
A ball, dropped
A ball falls and bounces along the floor. The blue circles are where it has been, one every few frames.
Look at where the circles crowd and where they spread. They crowd at the top of each arc, where the ball slows, turns over, and hangs for an instant. They spread near the floor, where it moves fastest. At the floor it squashes, and the harder it lands, the more. Each bounce is lower and shorter than the one before, until the ball rolls along the floor and comes to rest.
None of that is in the drawing. It’s all when and where, and every part of it tells you something about what the ball weighs.
Which ball is heavier?
Two balls fall from the same height. Which one is heavier?
What the eye reads
Chapter 2 used a rough rule: the longer move reads as heavier. That holds when duration is the only thing that changes. In physics, air aside, a ball falls just as fast whatever it weighs. So when the eye has more to go on, it stops timing the whole move and reads its shape.
The two dots below travel the same arc. The path is identical; only the timing and spacing differ. The heavy one takes 0.7s and the light one 1.3s, so the heavy one is the quicker of the two. Play it and watch the two ends of each move, the start and the finish, rather than the clock. Hover for the ghosts, and see where they bunch.
Four things tell them apart:
- Starting and stopping. A heavy thing resists any change of speed; that resistance is its inertia. So it eases in to get going and eases out to stop, and its ghosts crowd at both ends. A light thing changes speed at once.
- Hanging. A light thing lingers at the top of an arc and drifts down, because the air holds it up. A heavy one turns over and comes straight down.
- Bouncing. A heavy thing lands and stays down. A light one comes back up with much of its speed.
- Squashing. Contact shows the impact. A rubber ball flattens against the floor and springs back; a bowling ball keeps its shape and simply stops. Squash says how hard it hit and what it’s made of.
Total time is the weakest of these cues. The heavy dot above is faster overall, and it still reads as heavy, because of where it spends its time. This is why chapter 2’s rule needed the words “when duration is the only thing that changes”.
The bouncing ball
The ball in figure 4.1 is a classic first exercise for animators, because all of weight fits into one bounce. Read it in four parts:
- The fall: ease-in. Gravity adds the same amount of speed every frame, so each gap is wider than the last. The distance fallen grows with the square of the time: in twice the time, four times as far. That is why the fall is an ease-in curve, slow at first and quickest at the end.
- Contact. The fastest frame of the whole bounce. The ball squashes: flatter and wider, but the same amount of ball.
- The rise: ease-out. The fall played backwards. It leaves the floor fast and slows to a stop at the top, where the drawings crowd together.
- Energy loss. At each contact the ball keeps only part of its speed, so every bounce is lower and shorter than the last.
That fraction is the ball’s restitution, and the rest follows from it. Throw a ball upward twice as fast and it climbs four times as high, so height goes with the square of the speed. It also stays up twice as long, so time goes with the speed. Turn that round: a ball that keeps half its speed at contact bounces a quarter as high, for half as long. Each later bounce repeats the step, so the hops shrink in a fixed ratio.
| Restitution | Next bounce: height | Next bounce: time | Reads as |
|---|---|---|---|
| 0.2 | 4% | 20% | heavy, a thud |
| 0.5 | 25% | 50% | firm |
| 0.8 | 64% | 80% | light, lively |
Strictly, bounciness belongs to the material, not the mass. But the eye has learned from bowling balls and ping-pong balls, and it reads a dead landing as heavy. So restitution is the dial you turn to say “heavy” or “light”. The table’s bands (below about 0.3 heavy, above about 0.7 lively) are a matter of taste, not a law.
In CSS terms, every fall is an ease-in and every rise an ease-out. The keyword ease-in, the solid curve below, is a rough stand-in for gravity. A real fall, the blue dashed one, is cubic-bezier(.33, 0, .67, .33) to two decimals, and its velocity is a straight line, because gravity adds the same speed every frame. The rise is the same curve turned around, cubic-bezier(.33, .67, .67, 1). In the figure, compare the two velocity lines: the real fall climbs steadily, while the keyword ease-in curves away from it and reaches a higher top speed.
Weight in an interface
Interfaces have no gravity, but they borrow its cues. A large surface, like a sheet or a full-screen panel, should move like something heavy: slow to start, slow to settle, no bounce. A small thing, like a badge, a chip, or a like button, can snap into place and bounce a little.
Swap them and both feel wrong. A bouncing dialog seems flimsy, and a badge that takes its time seems sluggish. Pick the weight to match the size and importance of the thing, and keep it the same everywhere that thing appears. As starting points to adjust by eye: a sheet around 400ms with an ease-out and no overshoot, a badge around 200ms with a small overshoot. Both numbers are taste, not rules.
Make it heavy, then light
This is the ball from figure 4.1, with its knobs and its code. Change one thing at a time.
- Turn Restitution (the fraction of speed kept at each bounce, from the table above) down to about 0.2. The ball lands with a thud and stays down: heavy.
- Turn it up to about 0.9. It bounces on and on, like rubber: light.
- Lower Gravity (how fast the fall picks up speed, in px/s²). Every arc slows and hangs at the top, and the ball starts to float like a beach ball. Raise it and the ball drops like a stone.
- Turn Squash on contact off. The ball turns hard, like wood.
In the code, find the two lines that hold the weight: gravity adding to the speed every frame, and the contact that flips the speed and keeps only a fraction of it. Change the numbers there and run it again.
The notation
A transition has one curve, and a bounce needs two: ease-in for every fall, ease-out for every rise. Keyframes give you as many as you need. In CSS, an animation-timing-function set inside a keyframe applies to the segment from that keyframe to the next one. In the Web Animations API, a keyframe’s easing does the same. The contacts sit on keyframes, where one curve hands over to the other, and that sharp corner is the bounce. Below, the CSS card lists every height and time by hand. The JS card computes them from one number, the restitution.
/* Dropped from 160px. Each contact keeps half the speed,
so each bounce is a quarter as high and half as long.
Percentages are shares of the 1000ms: 0-40% is the first fall (400ms),
then each hop lasts half as long as the last (200ms, then 100ms).
Heights: 160px, then 40px (a quarter), then 10px (a quarter again).
"both" holds the first frame before it starts and the last after. */
.is-on .box {
animation: drop 1000ms both;
}
/* A keyframe's timing function runs until the next keyframe:
ease-in for every fall, ease-out for every rise. */
@keyframes drop {
0% { transform: translateY(-160px); animation-timing-function: ease-in; }
40% { transform: translateY(0); animation-timing-function: ease-out; }
60% { transform: translateY(-40px); animation-timing-function: ease-in; }
80% { transform: translateY(0); animation-timing-function: ease-out; }
90% { transform: translateY(-10px); animation-timing-function: ease-in; }
100% { transform: translateY(0); }
} // A keyframe's easing runs from that keyframe to the next one.
const FALL = 'cubic-bezier(.33, 0, .67, .33)'; // ease-in: gravity speeds it up
const RISE = 'cubic-bezier(.33, .67, .67, 1)'; // ease-out: gravity slows it down
function drop(el, { height = 160, fall = 400, restitution = 0.5 } = {}) {
const keys = [{ at: 0, transform: `translateY(${-height}px)`, easing: FALL }];
let at = fall; // ms: the first fall ends here, at the first contact
// e is the share of the speed kept after this contact: the hop is e² of
// the height and lasts e of the time. Each pass of the loop is one hop,
// and e shrinks by another restitution each time.
// Stop once a hop would be under a pixel high.
for (let e = restitution; e < 1 && height * e * e >= 1; e *= restitution) {
keys.push({ at, transform: 'translateY(0)', easing: RISE }); // contact
at += fall * e;
keys.push({ at, transform: `translateY(${-height * e * e}px)`, easing: FALL });
at += fall * e;
}
keys.push({ at, transform: 'translateY(0)' });
const total = at; // ms: the whole bounce, from release to rest
return el.animate(
// offset is each keyframe's place in the animation, from 0 to 1
keys.map(({ at, ...k }) => ({ ...k, offset: at / total })),
{ duration: total, fill: 'both' }, // no easing here: the keyframes carry it
);
}
drop(document.querySelector('.box'), { restitution: 0.5 }); // try 0.3, then 0.7 Two details. Leave the animation’s own easing alone: in the Web Animations API it would bend the whole bounce on top of the keyframes’ curves. And linear() from chapter 3 can draw a whole bounce as one curve; that is how the two balls in the “Which ball is heavier?” exercise above were made.
This bounce is drawn by hand: you chose every height and every time. Chapter 5 stops drawing and describes the physics instead. A spring is set by how quick it is and how bouncy, and the timing falls out.
Which is heavier?
Two balls drop and bounce. Pick the heavier one by eye, then check what gave it away: the fall, the hang, or the landing.
Two balls drop and bounce. Watch the fall and the rebound: which one is heavier?Weight: reading mass from timing and spacing.
5 trials. Judge with your eyes first; the numbers come after. Three right in a row makes trials harder, a miss eases them.