HTML canvas vs svg

HTML gives you two very different tools for drawing graphics on a web page: the <canvas> element and <svg> markup. They can look similar in a screenshot — both can produce circles, rectangles, charts, and icons — but they work in completely different ways under the hood. Understanding that difference is essential before you draw a single shape, because picking the wrong one leads to graphics that are slow, inaccessible, or impossible to update the way you want.

Overview: How canvas and svg Actually Work

<canvas> is a single, empty rectangular element. On its own, in plain HTML, it does nothing visible — it is a blank bitmap surface, like an empty painting canvas. Everything you see inside it (lines, shapes, images, text, chart bars) is drawn imperatively with JavaScript, pixel by pixel, using the Canvas 2D or WebGL APIs. Once something is drawn, the browser does not remember it as separate objects. There is no DOM node for “that red circle” — it is just colored pixels. If you want to move the circle, you must erase the canvas and redraw everything from scratch. Because this HTML course does not cover JavaScript, the canvas examples below only show the markup: how to declare the element, size it, and provide fallback content. Drawing onto it is the job of a separate JavaScript course.

<svg> (Scalable Vector Graphics) is the opposite: it is pure markup, parsed by the browser exactly like the rest of your HTML. Every shape — <circle>, <rect>, <line>, <path>, <text> — becomes a real node in the DOM tree, a child of the <svg> root. Because each shape is a DOM element, you can select it, give it an id, style it, and later change it, all without touching JavaScript at all for the initial rendering. SVG shapes are described mathematically (a circle’s center and radius, a path’s curve coordinates), so the browser can redraw them at any size without any blurring or pixelation — that’s the “scalable” part of the name.

In short: canvas is a bitmap you paint on with code; svg is a vector document the browser renders like any other markup. Canvas trades DOM overhead for raw pixel control (useful for games, simulations, and image processing); svg trades some raw performance for accessibility, scalability, and easy styling.

Syntax

<canvas id="myCanvas" width="400" height="300">
  Fallback content for unsupported browsers
</canvas>

<svg width="400" height="300" viewBox="0 0 400 300">
  <!-- shape elements like rect, circle, line, path go here -->
</svg>
Part Belongs to Purpose
width / height both Sets the element’s coordinate space in pixels. On <canvas> this defines the actual bitmap resolution; on <svg> it sets the default rendered size.
Content between the tags <canvas> Fallback content, shown only if the browser doesn’t support canvas (or read by some assistive tech). It is never drawn over.
viewBox <svg> Defines an internal coordinate system (“min-x min-y width height”) that the graphic is scaled to fit inside the element’s rendered box, making the artwork resize cleanly.
Shape elements (<rect>, <circle>, <line>, <path>, <text>…) <svg> Each becomes a real DOM node describing one piece of the drawing declaratively.
id both Used so JavaScript (canvas) or CSS/JavaScript (svg) can reference the element later.

Examples

Example 1: A bare canvas element with fallback content

<canvas id="chart-area" width="400" height="200">
  Your browser does not support the canvas element. Here is a summary of the sales data instead.
</canvas>

Result: The browser reserves a 400×200 pixel box on the page, but because nothing has drawn onto it with JavaScript, that box is completely blank (transparent) — you would not see a border or any content. Only browsers that don’t support <canvas> at all (vanishingly rare today) would display the fallback sentence in its place.

This example matters because it shows the core truth about canvas in an HTML-only context: the tag by itself draws nothing. It simply reserves space and a drawing surface for a script to use later.

Example 2: A basic inline svg graphic

<svg width="200" height="200" viewBox="0 0 200 200">
  <title>Traffic light icon</title>
  <circle cx="100" cy="60" r="40" fill="red" />
  <circle cx="100" cy="140" r="40" fill="green" />
</svg>

Result: Without any JavaScript at all, the browser renders a 200×200 pixel graphic showing a solid red circle stacked above a solid green circle — a simplified traffic light. Because this markup uses declarative shape elements, it appears the instant the HTML parses.

Notice the <title> element: it gives the graphic an accessible name that screen readers can announce, similar to alt text on an image. This is only possible because each shape is a real, addressable element in the document.

Example 3: A realistic side-by-side comparison (a chart)

<svg width="300" height="150" viewBox="0 0 300 150">
  <title>Simple bar chart of quarterly sales</title>
  <rect x="10" y="50" width="40" height="90" fill="steelblue" />
  <rect x="70" y="20" width="40" height="120" fill="steelblue" />
  <rect x="130" y="70" width="40" height="70" fill="steelblue" />
  <text x="15" y="145">Q1</text>
  <text x="75" y="145">Q2</text>
  <text x="135" y="145">Q3</text>
</svg>

<canvas id="sales-chart" width="300" height="150">
  A bar chart showing quarterly sales: Q1 $40k, Q2 $65k, Q3 $55k.
</canvas>

Result: The svg version renders immediately as a small bar chart: three blue bars of different heights (tallest in the middle) sitting above the labels “Q1”, “Q2”, and “Q3”. The canvas version, by contrast, renders as an empty 300×150 box — the exact same chart would require JavaScript code to calculate bar positions and paint them onto the canvas pixel by pixel every time the page loads.

