Command Palette

Search for a command to run...

Hectal
PHASE 12Advanced ~21 min· topic 1 of 7

Topic 12.1

Measuring SVG vs Canvas

In one line

SVG keeps one page element per mark, and the browser pays for every one. Canvas keeps one element and a grid of pixels. You measure the difference on your own device and learn where each one breaks before you choose.

Think of it like this

A wall of sticky notes against a poster. Every note is separate: you can peel it off, move it, write on it. That is wonderful for 25 notes and a nightmare for 25,000, because someone must keep track of every one. A poster is one sheet: it cannot lose a note, but you cannot peel anything off it. SVG is the wall of notes. Canvas is the poster.

Words you'll meet

New words in this topic, in plain English. Come back here whenever one feels fuzzy.

DOM node
One item in the page's tree, such as <circle> or <canvas>. The browser tracks every node.
mark
One drawn item of a chart: a bar, a dot, a line segment.
frames per second
How many pictures the page shows each second. Below about 30, motion looks jerky.
layout and paint
The browser's steps that place every element and turn it into pixels. Both run after your script.

Step by step

01Same 10,000 marks, two ways

The first example draws 10,000 made-up marks (from d3.randomLcg(42), so they never change) as SVG circles and as canvas dots. The pictures match. The page holds 10,000 elements against one canvas, and only 2,483 marks are inside the picture. Timings appear on the page, not in the Console, because they change from machine to machine.

02Count what you cannot see

The marks spread wider than the picture on purpose. The 7,517 outside it are still nodes the browser stores and processes.

03Race them

The widget draws N marks with a live frames-per-second meter. Slide N up and note where each approach drops frames on your device. That number is your own limit.

04Turn the numbers into a rule

The second example, choose, takes the number of marks, whether they are clickable and how often they change. Its table covers zero marks, one mark, NaN and 200,000 marks. Replace its limits with the numbers you measured.

Try it yourself

  1. 1

    Fewer marks

    In the first example, change const N = 10000; to 1000. The Console now says 1,000 circle elements and 261 marks inside the picture. The canvas is still one element.

  2. 2

    Many more marks

    Change const N = 10000; to 100000. The Console says 100,000 circle elements and 25,298 marks inside the picture. The page line now reads (on the machine this was checked on, one reading) about 540 ms for the SVG against about 40 ms for the canvas: ten times the marks, five to seven times the time, and the canvas still one element. Your numbers will differ: that is the point of measuring.

  3. 3

    Keep every mark in view

    Replace random() * 520 - 130 with random() * 260 and random() * 260 - 65 with random() * 130. All 10,000 marks now land inside the picture, so the Console says marks inside the picture: 10,000. The element count does not change: SVG paid for the 7,517 hidden marks before, and pays the same 10,000 now.

  4. 4

    Test the chooser's edges

    In the chooser example, add the cases { marks: Infinity, clickable: false }, { marks: -5, clickable: true } and { marks: "500", clickable: true }. All three print invalid: marks must be a number, 0 or more. A text "500" is rejected on purpose, because Number.isFinite does not turn text into a number. Then add { marks: 800, clickable: false, updatesPerSecond: 60 }: it still says SVG, which is Practice 1.

Code & diagrams

10,000 marks: elements against pixels New tab

The timings are drawn on the page, not logged, because they change from machine to machine. About half of the marks are outside the 260 by 130 picture on purpose.

Preview

Press Run to see the page.

Console

Nothing logged yet.

Expected console output

marks in the data: 10,000
SVG circle elements in the page: 10,000
canvas elements in the page: 1
marks inside the picture: 2,483 (the rest are still elements)
elements you can click one by one: SVG 10,000 - canvas 0

Try it: interactive

Race SVG against canvas

Slide the number of marks up and watch each meter. Note the count where SVG's frames per second fall below 30 on your device.

A chooser with its edge cases New tab

The limits are rules of thumb from one machine. The table includes the edges: no marks, one mark, NaN, a huge number, and clickable marks.

Console

(nothing was printed)

Expected console output

