SVG & Canvas

HTML · Lesson 19 of 44 8 min read Updated

Not every graphic has to be a photo file. Icons, logos, charts, diagrams and even games can be drawn by the browser itself. HTML gives you two tools for this: SVG, where you describe shapes in markup, and canvas, where JavaScript paints pixels onto a drawing surface.

In this lesson you will draw shapes with inline SVG, style them with CSS, draw on a canvas with a few lines of JavaScript, and learn when each tool is the better choice.

SVG: graphics made of shapes

SVG stands for Scalable Vector Graphics. A vector graphic is stored as a description of shapes ("a red circle here, a line there") rather than as a grid of coloured dots. Because the browser redraws the shapes at whatever size you need, SVG stays perfectly sharp on any screen, from a small phone to a 4K monitor.

You can write SVG directly inside your HTML. This is called inline SVG:

HTMLTry it yourself
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Inline SVG</title>
</head>
<body>
  <h1>Cricket pitch</h1>
  <svg width="300" height="160" viewBox="0 0 300 160" role="img" aria-label="A cricket pitch on green grass with a red ball near the crease">
    <rect x="0" y="0" width="300" height="160" fill="#4caf50" />
    <rect x="110" y="10" width="80" height="140" fill="#d7b98e" />
    <line x1="110" y1="40" x2="190" y2="40" stroke="white" stroke-width="3" />
    <circle cx="150" cy="100" r="8" fill="#c62828" />
    <text x="10" y="25" fill="white" font-family="sans-serif" font-size="16">Pitch</text>
  </svg>
</body>
</html>

The <svg> element is the drawing area. Inside it, each shape is an element with attributes for its position, size and colour. Later shapes are drawn on top of earlier ones, so the field comes first and the ball comes last.

Coordinates and viewBox

SVG measures from the top-left corner. x grows to the right and y grows downwards, which surprises people who remember graphs from maths class.

The viewBox="0 0 300 160" attribute sets up the drawing's own coordinate system: it starts at 0, 0 and is 300 units wide and 160 units tall. The width and height attributes set how big it appears on the page. If you change width to 600 and height to 320, the whole drawing doubles in size and stays sharp, because the shapes are simply redrawn.

The basic shapes

ElementDrawsKey attributes
<rect>Rectanglex, y, width, height, rx for round corners
<circle>Circlecx, cy (centre), r (radius)
<ellipse>Ovalcx, cy, rx, ry
<line>Straight linex1, y1, x2, y2
<polygon>Closed shape with straight sidespoints
<path>Any shape, including curvesd
<text>Textx, y (the left end of the text baseline)

Colours are set with fill (the inside) and stroke (the outline), with stroke-width for the outline thickness. The <path> element is the most powerful, but its d attribute is a compact mini-language; most people draw complex paths in a design tool such as Inkscape or Figma and export them.

Styling and labelling SVG

Inline SVG shapes are real elements in the page. That means CSS can style them and JavaScript can change them, just like paragraphs and headings. This bar chart shows runs scored in each over. Hover over a bar to see CSS at work:

