SVG & Canvas
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:
<!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
| Element | Draws | Key attributes |
|---|---|---|
<rect> | Rectangle | x, y, width, height, rx for round corners |
<circle> | Circle | cx, cy (centre), r (radius) |
<ellipse> | Oval | cx, cy, rx, ry |
<line> | Straight line | x1, y1, x2, y2 |
<polygon> | Closed shape with straight sides | points |
<path> | Any shape, including curves | d |
<text> | Text | x, 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:
<!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 anaria-labelthat 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.
<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:
<!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.fillStyleandstrokeStyleset the colours;fillRect(x, y, width, height)paints a filled rectangle.beginPath,moveTo,lineToandstrokedraw a line.fillTextpaints 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
widthandheight. 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 for | Choose canvas for |
|---|---|
| Logos, icons and illustrations | Games and fast animations with many moving objects |
| Charts and diagrams with a moderate number of items | Charts with thousands of data points |
| Graphics that must be sharp at every size | Editing photos or working with individual pixels |
| Graphics you want to style with CSS or make accessible | Drawings 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
yto grow upwards. In both SVG and canvas,ygrows downwards from the top. - Sizing a canvas only with CSS. Set the
widthandheightattributes to get sharp pixels. - Forgetting a text alternative. Add
role="img"andaria-labelto 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.
viewBoxsets the drawing's coordinate system;widthandheightset its size on the page.- Inline SVG shapes are real elements you can style with CSS; label meaningful SVG with
role="img"andaria-label. <canvas>is a pixel surface that you draw on with JavaScript throughgetContext("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
<!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?
SVG is a vector format, so the shapes are recalculated for each size.
2. In SVG and canvas, which way does the y coordinate grow?
Both measure from the top-left corner, with y increasing downwards.
3. What is the default size of a <canvas> without width and height attributes?
A canvas is 300 pixels wide and 150 pixels tall unless you set its attributes.
4. Which is the better choice for a set of icons in a website menu?
Icons are small, simple shapes that should be sharp, styleable and accessible, which suits SVG.
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