0 marks, clickable true, 0 updates a second: nothing to draw: show an empty-state sentence
1 marks, clickable true, 0 updates a second: SVG: every mark is an element with its own events and focus
25 marks, clickable true, 0 updates a second: SVG: every mark is an element with its own events and focus
365 marks, clickable false, 0 updates a second: SVG: small enough, easy to inspect and style
3000 marks, clickable false, 0 updates a second: SVG works if the chart never changes: measure first
10000 marks, clickable true, 0 updates a second: Canvas: one element, you redraw it and hit-test it yourself
10000 marks, clickable true, 60 updates a second: Canvas: one element, you redraw it and hit-test it yourself
100000 marks, clickable false, 60 updates a second: WebGL: 6,000,000 mark-draws a second is too many for 2D canvas
200000 marks, clickable false, 0 updates a second: Canvas for a still picture, WebGL if it moves or zooms
NaN marks, clickable true, 0 updates a second: invalid: marks must be a number, 0 or more

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

Forget moveTo before each arc

In the first example, delete the line ctx.moveTo(m.x + 2, m.y); from the canvas loop.

terminal
$ In the picture
── what you'll see ──
No error. The right-hand canvas turns into an almost solid teal sheet with thin white streaks, while the SVG on the left is unchanged.

Myth vs fact

Myth

Canvas is always faster than SVG.

Fact

For a few hundred marks SVG is just as fast, and gives you events, styling and accessibility for free. Canvas wins when the number of elements becomes the cost.

Pro corner

Extra depth for experienced readers. New to this? Skip it for now and come back later.

  • ▸

    Script time is not frame time. After your loop the browser runs style, layout and paint, and for 10,000 SVG nodes those can cost more than the script. In the browser's Performance panel, a long "Recalculate Style" or "Layout" bar after your script is the cost of the DOM.

Remember this

  1. 1

    The problem this solves: teams pick SVG or canvas by habit, and then a real dataset freezes the page. Measuring first tells you where your own limit is, on the phones and laptops your users really have.

  2. 2

    What an SVG mark costs. Each <circle> is a node in the page's tree (the DOM). The browser stores it, styles it, lays it out, paints it and keeps it ready for events and screen readers, and repeats that when anything changes. The cost grows with the node count, even for unseen nodes: the first example holds 10,000 circles while only 2,483 are inside the picture.

  3. 3

    What a canvas mark costs. A canvas is one element with a grid of pixels. Drawing a dot sets pixels, and then the dot is gone: the browser keeps no record of it. A hundred thousand dots cost a hundred thousand drawing calls but no hundred thousand nodes. The price: you keep the data, redraw when it changes and work out which mark is under the pointer (Topic 12.3).

  4. 4

    Measured, on one machine. Building 10,000 circles took about 75 to 100 milliseconds of script time over several runs, drawing 10,000 canvas dots about 5 to 6. Those are single readings, not laws, and they leave out the layout and paint that follow and cost SVG more. Phones are usually slower: use the race widget on the device that matters.

  5. 5

    Where each one breaks. SVG breaks on the number of elements and on how often they change: every update makes the browser look at all of them again. Canvas breaks on the number of drawing calls per frame and, for very large canvases, on memory (a canvas of many millions of pixels is a big bitmap, and browsers set their own maximum size). Past that, WebGL hands the drawing to the graphics card, which can draw millions of simple marks, at the price of writing shaders or using a library. The later 3D Web course is coming and covers it; this course stays with 2D canvas.

Explain it without notes

01

Why does an SVG with 10,000 circles cost more than a canvas with 10,000 dots that looks the same?

02

Which things does SVG give you for free that canvas does not?

Practice

01

Change the chooser so 800 marks that change 60 times a second are advised canvas, while 800 static marks stay SVG. (Run it with both cases to prove it.)

02

Write insideView(marks, width, height) that returns how many marks are inside the picture, and 0 for an empty array.

Trade-offs

  • ↔

    SVG gives inspectability and free accessibility, and costs memory and layout time per mark. Canvas gives speed at scale, and costs you hit-testing, accessibility and redrawing.

Done when you can

  • I can say what the browser stores for an SVG circle and for a canvas dot.

  • I know marks outside the view still cost in SVG.

  • I raised the race widget's count until my device dropped below 30 frames per second.

  • I can name what I give up when I choose canvas, and when WebGL comes in.