HTMLTry it yourself
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>SVG bar chart</title>
  <style>
    .bar { fill: #2563eb; }
    .bar:hover { fill: #f97316; }
    .label { font: 14px sans-serif; text-anchor: middle; }
  </style>
</head>
<body>
  <h2>Runs per over</h2>
  <svg width="320" height="170" viewBox="0 0 320 170" role="img" aria-label="Bar chart of runs per over: 4, 9, 6, 12 and 7">
    <line x1="10" y1="150" x2="310" y2="150" stroke="#333" stroke-width="2" />
    <rect class="bar" x="25" y="110" width="40" height="40" />
    <rect class="bar" x="80" y="60" width="40" height="90" />
    <rect class="bar" x="135" y="90" width="40" height="60" />
    <rect class="bar" x="190" y="30" width="40" height="120" />
    <rect class="bar" x="245" y="80" width="40" height="70" />
    <text class="label" x="45" y="104">4</text>
    <text class="label" x="100" y="54">9</text>
    <text class="label" x="155" y="84">6</text>
    <text class="label" x="210" y="24">12</text>
    <text class="label" x="265" y="74">7</text>
  </svg>
</body>
</html>

Two accessibility rules apply to every inline SVG:

  • If the graphic carries meaning, add role="img" and an aria-label that says what it shows, as both examples above do. Screen readers then announce it as one image with that description.
  • If the graphic is only decoration, such as an icon next to a text label that already says "Search", add aria-hidden="true" so screen readers skip it.

SVG files in an img element

An SVG can also live in its own .svg file and be shown with a normal <img>, like any other picture. This is simpler, and the browser can cache the file. The trade-off is that your page's CSS and JavaScript cannot reach inside it.

HTML
<img src="https://course.ethicscomputer.in/assets/samples/logo.svg" alt="Chai Corner" width="240" height="80">

A standalone .svg file must start its <svg> element with xmlns="http://www.w3.org/2000/svg". Inline SVG inside an HTML page does not need it.

Canvas: a surface for painting pixels

The <canvas> element is an empty rectangle of pixels. On its own it shows nothing: you draw on it with JavaScript. Here is the same runs chart, painted on a canvas:

HTMLTry it yourself
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Canvas chart</title>
</head>
<body>
  <h2>Runs per over</h2>
  <canvas id="chart" width="320" height="170">
    Bar chart of runs per over: 4, 9, 6, 12 and 7.
  </canvas>
  <script>
    const canvas = document.querySelector("#chart");
    const ctx = canvas.getContext("2d");
    const runs = [4, 9, 6, 12, 7];

    ctx.strokeStyle = "#333";
    ctx.lineWidth = 2;
    ctx.beginPath();
    ctx.moveTo(10, 150);
    ctx.lineTo(310, 150);
    ctx.stroke();

    ctx.font = "14px sans-serif";
    ctx.textAlign = "center";
    runs.forEach((score, i) => {
      const x = 25 + i * 55;
      const height = score * 10;
      ctx.fillStyle = "#2563eb";
      ctx.fillRect(x, 150 - height, 40, height);
      ctx.fillStyle = "#111";
      ctx.fillText(String(score), x + 20, 150 - height - 6);
    });
  </script>
</body>
</html>

How it works:

  • getContext("2d") gives you a drawing context, an object with methods for drawing in two dimensions.
  • fillStyle and strokeStyle set the colours; fillRect(x, y, width, height) paints a filled rectangle.
  • beginPath, moveTo, lineTo and stroke draw a line. fillText paints text.
  • The text between <canvas> and </canvas> is fallback content. Browsers that can draw ignore it, but it gives assistive tools something to work with.

The coordinates work just like SVG: from the top-left corner, with y growing downwards. The drawing loop uses ordinary JavaScript, which you will learn in the JavaScript tutorial.

Things to know about canvas

  • Set the size with attributes. A canvas is 300 by 150 pixels unless you set width and height. If you resize it only with CSS, the existing pixels are stretched and the drawing looks blurry.
  • It is only pixels. Once drawn, a bar is not an element. You cannot hover it with CSS, inspect it in DevTools or select its text. Try hovering over the canvas bars: nothing happens.
  • Screen readers see nothing inside. Always provide the same information as text: fallback content, a caption, or a table near the canvas.

SVG or canvas?

Choose SVG forChoose canvas for
Logos, icons and illustrationsGames and fast animations with many moving objects
Charts and diagrams with a moderate number of itemsCharts with thousands of data points
Graphics that must be sharp at every sizeEditing photos or working with individual pixels
Graphics you want to style with CSS or make accessibleDrawings that change every frame

As a rule of thumb, start with SVG. Move to canvas when you are drawing so many things, or redrawing so often, that separate elements would be too slow.

Common mistakes

  • Expecting y to grow upwards. In both SVG and canvas, y grows downwards from the top.
  • Sizing a canvas only with CSS. Set the width and height attributes to get sharp pixels.
  • Forgetting a text alternative. Add role="img" and aria-label to meaningful SVG, and fallback text or a visible description for canvas.
  • Using canvas for a simple icon. An SVG is lighter, sharper and accessible.
  • Writing the shapes in the wrong order. Later shapes cover earlier ones, so draw the background first.

Key points

  • SVG describes graphics as shapes in markup, so it stays sharp at any size.
  • viewBox sets the drawing's coordinate system; width and height set its size on the page.
  • Inline SVG shapes are real elements you can style with CSS; label meaningful SVG with role="img" and aria-label.
  • <canvas> is a pixel surface that you draw on with JavaScript through getContext("2d").
  • Set canvas size with attributes, and always give canvas content a text alternative.
  • Use SVG for icons, logos and most charts; use canvas for games and very large or fast-changing drawings.

Exercise

Draw a simple traffic signal twice. First in inline SVG: a dark rounded rectangle with three circles (red, yellow, green) stacked from top to bottom, labelled for screen readers. Then on a canvas of the same size, using fillRect for the box and arc with fill for the three lights. Tip: ctx.arc(x, y, radius, 0, Math.PI * 2) describes a full circle.

Show solution
HTML
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Traffic signal</title>
</head>
<body>
  <svg width="80" height="200" viewBox="0 0 80 200" role="img" aria-label="Traffic signal with red, yellow and green lights">
    <rect x="0" y="0" width="80" height="200" rx="12" fill="#222" />
    <circle cx="40" cy="40" r="25" fill="red" />
    <circle cx="40" cy="100" r="25" fill="gold" />
    <circle cx="40" cy="160" r="25" fill="green" />
  </svg>

  <canvas id="signal" width="80" height="200">Traffic signal with red, yellow and green lights.</canvas>
  <script>
    const ctx = document.querySelector("#signal").getContext("2d");
    ctx.fillStyle = "#222";
    ctx.fillRect(0, 0, 80, 200);
    const lights = ["red", "gold", "green"];
    lights.forEach((colour, i) => {
      ctx.fillStyle = colour;
      ctx.beginPath();
      ctx.arc(40, 40 + i * 60, 25, 0, Math.PI * 2);
      ctx.fill();
    });
  </script>
</body>
</html>

Both versions use the same coordinates. The SVG lights are elements you could restyle with CSS later, while the canvas lights are just painted pixels.

Quick Quiz

1. Why does an SVG logo stay sharp when you make it bigger?

2. In SVG and canvas, which way does the y coordinate grow?

3. What is the default size of a <canvas> without width and height attributes?

4. Which is the better choice for a set of icons in a website menu?

Learn HTML with a mentor

Free lessons get you started. Our Full Stack Web Development training adds real projects and code reviews from working developers.

  • Project-based learning
  • Mentor code reviews
  • Free counselling call