Topic 5.7
Canvas, a First Look
In one line
<canvas> is a blank rectangle of pixels that JavaScript paints on; it's fast for games, charts and image effects, but what you draw is only pixels, so you must give it a text alternative.
Think of it like this
A whiteboard versus fridge magnets. SVG is like magnets: every shape is a separate object you can move later. Canvas is a whiteboard: once you draw a circle, it's just ink. To move it, you wipe the board and draw everything again in the new place.
Words you'll meet
New words in this topic, in plain English. Come back here whenever one feels fuzzy.
- canvas
- An HTML element that is a blank grid of pixels JavaScript can draw on.
- Bitmap
- An image stored as a grid of pixels.
- Context
- The object you get from
getContext("2d"); it has all the drawing commands. - Immediate mode
- Drawing that becomes pixels at once; the shapes aren't remembered as objects.
- Retained mode
- Drawing where every shape stays an object you can change later (like SVG elements).
- Radians
- A way to measure angles for drawing. A full circle is
2 × π(about 6.28). - Fallback content
- Content inside
<canvas>used when the canvas can't be shown, and as a text alternative.
Step by step
01Add a canvas with fallback
Give it a pixel size and an id. The text inside describes what will be drawn, for anyone who can't see it.
<canvas id="chart" width="300" height="150" role="img"
aria-label="Bar chart: apples 40, bananas 70, cherries 25">
Fruit sold this week: apples 40, bananas 70, cherries 25.
</canvas>02Get the context
Nothing appears until JavaScript draws. getContext("2d") returns the drawing object; it's the same object every time you ask.
const canvas = document.getElementById("chart");
const ctx = canvas.getContext("2d");03Draw rectangles
Set the colour, then fill. Each bar is a rectangle whose top-left y is the canvas height minus the bar's height, because y grows downwards.
const data = [40, 70, 25];
ctx.fillStyle = "steelblue";
data.forEach((value, i) => {
ctx.fillRect(30 + i * 90, 150 - value * 2, 60, value * 2);
});04Draw a circle and text
Circles are paths: begin, add an arc, fill. Text uses a CSS-style font string.
ctx.beginPath();
ctx.arc(270, 25, 15, 0, Math.PI * 2);
ctx.fillStyle = "gold";
ctx.fill();
ctx.fillStyle = "black";
ctx.font = "14px sans-serif";
ctx.fillText("Fruit", 10, 20);05To change it, redraw it
You can't select the gold circle and move it. You clear the canvas with clearRect and draw everything again. Animations do this up to 60 or more times a second with requestAnimationFrame (JavaScript course).
ctx.clearRect(0, 0, canvas.width, canvas.height);
// ...draw everything again in its new positionTry it yourself
- 1
Change the sky
In the first example, predict the first console line if you move the sun to
arc(150, 40, …). (The pixel at 240,40 becomes sky:135,206,235,255.) Run it to check. - 2
Add a fourth bar
In the chart example, add a table row for
Dateswith55. You don't need to touch the JavaScript: predict the console line, then run it. (Hint: the fourth bar starts at x = 300, off the edge. Make the canvas wider to see it.) - 3
Fix the blur
In the third example, change the blurry canvas's attributes to
width="300" height="150"and watch it become sharp.
Code & diagrams
`gold` is rgb(255, 215, 0) and `seagreen` is rgb(46, 139, 87). The only way to ask "what's here?" is to read pixels. The fallback text is a text node, not an element, so `children` is 0.
Preview
Console
Nothing logged yet.Expected console output
Pixel at the sun's centre (r,g,b,alpha): 255,215,0,255
Pixel at the bottom-left (the hill): 46,139,87,255
Shapes in the DOM: 0 (the drawing is only pixels)The numbers live in a real HTML table (hidden visually, still read by screen readers), and the canvas is drawn from it. One source of truth, two ways to perceive it.
Preview
Console
Nothing logged yet.Expected console output
Drew 3 bars from the table: [ 40, 70, 25 ]The top canvas has a third of the pixels, so the browser stretches each one three times wider: soft text and fuzzy curves.
Preview
Console
Nothing logged yet.Expected console output
blurry: bitmap 100x50, shown 300x150
sharp: bitmap 300x150, shown 300x150Break it on purpose
Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.
Break #1
Draw before the canvas exists
Put <script>document.getElementById("c").getContext("2d")</script> in the <head>, above the <canvas>.
Break #2
Size the canvas only with CSS
Write <canvas id="c"></canvas> and canvas { width: 600px; height: 300px; }, then draw a circle at 300,150.
Myth vs fact
Myth
Canvas and SVG do the same thing.
Fact
Both draw graphics, but SVG keeps shapes as styleable, accessible elements, while canvas only keeps pixels. Use SVG for icons and diagrams, canvas for many moving things or pixel effects.
Myth
Text drawn on a canvas can be read by screen readers and search engines.
Fact
It's only pixels. Put the information in fallback content, aria-label, or real HTML next to the canvas.
Myth
A canvas needs a library to work.
Fact
The built-in 2D context draws rectangles, paths, text and images. Libraries such as Chart.js add conveniences on top of it.
Pro corner
Extra depth for experienced readers. New to this? Skip it for now and come back later.
- ▸
For crisp drawing on high-DPR screens:
canvas.width = cssWidth * devicePixelRatio; canvas.height = cssHeight * devicePixelRatio; ctx.scale(devicePixelRatio, devicePixelRatio);and keep the CSS size atcssWidth × cssHeight. - ▸
Drawing an image from another origin without CORS taints the canvas: from then on,
getImageData()andtoDataURL()throw aSecurityError, so a page can't use canvas to read pixels of images it isn't allowed to see. - ▸
getContext()also accepts"webgl","webgl2"and"webgpu"for GPU-accelerated 3D and compute.OffscreenCanvaslets a Web Worker draw off the main thread, keeping the page responsive. - ▸
Focusable elements placed in canvas fallback content (like buttons) are part of the accessibility tree and keyboard order, so you can offer real controls for things drawn on the canvas.
Remember this
- 1
<canvas width="300" height="150">creates a bitmap: a grid of pixels, 300 × 150 by default if you don't give a size. On its own, HTML can't draw anything on it. The canvas needs JavaScript: you ask it for a drawing context, an object full of drawing commands, withcanvas.getContext("2d"), and call methods likefillRect,arcandfillText. - 2
Canvas is immediate mode: each command paints pixels immediately and is forgotten. There is no tree of shapes (unlike SVG's retained mode, where every shape stays an element in the DOM). So the browser can't tell you which shape was clicked, CSS can't style a shape, and screen readers see nothing but a picture. In exchange, drawing thousands of things every frame is fast, which is why games, data visualisations with many points, and photo filters use canvas.
- 3
The 2D context draws with a current style: set
ctx.fillStyle = "tomato"(any CSS colour), thenctx.fillRect(x, y, width, height)fills a rectangle. For other shapes you build a path:ctx.beginPath(), thenctx.arc(x, y, radius, startAngle, endAngle)(angles in radians: a full circle isMath.PI * 2), thenctx.fill()orctx.stroke().ctx.fillText("Hi", x, y)draws text. Like SVG,ygrows downwards from the top-left. - 4
The
widthandheightattributes set how many pixels the bitmap has. CSSwidth/heightonly stretch that bitmap on screen. If they disagree, the drawing is scaled and looks blurry or squashed. On high-density screens (DPR 2+), professionals make the bitmap DPR times bigger and scale the context, so lines stay crisp. - 5
Accessibility fallback. Content between
<canvas>and</canvas>is fallback content: it isn't drawn in browsers that support canvas, but it stays in the page and assistive technology may use it, so it's the place for a text description, a data table, or interactive equivalents. For a simple picture, also addrole="img"and anaria-labeldescribing it. For a chart, give the numbers in a table too; a picture of a chart isn't enough for a screen reader user.
Explain it without notes
Why does <canvas> need JavaScript, and what does getContext("2d") give you?
What is the difference between immediate mode (canvas) and retained mode (SVG), and when would you pick each?
Why can a canvas look blurry, and how do you fix it?
How do you make a canvas chart accessible?
Practice
Draw a 200 × 200 canvas with a red square of side 100 in the centre, and give it a text alternative.
Draw a smiley face (a yellow circle, two black eye circles) on a 150 × 150 canvas.
Trade-offs
- ↔
Canvas is faster for many objects but you rebuild accessibility, hit testing and text yourself; SVG gives those for free but slows down with thousands of elements.
Done when you can
I can create a canvas with a pixel size and fallback content.
I can get the 2D context and draw rectangles, circles and text.
I can explain why canvas needs JavaScript and how it differs from SVG.
I can avoid blurry canvases by matching bitmap and CSS size.
I can give a canvas a proper text alternative.