Course outline
Easing and Tweening
In one line
Move a value from A to B over a set time along a curve that starts slow, ends slow or overshoots. You write the tween by hand, then compare it with damp and a spring when the target keeps changing.
Think of it like this
A train does not go from standing to 100 km/h in one instant, nor stop dead at the platform. It speeds up, cruises and brakes. A value that moves at one steady speed feels like a machine. One that eases in and out feels alive.
Words you'll meet
New words in this topic, in plain English. Come back here whenever one feels fuzzy.
- lerp
- Linear interpolation: a + (b - a) * t. A straight-line blend between two values.
- easing
- A function that bends t (0 to 1) so motion speeds up or slows down.
- tween
- An animation that moves a value from one number to another over a set time.
- damping
- Moving a fixed share of the remaining distance per second, so the motion slows as it arrives.
- spring
- A moving value with a velocity that is pulled towards a target. It can overshoot or not (critically damped).
Step by step
01Read the table
The first example prints five easings at t = 0, 0.25, 0.5, 0.75, 1, and at two values outside that range. Every curve is 0 at the start and 1 at the end. easeOutCubic is already 0.578 at t = 0.25. easeOutBack passes 1 (1.088 at t = 0.5) and comes back.
02Watch the balls
Five balls travel the same road in 2 s, one per easing, then wait 1 s and repeat. They leave and arrive together, and differ in between.
03Frame rate
The second example chases a target for exactly one second at 30, 60 and 144 fps. The fixed-fraction version gives 0.958, 0.998 and 1.000. damp gives 0.998 each time.
04Change the target
The third example moves the target from 1 to 2 at frame 30. The tween restarts and its speed jumps from 0.78 to 3.32 units a second. damp jumps too (0.31 to 5.99) because it reacts at once. The spring bends: 0.40 to 1.85.
Try it yourself
- 1
Clamp t
In the first example, remove the
Math.min(..., 1)in the loop. The balls fly past the end during the 1 s wait andeaseOutBackgoes far beyond. That is the 1.5 column of the table at work. - 2
Faster damp
In the second example change 6.3 to 12.6. The damp column prints 1.000 in all three rows (the lerp column does not change), and the formula line prints 0.1894 = 0.1894.
Code & diagrams
The table is printed first. In the picture, the top ball (grey) is linear.
Console
Run the scene to see its console.Expected console output
t 0 0.25 0.5 0.75 1 1.5 -0.5
linear 0.000 0.250 0.500 0.750 1.000 1.500 -0.500
easeInQuad 0.000 0.063 0.250 0.563 1.000 2.250 0.250
easeOutCubic 0.000 0.578 0.875 0.984 1.000 1.125 -2.375
easeInOutCubic 0.000 0.063 0.500 0.938 1.000 1.500 -0.500
easeOutBack 0.000 0.817 1.088 1.064 1.000 1.763 -4.289
clamp(1.5, 0, 1) = 1 | lerp(2, 10, 0.25) = 4No canvas: pure numbers, so you can read them.
Console
Run the scene to see its console.Expected console output
fps lerp 0.1 per frame damp(lambda 6.3, delta)
30 0.958 0.998
60 0.998 0.998
144 1.000 0.998
damp is lerp(x, y, 1 - exp(-lambda * dt)): 0.0997 = 0.0997The target jumps at frame 31. Each cell is the value and, in brackets, its speed in units a second.
Console
Run the scene to see its console.Expected console output
target 1 until frame 30, then 2. Each cell: value (speed in units per second)
frame 30 tween 0.875 (0.78) damp 0.950 (0.31) spring 0.944 (0.40)
frame 31 tween 0.930 (3.32) damp 1.050 (5.99) spring 0.975 (1.85)
frame 32 tween 0.984 (3.21) damp 1.141 (5.42) spring 1.022 (2.80)
frame 60 tween 1.859 (0.87) damp 1.948 (0.33) spring 1.943 (0.41)
frame 90 tween 2.000 (0.00) damp 1.997 (0.02) spring 1.998 (0.01) Break it on purpose
Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.
Break #1
A zero duration
In the third example, call createTween(0, 1, 0).
Myth vs fact
Myth
Easing makes the animation take longer.
Fact
It does not change the duration. It changes where along the road the value is at each moment.
Pro corner
Extra depth for experienced readers. New to this? Skip it for now and come back later.
- ▸
dampis the exact solution of x' = -lambda (x - target) over a step, so the result does not depend on how you cut the time into frames. Comparex += (y - x) * 0.1, whose result does. - ▸
CSS has the same curves:
transition-timing-function: ease-outandcubic-bezier(...). For DOM animation, the Web Animations API (element.animate) can run off the main thread. Inside a WebGL scene you animate in your loop, so you write the curve in JavaScript. - ▸
The tween here holds no state outside
elapsed, so it is easy to test: calltick(0.5)and compare withfrom + (to - from) * ease(0.5).
Remember this
- 1
The problem this solves:
lerp(a, b, t)moves a value in a straight line. Cameras, panels and colours that move like that feel stiff. And if the target keeps changing (a mouse, a slider) a fixed-time animation has to restart again and again. - 2
Lerp is linear interpolation:
a + (b - a) * t, wheretruns from 0 to 1. At t = 0.25 it is a quarter of the way.THREE.MathUtils.lerp(2, 10, 0.25)is 4. - 3
Easing bends
tbefore it is used:value = a + (b - a) * ease(t).easeInQuadist * t(slow start).easeOutCubicis1 - (1 - t)^3(fast start, gentle stop).easeInOutCubicdoes both.easeOutBackovershoots, then settles. A good easing maps 0 to 0 and 1 to 1, so the animation starts and ends exactly where you say. - 4
A tween is a value plus a timer: it keeps
elapsed, addsdeltaeach frame, clampselapsedtoduration, and returnsfrom + (to - from) * ease(elapsed / duration). The clamp matters: outside 0 to 1 many easings shoot far away (the table showseaseOutBack(-0.5) = -4.289). - 5
When the target keeps changing, a tween must restart from the current value, which gives a visible kink in the speed. Two tools avoid that.
damp(THREE.MathUtils.damp(x, y, lambda, dt)) moves a fraction1 - exp(-lambda * dt)of the remaining gap each frame, so it is frame-rate independent and never needs a duration. A spring keeps a velocity, so a new target bends the path smoothly. - 6
**Why not
x += (target - x) * 0.1?** That is a fraction per frame, so it is frame-rate dependent (Topic 9.1). After exactly one second it has covered 95.8% at 30 fps and 99.8% at 60 fps.dampcovers the same 99.8% at any frame rate.
Explain it without notes
Why must a tween clamp elapsed to the duration?
A slider moves the camera's target every frame. Would you use a tween or damp, and why?
Practice
Write createTween(from, to, duration, ease) with a tick(delta) that returns the value, and show that tick(0.5) for from = 0, to = 1, duration = 1 with easeOutCubic is 0.875.
A sphere should follow a mouse x position with damp. Write the frame code with lambda 8.
Trade-offs
- ↔
A tween is predictable (it ends at a known time) but cannot follow a moving target. Damp follows anything but never quite arrives, so stop it when the gap is tiny.
Done when you can
Write lerp and two easing functions from memory
Write a tween with a clamped elapsed time
Say why a fixed fraction per frame depends on the frame rate and damp does not
Choose between a tween, damp and a spring for a given job