Topic 11.1
The Canvas and Its Context
In one line
A canvas is one page element holding a grid of pixels, and the context is the pen you paint on it with. It has two sizes, the grid and the box, and it forgets every shape the moment it is painted.
Think of it like this
A whiteboard. You draw a bar, step back, and the board shows a picture. The board does not remember that you drew a "bar": it only holds the marker's colour where you pressed. To change the picture you wipe the board and draw it all again. A canvas works the same way.
Words you'll meet
New words in this topic, in plain English. Come back here whenever one feels fuzzy.
- canvas
- A page element that holds a grid of pixels you paint with code.
- context
- The object from
getContext("2d")that has the drawing methods. You paint through it. - backing store
- The grid of pixels behind a canvas. Its size is the
widthandheightattributes. - immediate mode
- Drawing commands paint pixels right away and are forgotten. The opposite of SVG, which keeps every shape.
- clearRect
- The context method that makes a rectangle of pixels transparent again.
Step by step
01Look at the two sizes
The first example has two canvases. The first has no sizes at all and is 300 × 150 pixels. The second is sized only with CSS (240 × 60 on the page), but its grid is still 300 × 150.
The code then paints a circle into the second one. The picture shows an oval: 300 × 150 pixels squeezed into 240 × 60. The log shows the grid and the box side by side.
02Paint, then read a pixel
fillRect(10, 10, 100, 100) paints a teal square. getImageData(50, 50, 1, 1) reads one pixel inside it: [42, 157, 143, 255], the colour #2a9d8f at full alpha. A pixel outside is [0, 0, 0, 0]: transparent black.
03See what a size change does
The first example sets scratch.width = 300 on a canvas that already holds a black square. The pixel was [0, 0, 0, 255] before and is [0, 0, 0, 0] after: the assignment erased it. Bad values fall back: 126.25 becomes 126, "abc" becomes 0 and -5 becomes 300.
04Clear and redraw
The second example draws a week of bars with a function that starts by clearing the canvas. Four different inputs go through the same function, so the picture is always the new data and nothing is left over.
A button cycles the weeks. Each click clears and paints again: this is what "update" means for a canvas chart.
Try it yourself
- 1
Fix the oval
In the first example, give the second canvas
width="240" height="60"attributes. Its grid becomes 240 × 60, the log printscss: 240 x 60 shown in a box 240 x 60, and the circle is no longer squeezed. (The radius 60 then does not fit in a 60-pixel-high canvas: it is cut off, so change the arc toarc(120, 30, 25, …).) - 2
Skip the clearing
In the second example, delete the first line of
draw(ctx.clearRect(…)). Click the button: each week is painted on top of the last, because nothing erased it. The empty week leaves the old bars in place.
Code & diagrams
Edge cases in one example: no size, CSS-only size (a stretched oval), bad width values, and a context that is null.
Preview
Console
Nothing logged yet.Expected console output
plain: 300 x 150
css: 300 x 150 shown in a box 240 x 60
inside the square: [ 42, 157, 143, 255 ]
outside the square: [ 0, 0, 0, 0 ]
before width = 300: [ 0, 0, 0, 255 ]
after width = 300: [ 0, 0, 0, 0 ]
126.25 -> 126, abc -> 0, -5 -> 300, 0 -> 0
same context again: true
webgl on a 2d canvas: null
unknown type: nullThe same function handles a normal week, zeros, an empty array and one value. Press the button to see each picture.
Preview
Console
Nothing logged yet.Expected console output
week 1: 7 values, 7 bars painted
week 2: 7 values, 3 bars painted
week 3: 0 values, 0 bars painted
week 4: 1 values, 1 bars painted
child elements in the canvas: 0
pixel at the top of Tuesday's bar: [ 42, 157, 143, 255 ]Break it on purpose
Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.
Break #1
Use a context that is null
At the end of the first example add const gl = c.getContext("webgl"); gl.clearColor(0, 0, 0, 1); (the canvas c already has a 2D context).
Myth vs fact
Myth
A canvas is faster than SVG.
Fact
It scales better with thousands of marks, because it keeps no per-mark objects. For a handful of marks, SVG is as fast and keeps your shapes inspectable, clickable and styleable (Topic 12.1 measures where the line is).
Myth
Setting the CSS size sets the canvas size.
Fact
CSS only sets the box. The grid is the width and height attributes. Mixing them up gives stretched, blurry pictures.
Pro corner
Extra depth for experienced readers. New to this? Skip it for now and come back later.
- ▸
Browsers cap the size of the grid, and phones allow less. Past the limit the canvas turns into a silent blank, with no error. In this course's Chromium a 16,384 × 16,384 canvas still painted, while 16,385 × 16,384 and 20,000 × 20,000 read back all zeros: the cap there is on the total pixel count (16,384 squared), not on each side, and other browsers differ. Each pixel costs 4 bytes, so 1,600 × 900 is about 5.8 MB.
- ▸
The default 300 × 150 is the HTML spec's default and is the grid size. The element's default CSS box is the same, so
<canvas>alone looks right and fools you until you size it with CSS only.
Remember this
- 1
The problem this solves: SVG keeps one page element for every mark, and 10,000 marks means 10,000 elements for the browser to track (Topic 0.3). A canvas keeps none: it is one element with a grid of pixels, so the cost stays the same however many marks you paint. In return, the shapes are not yours to inspect or click any more.
- 2
Two objects: the canvas and its context. The
<canvas>element is the grid. You never draw on the element itself: you callcanvas.getContext("2d")and get a context, an object with the drawing methods (fillRect,arc,fillText…). Asking twice returns the same object. Asking for a type the browser cannot give (or a different type after"2d", such as"webgl") returnsnull, so a carelessctx.fillRect(...)then throws aTypeError. - 3
Two sizes. The width and height attributes set the number of pixels in the grid, 300 × 150 if you set nothing. CSS
widthandheightset the size of the box on the page. If they differ, the browser stretches the grid to fit the box, and a circle becomes an oval. The first example shows both. In SVG a stretched picture stays sharp; in a canvas it is only a stretched bitmap. - 4
Immediate mode: paint and forget.
fillRectchanges pixels and that is all. The canvas has no list of rectangles, no children, and nothing to ask "which bar is this?" (canvas.children.lengthis 0). To change the picture you clear it withclearRectand paint it all again. Libraries and your own code keep the data; the canvas keeps pixels. Phase 12 shows how to click things anyway. - 5
Changing a size erases the canvas. Assigning
canvas.widthorcanvas.height, even to the value it already has, clears every pixel and resets the context's settings (colour, transform, line width). The attribute is read as a whole number:126.25becomes126,"abc"becomes0, and a negative number falls back to300. A canvas 0 pixels wide draws nothing and raises no error.
Explain it without notes
What are the canvas's two sizes, and what happens if they differ?
Why does a canvas have no "update this bar" operation?
What does canvas.width = canvas.width do?
Practice
Write setSize(canvas, w, h) that sets the grid size and returns the context, throwing a clear error when w or h is not a positive number.
Write paintSquares(ctx, counts) that clears the canvas, then paints one 10-pixel square per entry in counts in a row, painting a "No data" sentence when counts is empty.
Trade-offs
- ↔
Canvas keeps nothing per mark, so it scales to huge counts, but you give up inspection, CSS styling, built-in events and free accessibility.
Done when you can
I can name the canvas's two sizes and say which one sets the pixels.
I can explain immediate mode and why a canvas has no children.
I can say what erases a canvas and what a size change resets.
I check
getContextfornulland handle empty data on purpose.