This pair illustrates the practical trade-off directly: svg gave you a finished, inspectable chart using only markup, while canvas needs a programming layer this HTML course doesn’t cover to produce the same result — but that programming layer can also update the chart far more efficiently for things like real-time data or thousands of moving points, which plain svg struggles with at large scale.

Under the Hood: What the Browser Actually Does

When the HTML parser encounters <canvas>, it creates one ordinary DOM node, exactly like a <div>, with an attached internal bitmap buffer. That’s it — the parser does not look inside the tag for graphics instructions, because there aren’t any; the content between the tags is parsed as ordinary fallback HTML and simply not displayed once the canvas is supported and (eventually) drawn on by script.

When the parser encounters <svg>, it switches into a different parsing mode (technically, the SVG namespace) and builds a full sub-tree of DOM nodes for every shape inside it, the same way it builds nodes for <ul> and <li>. Each <rect>, <circle>, or <path> becomes a real, inspectable, styleable element you can find in browser developer tools, right alongside your regular HTML elements. That’s why the bar chart in Example 3 shows up instantly with no code: the browser’s normal rendering pipeline (parse → DOM → render tree → paint) handles it exactly like text and boxes.

Common Mistakes

Mistake 1: Forgetting to set explicit width and height on canvas

<canvas id="preview"></canvas>

This is valid markup, but without width and height attributes, the browser silently falls back to a default bitmap size of 300×150 pixels. If your page layout expects the canvas to fill a much larger area, developers often assume the drawing code is broken when really the canvas itself is simply too small (or, if resized only with CSS, the image inside becomes blurry because the bitmap resolution never changed).

Corrected:

<canvas id="preview" width="600" height="300"></canvas>

Always set the width and height attributes to the actual pixel resolution you want the drawing surface to have.

Mistake 2: Malformed, unclosed shape tags inside svg

<svg width="100" height="100">
  <circle cx="50" cy="50" r="40" fill="blue">
</svg>

Here the <circle> tag is opened but never closed or self-closed, so the parser doesn’t know where it ends. Because svg content is real markup parsed by the same engine as HTML, this kind of mistake breaks the document tree just as an unclosed <div> would.

Corrected — self-close the empty shape element:

<svg width="100" height="100">
  <circle cx="50" cy="50" r="40" fill="blue" />
</svg>

A related mistake worth knowing about even without code: using <img src="chart.svg" alt="Quarterly sales chart"> when you actually need to style or interact with individual pieces of the graphic later. Loading svg as an image file works fine for static icons and logos, but the shapes inside an <img>-referenced file are not exposed in your page’s DOM at all — you get vector quality, but none of svg’s addressability. For that, the svg markup must be inlined directly in the HTML, as in the examples above.

Best Practices

  • Use <svg> for icons, logos, diagrams, and charts with a moderate, mostly-static number of shapes — you get crisp scaling at any zoom level and each part remains selectable and stylable.
  • Use <canvas> for pixel-heavy, frequently redrawn, or interactive-at-scale graphics such as games, image filters, or real-time visualizations with thousands of moving points, where keeping thousands of separate DOM nodes would be slow.
  • Always give <canvas> meaningful fallback content describing what the graphic conveys, since it is invisible to non-supporting browsers, some search engines, and certain assistive tools.
  • Always give important <svg> graphics a <title> (and optionally <desc>) child element so assistive technology can announce what the image represents.
  • Set explicit width and height on both elements to avoid unexpected default sizing or blurry, stretched output.
  • Remember that CSS styling and any interactivity beyond static markup belong to the CSS and JavaScript courses respectively — this lesson only covers the HTML structure of each element.
  • Prefer loading svg as an external <img> file only when you don’t need to style or script its internal shapes; inline it directly in the HTML whenever you do.

Practice Exercises

  • Write a <canvas> element with an id of game-board, a width of 500, a height of 400, and fallback text describing a tic-tac-toe board for browsers that can’t render canvas.
  • Write an inline <svg> that draws a simple smiley face using two small <circle> elements for eyes and one <path> or <line> for a mouth, inside a 150×150 viewBox. Give it a <title> describing the image.
  • Take the malformed svg from Mistake 2 in this lesson and rewrite it with two shapes instead of one (a circle and a rect), making sure every tag is properly closed or self-closed.

Summary

  • <canvas> is a blank bitmap surface; nothing appears inside it until JavaScript draws on it pixel by pixel.
  • <svg> is real markup; every shape becomes an inspectable, styleable DOM node the browser renders immediately, with no scripting required.
  • Canvas content has no fallback rendering path once JavaScript runs; always provide descriptive fallback content inside the tag for accessibility and non-JS contexts.
  • Svg content scales without blurring because shapes are defined mathematically, not as fixed pixels.
  • Choose svg for static or moderately complex graphics that need to be accessible, stylable, and scalable; choose canvas for high-volume, frequently redrawn, or pixel-manipulation-heavy graphics like games and simulations.
  • Malformed, unclosed shape tags break an inline svg document exactly like malformed HTML would — always close or self-close every shape element.