Chapter 34: Canvas Drawing
Canvas Drawing in a very clear, step-by-step, human-teacher way.
Imagine I’m sitting next to you with my laptop open and we’re building everything together from the smallest piece to more interesting things. No magic, no skipping steps, lots of tiny complete examples you can copy-paste right now.
1. Canvas Drawing – the most honest mental model
When you draw on Canvas you are not creating objects that live forever (like in SVG). You are painting pixels on a bitmap screen.
Every time you want something to change (move, grow, disappear, change color), you basically have to erase the old picture and paint a new one.
That is why almost every Canvas program has this repeating pattern:
|
0 1 2 3 4 5 6 7 8 9 10 |
while (true) { erase everything update positions / logic draw everything again } |
This loop usually runs ~60 times per second (thanks to requestAnimationFrame).
2. The absolute smallest drawing example (copy → run)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Canvas – First Brush Stroke</title> <style> canvas { border: 1px solid #ccc; background: #f8f9fa; display: block; margin: 40px auto; } </style> </head> <body> <canvas id="c" width="600" height="400"></canvas> <script> // Step 1: get canvas & context const canvas = document.getElementById('c'); const ctx = canvas.getContext('2d'); // Step 2: choose paint color ctx.fillStyle = '#4CAF50'; // green // Step 3: paint a rectangle ctx.fillRect(80, 80, 200, 140); // Step 4: paint an outline rectangle ctx.strokeStyle = '#2E7D32'; // darker green ctx.lineWidth = 10; ctx.strokeRect(340, 80, 200, 140); </script> </body> </html> |
What we just learned in plain English:
- getContext(‘2d’) → gives you the paintbrush object (99.9% of Canvas code uses ‘2d’)
- fillStyle / strokeStyle → current paint color (stays until you change it)
- fillRect(x, y, width, height) → paint a filled rectangle
- strokeRect(x, y, width, height) → paint only the outline
Coordinates: (0,0) is top-left, y grows downward.
3. The second most important concept: Paths
Almost everything interesting (circles, curves, polygons, stars, arrows…) is built with paths.
Basic path recipe (you will write this pattern thousands of times):
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
ctx.beginPath(); // ← very important – forget it and shapes connect to each other ctx.moveTo(100, 100); // pen up + jump to start point ctx.lineTo(300, 50); ctx.lineTo(400, 180); ctx.closePath(); // optional – connect back to start ctx.strokeStyle = '#673AB7'; ctx.lineWidth = 12; ctx.lineCap = 'round'; ctx.lineJoin = 'round'; ctx.stroke(); // ← actually draw the outline // or fill it ctx.fillStyle = 'rgba(103,58,183,0.4)'; ctx.fill(); |
4. Drawing a circle / arc (the most common shape after rectangles)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
ctx.beginPath(); ctx.arc( 300, // center x 200, // center y 80, // radius 0, // start angle (0 = right = 3 o'clock) Math.PI * 1.8, // end angle (Math.PI*2 = full circle) false // counterclockwise? → usually false ); ctx.fillStyle = '#FF9800'; ctx.fill(); ctx.strokeStyle = '#EF6C00'; ctx.lineWidth = 14; ctx.stroke(); |
Quick angle cheat-sheet (radians)
- 0 → right (3 o’clock)
- Math.PI/2 → down (6 o’clock)
- Math.PI → left (9 o’clock)
- Math.PI*2 → full circle
5. Drawing text
|
0 1 2 3 4 5 6 7 8 9 10 11 |
ctx.font = 'bold 60px Arial'; ctx.fillStyle = '#2196F3'; ctx.textAlign = 'center'; // left | center | right | start | end ctx.textBaseline = 'middle'; // top | hanging | middle | alphabetic | ideographic | bottom ctx.fillText('Hello Canvas!', 320, 280); |
6. Quick summary table – 80/20 most used drawing commands
| Goal | Code example |
|---|---|
| Filled rectangle | ctx.fillRect(x, y, w, h) |
| Outlined rectangle | ctx.strokeRect(x, y, w, h) |
| Circle / arc | ctx.arc(cx, cy, r, startAngle, endAngle) |
| Line | ctx.moveTo(x1,y1); ctx.lineTo(x2,y2); ctx.stroke() |
| Change color | ctx.fillStyle = ‘#FF5722’ or ‘rgba(255,87,34,0.7)’ |
| Line thickness & cap style | ctx.lineWidth = 10; ctx.lineCap = ’round’ |
| Save / restore state | ctx.save(); … ctx.restore(); |
| Clear whole canvas | ctx.clearRect(0, 0, canvas.width, canvas.height) |
Your first three tiny practice tasks (10–15 minutes each)
Task 1 – Smiley face Yellow circle + two black eyes + red smiling mouth (arc)
Task 2 – House Rectangle base + triangle roof + rectangle door + circle window
Task 3 – Traffic light Three stacked circles (red, yellow, green) with black outlines
Copy any of them into a file, run it, then come back and paste your code here if you want feedback. I’ll review it exactly like we’re pair-programming together.
Which part feels most confusing or strange right now?
- The fact that Canvas is “stateful” and remembers colors/line width?
- Why we need beginPath()?
- Angles in radians for arcs?
- Difference between fill and stroke?
- Something else?
Tell me honestly — we’ll slow down and fix exactly the piece that’s fuzzy. Canvas feels weird at first, but once it clicks it becomes very logical and powerful.
You’re doing great — let’s keep going! 🚀
