Canvas
The HTML <canvas> element is a blank, bitmap drawing surface that you control entirely from JavaScript through its 2D context, letting you draw shapes, text, images, and gradients pixel by pixel for games, charts, and animations.
Part of the free HTML & CSS course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
By the end you'll draw shapes, text, and gradients on a <canvas> , capture mouse input to draw freehand, and run a smooth animation loop with requestAnimationFrame .
What You'll Learn
Before you start: Canvas is driven entirely by JavaScript, so you'll want to be comfortable with variables, functions, and events first. If you need a refresher, revisit the previous lesson and the JavaScript course.
💡 Real-World Analogy
Think of canvas as a whiteboard with a single marker . You don't place objects you can move later — you give the browser instructions ("put the pen here, draw a circle, fill it green"), and it stains the board with pixels. Once a stroke is down it isn't a separate "thing" anymore; to change it you wipe the area and redraw. That's why animation means clear, then redraw the whole scene on every frame.
1. The drawing context
The <canvas> element is just a blank rectangle until you grab its 2D context — the object that holds every drawing method. You get it once and reuse it:
Coordinates start at (0, 0) in the top-left ; x grows to the right and y grows downward . Canvas is great for games, charts, and effects; for icons and logos prefer SVG (next lesson) because it stays sharp at any zoom.
Canvas vs SVG — pick the right tool
Feature
Canvas
SVG
Type
Pixel-based (bitmap)
Vector-based (DOM nodes)
Scaling
Gets blurry on zoom
Infinitely sharp
Best for
Games, visualisations, effects
Icons, logos, diagrams
Interactivity
Manual hit-testing
Native DOM events per element
Performance
Better with many objects
Slows with 100s of nodes
2. Shapes, fills, strokes, text & gradients
Read this fully-commented example, then run it. Notice three patterns you'll use constantly: fill vs stroke (inside colour vs outline), paths ( beginPath → arc / lineTo → fill / stroke ), and a gradient built from colour stops.
Important: Set canvas size with HTML attributes ( <canvas width="500" height="300"> ), not CSS. CSS only stretches the bitmap — the real resolution stays at the attribute values, so mismatched CSS sizes produce blurry output.
3. 🎯 Your turn — a traffic light
Time to practise fill and beginPath . Fill in the blanks marked ___ following the 👉 hints. Each lamp is its own path, so beginPath() before every circle.
4. Capturing mouse input
To draw freehand you turn mouse positions into a path. On mousedown you start a path, on mousemove you lineTo the new point and stroke() , and you stop on mouseup . getBoundingClientRect() converts the page coordinates into canvas-local ones.
5. Animating with requestAnimationFrame
An animation is just a function that draws one frame and then asks to be called again. requestAnimationFrame(fn) runs fn right before the next repaint (~60 times/sec). Each frame you clear (or paint a faint layer for trails), update positions, redraw, then schedule the next frame.
6. 🎯 Your turn — a moving square
Build your own animation loop. Fill in the three blanks: the clear method, and the function name passed to requestAnimationFrame so the loop keeps going.
7. A real example — bar chart from data
Loops + rectangles + text alignment combine into a genuine data visualisation. Watch how textAlign = 'center' changes what the x coordinate means for the labels.
8. 🎯 Mini-challenge — smiley face
No blanks this time — just a comment outline. Combine everything you've learned (paths, arcs, fills, strokes) to draw a smiley face. The trick for the smile: an arc from 0 to Math.PI draws the bottom half of a circle.
When to reach for canvas
- Games: sprite rendering, collision detection, particle effects
- Data visualisation: custom charts, graphs, and infographics
- Image manipulation: filters, cropping, drawing on photos with drawImage
- Signatures: capturing handwritten input on forms
- NOT for: simple icons, logos, or text — use SVG or HTML instead
Common Errors & Fixes
- TypeError: Cannot read properties of null (reading 'getContext') — your getElementById returned null . The <script> ran before the canvas existed, or the id is misspelled. Put the script after the canvas (or in a load handler) and check the id.
- Everything joins into one strange blob — you forgot beginPath() . The path accumulates until you start a new one; call ctx.beginPath() before each shape.
- Shape appears but has no colour — you set fillStyle but never called fill() (or set strokeStyle but never stroke() ). Defining a path doesn't paint it.
- Drawing looks blurry / stretched — you sized the canvas with CSS instead of the width / height attributes. Set the resolution with attributes.
- Animation flickers or smears — you didn't clear the previous frame. Call ctx.clearRect(0, 0, w, h) at the top of each frame (or a semi-transparent fillRect for trails).
📋 Quick Reference
Method / Property
What it does
canvas.getContext('2d')
Get the 2D drawing context (the "pen")
fillStyle / strokeStyle
Colour for fills / for outlines
fillRect(x,y,w,h)
Draw a filled rectangle
strokeRect(x,y,w,h)
Draw a rectangle outline
clearRect(x,y,w,h)
Erase a rectangular area to transparent
beginPath()
Start a new path (call before each shape)
arc(x,y,r,0,Math.PI*2)
Add a circle (or arc) to the path
moveTo(x,y) / lineTo(x,y)
Move pen without drawing / draw a line
fill() / stroke()
Paint the current path's inside / outline
fillText(text,x,y)
Draw text (y is the baseline)
createLinearGradient(...)
Make a gradient for fillStyle/strokeStyle
drawImage(img,x,y)
Draw a loaded image onto the canvas
requestAnimationFrame(fn)
Run fn before the next repaint (~60fps)
Frequently Asked Questions
🎉 Lesson Complete
- ✅ getContext('2d') hands you the canvas drawing API
- ✅ fillRect , arc , and moveTo / lineTo draw the basic shapes
- ✅ Fill paints the inside; stroke paints the outline
- ✅ createLinearGradient blends colours; fillText renders text
- ✅ Mouse events + lineTo / stroke enable freehand drawing
- ✅ clearRect + requestAnimationFrame produce smooth animation
- ✅ Canvas is pixel-based; SVG is vector-based — choose by use case
- ✅ Size with HTML attributes (not CSS) and add fallback text for accessibility
Up next: SVG Mastery — the vector counterpart to canvas, perfect for crisp icons and diagrams.
Practice quiz
How do you get the 2D drawing context from a canvas element?
- canvas.getContext('2d')
- canvas.draw2d()
- canvas.context('2d')
- canvas.get2D()
Answer: canvas.getContext('2d'). canvas.getContext('2d') returns the 2D drawing context — your 'pen' for all drawing methods.
Where is the origin point (0, 0) on a canvas?
- The center
- The bottom-left
- The top-left
- The top-right
Answer: The top-left. Canvas coordinates start at (0,0) in the top-left; x grows right and y grows downward.
What is the correct way to set a canvas's drawing resolution?
- With CSS width and height
- With the width and height HTML attributes
- With the resolution attribute
- It is set automatically
Answer: With the width and height HTML attributes. Set size with the width/height HTML attributes; CSS only stretches the bitmap and causes blur.
What does fillRect(x, y, w, h) do?
- Draws a rectangle outline only
- Draws a filled rectangle
- Clears a rectangle
- Defines a path
Answer: Draws a filled rectangle. fillRect paints a filled rectangle using fillStyle; strokeRect draws only the outline.
Why should you call ctx.beginPath() before drawing a new shape with a path?
- To clear the canvas
- To set the fill color
- So the new shape isn't joined to the previous path
- To start the animation loop
Answer: So the new shape isn't joined to the previous path. Without beginPath() the path keeps accumulating, so every new arc/line joins the previous shape.
What is the difference between fill() and stroke()?
- fill() paints the inside, stroke() paints the outline
- fill() paints the outline, stroke() paints the inside
- They are identical
- Only fill() works on paths
Answer: fill() paints the inside, stroke() paints the outline. fill() paints the inside of a path using fillStyle; stroke() paints its outline using strokeStyle.
Which method draws a full circle as a path?
- circle(x, y, r)
- arc(x, y, r, 0, Math.PI * 2)
- ellipse(x, y, r)
- drawCircle(x, y, r)
Answer: arc(x, y, r, 0, Math.PI * 2). arc(x, y, radius, 0, Math.PI * 2) sweeps a full 360-degree circle.
Which method should you call each frame to schedule smooth ~60fps animation?
- setInterval(fn, 16)
- requestAnimationFrame(fn)
- setTimeout(fn, 60)
- ctx.animate(fn)
Answer: requestAnimationFrame(fn). requestAnimationFrame(fn) runs fn right before the next repaint, about 60 times per second.
How do you erase a rectangular area of the canvas back to transparent?
- ctx.eraseRect(...)
- ctx.clearRect(x, y, w, h)
- ctx.deleteRect(...)
- ctx.reset()
Answer: ctx.clearRect(x, y, w, h). clearRect(x, y, w, h) erases that rectangle to transparent — used to clear each animation frame.
Compared to canvas, what is SVG better suited for?
- Per-pixel image filters
- Games with many sprites
- Crisp icons and logos that scale
- Real-time particle effects
Answer: Crisp icons and logos that scale. SVG is vector-based, staying sharp at any zoom — ideal for icons, logos, and diagrams.
Continue this course
- Previous: Reusable Components
- Next: SVG Mastery