Animation
Everything you've drawn so far has been a single, static frame. Hit Run, see the result, done. Animation changes that — now your code runs over and over, redrawing the canvas many times per second. A shape that was at x=100 is now at x=102, then x=104. Your eye sees motion. That's it — that's animation.
The animation loop
The browser gives you requestAnimationFrame — a function that says “call me back right before the next screen repaint.” That's roughly 60 times per second on most displays. You could use setInterval, but it doesn't sync to the display, wastes power when the tab is hidden, and produces janky motion. requestAnimationFrame is the right tool.
The pattern is always the same: clear the canvas, draw everything for this frame, request the next one. In our playground, you get an animate() helper that handles the loop for you. Pass it a function and it calls that function every frame with a timestamp in milliseconds.
When you hit Run, the animation starts. Hit Stop to pause it. Hit Run again to restart from scratch. The playground cancels the previous loop automatically — no zombie animations stacking up.
Your first animation
Let's make a circle move across the canvas. The trick: define your state variables (like x) outside the animate callback so they persist between frames. Each frame, update the value, clear, and redraw.
Try changing the speed from 2 to 5 or 0.5. Make it move vertically instead (change x to y in the arc call). What happens if you remove the clearRect line? You get a trail — sometimes that's what you want.
Animating transforms
Remember save(), translate(), rotate(), and restore() from the previous lesson? They're even more useful in animation. Here a square spins in place — all we do is increment the angle each frame.
Try drawing a rectangle instead of a square — the rotation becomes much more visible. Add a second shape rotating in the opposite direction (-angle). Can you make a clock hand that rotates once per minute?
Time-based motion
There's a subtle problem with angle += 0.02 — it adds the same amount every frame. On a 144Hz monitor that's twice as fast as a 60Hz one. The fix is to use the timestamp parameter, which counts milliseconds since the page loaded. Multiply by a speed factor and your motion is the same on every device.
Math.sin(timestamp * factor) is your best friend for smooth oscillation. Try changing 0.003 to make the wave faster or slower. Change 40 to control how tall the wave is. Can you add a Math.cos version moving in the opposite phase?
Multiple objects
Most animations involve more than one thing moving. The pattern: store your objects in an array, and loop over them each frame. Each object holds its own position, speed, colour — whatever it needs.
Add more balls to the array. Give each one a phase offset so they don't all peak at the same time: Math.sin(timestamp * 0.003 * ball.speed + ball.phase). Can you make them move horizontally too?
Putting it all together
Here's a bouncing ball with basic physics — gravity pulls it down, it bounces off the floor and walls, and a shadow tracks it underneath. Every technique here is something you've already learned: the animation loop, position/velocity variables, collision checks, and drawing shapes. Take it apart, tweak the constants, break it, rebuild it.
Change gravity (try 0.05 for the moon) and bounce (try 0.95 for a super bouncy ball). Set vx to 0 for a straight drop. Can you add a second ball with different starting conditions? What about leaving a motion trail by replacing clearRect with a semi-transparent fill?