Chapter 53: Canvas Examples
Canvas examples — complete, beautiful, useful mini-projects that show how these pieces combine in actual graphics people use every day.
I’m going to teach this like your favorite teacher showing finished artwork on the board: explaining why each part is there, what technique we’re reusing, and giving you the full code so you can immediately run it, break it, modify it, and make it your own.
All examples are self-contained single-file HTML — just copy into a file and open in browser.
Example 1 – Animated Loading Spinner (pure CSS + Canvas circle)
Very common UI element — lightweight, vector-sharp, no GIF.
|
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 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Canvas Loading Spinner</title> <style> body { margin:0; height:100vh; display:flex; justify-content:center; align-items:center; background:#f0f2f5; } canvas { display:block; } </style> </head> <body> <canvas id="spinner" width="120" height="120"></canvas> <script> const canvas = document.getElementById('spinner'); const ctx = canvas.getContext('2d'); const cx = canvas.width / 2; const cy = canvas.height / 2; let angle = 0; function draw() { ctx.clearRect(0, 0, canvas.width, canvas.height); ctx.save(); ctx.translate(cx, cy); ctx.rotate(angle); // Thick blue arc that grows ctx.beginPath(); ctx.arc(0, 0, 45, 0, Math.PI * 1.5); ctx.strokeStyle = '#2196F3'; ctx.lineWidth = 12; ctx.lineCap = 'round'; ctx.stroke(); ctx.restore(); angle += 0.08; // rotation speed requestAnimationFrame(draw); } draw(); </script> </body> </html> |
Techniques used here:
- clearRect every frame
- save() / restore() to isolate rotation
- translate + rotate to spin around center
- arc + stroke + lineCap=’round’
- requestAnimationFrame animation loop
Example 2 – Interactive Button with shine & hover shadow
|
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 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Canvas Interactive Button</title> <style> body { margin:0; height:100vh; display:flex; justify-content:center; align-items:center; background:#f0f2f5; } canvas { cursor:pointer; } </style> </head> <body> <canvas id="btn" width="320" height="100"></canvas> <script> const canvas = document.getElementById('btn'); const ctx = canvas.getContext('2d'); let hover = false; function drawButton() { ctx.clearRect(0, 0, canvas.width, canvas.height); // Shadow (stronger when hover) ctx.shadowColor = 'rgba(0,0,0,0.4)'; ctx.shadowBlur = hover ? 20 : 8; ctx.shadowOffsetX = hover ? 6 : 4; ctx.shadowOffsetY = hover ? 10 : 6; // Base gradient const grad = ctx.createLinearGradient(0,0,0,100); grad.addColorStop(0, hover ? '#42A5F5' : '#2196F3'); grad.addColorStop(1, hover ? '#1976D2' : '#1565C0'); ctx.fillStyle = grad; ctx.beginPath(); ctx.roundRect(10, 10, 300, 80, 40); ctx.fill(); // Shine highlight (stronger on hover) const shine = ctx.createRadialGradient(80, 40, 10, 80, 40, 180); shine.addColorStop(0, 'rgba(255,255,255,0.9)'); shine.addColorStop(0.5, 'rgba(255,255,255,0.3)'); shine.addColorStop(1, 'rgba(255,255,255,0)'); ctx.fillStyle = shine; ctx.fill(); // Text ctx.shadowColor = 'transparent'; ctx.font = 'bold 36px Arial'; ctx.fillStyle = 'white'; ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; ctx.fillText('Hover Me!', 160, 50); } drawButton(); canvas.onmouseenter = () => { hover = true; drawButton(); }; canvas.onmouseleave = () => { hover = false; drawButton(); }; </script> </body> </html> |
Techniques used:
- roundRect for smooth corners
- Linear gradient base
- Radial gradient shine overlay
- Dynamic shadow strength on hover
- save/restore not needed here because we redraw everything
Example 3 – Animated bouncing ball with trail
|
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 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Canvas – Bouncing Ball with Trail</title> <style> body { margin:0; background:#000; } canvas { display:block; } </style> </head> <body> <canvas id="c"></canvas> <script> const canvas = document.getElementById('c'); canvas.width = window.innerWidth; canvas.height = window.innerHeight; const ctx = canvas.getContext('2d'); let x = canvas.width / 2; let y = canvas.height / 2; let vx = 4.2; let vy = 3.7; const radius = 30; function animate() { // Fade trail instead of full clear ctx.fillStyle = 'rgba(0,0,0,0.08)'; ctx.fillRect(0, 0, canvas.width, canvas.height); // Update position x += vx; y += vy; // Bounce on edges if (x < radius || x > canvas.width - radius) vx = -vx; if (y < radius || y > canvas.height - radius) vy = -vy; // Draw ball ctx.beginPath(); ctx.arc(x, y, radius, 0, Math.PI*2); ctx.fillStyle = '#FF5722'; ctx.shadowColor = '#FF5722'; ctx.shadowBlur = 30; ctx.fill(); ctx.shadowBlur = 0; requestAnimationFrame(animate); } animate(); window.onresize = () => { canvas.width = window.innerWidth; canvas.height = window.innerHeight; }; </script> </body> </html> |
Techniques used:
- Partial clear (fade trail) instead of full clearRect
- shadowBlur + shadowColor for glow
- requestAnimationFrame loop
- Resize handling
Example 4 – Simple drawing app (mouse + touch)
|
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 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Canvas – Simple Drawing App</title> <style> body { margin:0; overflow:hidden; background:#000; } canvas { display:block; } </style> </head> <body> <canvas id="draw"></canvas> <script> const canvas = document.getElementById('draw'); canvas.width = window.innerWidth; canvas.height = window.innerHeight; const ctx = canvas.getContext('2d'); let isDrawing = false; ctx.lineWidth = 8; ctx.lineCap = 'round'; ctx.strokeStyle = '#FFFFFF'; function draw(e) { if (!isDrawing) return; const rect = canvas.getBoundingClientRect(); const x = (e.clientX || e.touches[0].clientX) - rect.left; const y = (e.clientY || e.touches[0].clientY) - rect.top; ctx.lineTo(x, y); ctx.stroke(); ctx.beginPath(); ctx.moveTo(x, y); } canvas.addEventListener('mousedown', (e) => { isDrawing = true; draw(e); }); canvas.addEventListener('mousemove', draw); canvas.addEventListener('mouseup', () => isDrawing = false); canvas.addEventListener('mouseout', () => isDrawing = false); // Touch support canvas.addEventListener('touchstart', (e) => { e.preventDefault(); isDrawing = true; draw(e); }); canvas.addEventListener('touchmove', (e) => { e.preventDefault(); draw(e); }); canvas.addEventListener('touchend', () => isDrawing = false); window.onresize = () => { canvas.width = window.innerWidth; canvas.height = window.innerHeight; }; </script> </body> </html> |
Techniques used:
- Mouse & touch events
- lineTo + stroke continuous path
- beginPath() + moveTo after each stroke segment
- lineCap = ’round’ for smooth joins
These four examples cover a huge range of real-world Canvas usage:
- Loading spinner (animation loop, arc, stroke)
- Interactive button (gradients, shadows, hover state)
- Bouncing ball with trail (partial clear, shadow glow)
- Drawing app (mouse/touch events, continuous path)
Which one did you like most? Or what kind of example would you like next?
- Animated clock
- Particle explosion
- Mini paint app with color picker
- Image filter (grayscale, sepia)
- Simple game (paddle + ball)
- Morphing shape animation
- Something else?
Tell me — we’ll keep building real, beautiful Canvas projects together until you feel like a pro! 🚀
