Drawing Shapes
In the last lesson we drew a single rectangle with fillRect. That's a fine start, but canvas can draw a lot more than boxes. In this lesson we'll cover the full shape toolkit: outlined rectangles, freeform paths, arcs, and circles. By the end you'll have enough to draw almost anything.
Rectangles, revisited
You already know fillRect(x, y, w, h) — it draws a filled rectangle. Canvas gives you two siblings: strokeRect draws just the outline, and clearRect erases a rectangular area back to transparent. Think of clearRect as a hole-punch.
Try changing lineWidth on the stroked rectangle, or move the clearRect coordinates to punch the hole in a different spot.
Paths & the invisible pen
Rectangles are special-cased, but everything else you draw on canvas goes through paths. A path is a series of connected points that you build up step by step, then either fill or stroke at the end.
The mental model: imagine you're holding an invisible pen over the canvas. beginPath() starts a new shape. moveTo(x, y) lifts the pen and places it somewhere without drawing. lineTo(x, y) draws a line from where the pen currently is to the new point. When you're done, call stroke() to trace the outline, or fill() to colour the interior. closePath() draws a straight line back to where you started — handy for closing off shapes like triangles.
This is a triangle, but you can make any polygon. Try adding more lineTo calls to turn it into a pentagon or a star. What happens if you remove closePath()?
Every path follows the same recipe. Once this pattern clicks, it'll feel second-nature:
Arcs & circles
For anything round, you need arc(). It takes six parameters:
arc(x, y, radius, startAngle, endAngle, anticlockwise)
The angles are in radians, not degrees. A full circle is 0 to 2 * Math.PI (roughly 6.28). Half a circle is 0 to Math.PI. The last parameter is optional and defaults to false (clockwise).
Since arc() is a path command, you still wrap it in beginPath() and end with fill() or stroke() — just like lines.
Try changing Math.PI to Math.PI * 0.5 for a quarter circle. Or add true as the last argument to flip the arc direction. What happens if you forget beginPath() before the second arc?
If radians feel unnatural, here's the quick mapping. You'll memorise these after a few playgrounds:
- Quarter circle:
Math.PI * 0.5 - Half circle:
Math.PI - Three-quarters:
Math.PI * 1.5 - Full circle:
Math.PI * 2
Putting it all together
Each beginPath() starts a brand new shape, so you can stack as many as you want on the same canvas. Order matters — things drawn later paint over things drawn earlier, like layering paper cut-outs.
Here's a little scene to get you started. Tear it apart, change the colours, move things around, add your own shapes.
Can you add a chimney? Some clouds (hint: overlapping circles)? A tree made of a rectangle trunk and a circle top? Go wild.