HTML Canvas

Draw graphics with the canvas element and JavaScript: rectangles, lines, circles, text and images, with colours and gradients, and learn when canvas is the right choice.

Lesson 33 of 44Intermediate15 min read

The <canvas> element is a blank rectangle that you draw on with JavaScript, pixel by pixel. It's how browser games, drawing apps, charts, image editors, photo filters and many data visualisations are made. On its own, a canvas shows nothing; all the drawing happens in a script.

You draw through a context, an object full of drawing commands. The common one is the 2D context: canvas.getContext('2d'). It can fill and outline rectangles, draw lines and curves, write text, paint images and apply colours, gradients and shadows. Coordinates start at the top-left corner at (0, 0), with x increasing to the right and y increasing downwards.

Canvas is very fast for drawing lots of things, but what you draw becomes plain pixels: there are no elements to click or style afterwards, and screen readers can't see the drawing. In this lesson you'll learn the drawing basics, how to make a canvas sharp on high-resolution screens, and when SVG is the better choice.

Getting started

HTML
<canvas id="art" width="300" height="150">A drawing of a red square.</canvas>
<script>
  const canvas = document.getElementById('art');
  const ctx = canvas.getContext('2d');
  ctx.fillStyle = 'crimson';
  ctx.fillRect(20, 20, 100, 60);   // x, y, width, height
</script>
  • The width and height attributes set the drawing size in pixels; the default is 300 by 150.
  • Text between the tags is fallback content for browsers and screen readers that can't show the canvas. Describe what's drawn.
  • Set colours with fillStyle (inside) and strokeStyle (outline) before drawing.

Drawing commands

CommandDraws
fillRect(x, y, w, h) / strokeRect(...)Filled or outlined rectangle
clearRect(x, y, w, h)Erases an area
beginPath(), moveTo(x, y), lineTo(x, y), stroke()Lines and shapes
arc(x, y, r, start, end) then fill()Circles and arcs (angles in radians, a full circle is Math.PI * 2)
fillText(text, x, y)Text, styled with ctx.font
drawImage(img, x, y, w, h)An image, once it has loaded
createLinearGradient(...)A gradient to use as a fill

lineWidth sets the thickness of strokes, and ctx.globalAlpha sets transparency for everything drawn afterwards.

Sharp and responsive canvases

On high-resolution screens a 300 by 150 canvas can look blurry, because each canvas pixel is stretched over several screen pixels. The fix is to draw at a larger size and scale back down:

JavaScript
const ratio = window.devicePixelRatio || 1;
canvas.width = 300 * ratio;
canvas.height = 150 * ratio;
canvas.style.width = '300px';
ctx.scale(ratio, ratio);

For responsive layouts, set the CSS width to 100% and redraw when the size changes. Remember that changing canvas.width clears the drawing.

HTML Canvas Syntax

Canvas syntax

<canvas id="c" width="400" height="200">Description of the drawing</canvas>
<script>
  const ctx = document.getElementById('c').getContext('2d');
  ctx.fillStyle = '#2563eb';
  ctx.fillRect(10, 10, 80, 50);
  ctx.beginPath(); ctx.moveTo(10, 100); ctx.lineTo(200, 100); ctx.stroke();
  ctx.beginPath(); ctx.arc(300, 60, 40, 0, Math.PI * 2); ctx.fill();
  ctx.font = '20px sans-serif'; ctx.fillText('Hello', 10, 160);
</script>

HTML Canvas Summary

  • <canvas> is a drawing surface; JavaScript draws on it through getContext('2d').
  • Coordinates start at the top-left, with y increasing downwards.
  • Use fillRect, paths with moveTo and lineTo, arc, fillText and drawImage to draw.
  • Add fallback text inside the element to describe the drawing.
  • Scale by devicePixelRatio for sharp results; use SVG when you need scalable, accessible shapes.

HTML Canvas Examples

Each example runs in the page. Edit the code and press Run to see what changes.

Example 1

Shapes, lines and text

<canvas id="scene" width="320" height="180" style="border: 1px solid #ddd">A sunny scene with a house and the word Home.</canvas>
<script>
  const ctx = document.getElementById('scene').getContext('2d');
  ctx.fillStyle = '#bfdbfe';
  ctx.fillRect(0, 0, 320, 180);
  ctx.fillStyle = '#fbbf24';
  ctx.beginPath();
  ctx.arc(270, 40, 24, 0, Math.PI * 2);
  ctx.fill();
  ctx.fillStyle = '#f97316';
  ctx.fillRect(110, 90, 100, 70);
  ctx.beginPath();
  ctx.moveTo(100, 90);
  ctx.lineTo(160, 45);
  ctx.lineTo(220, 90);
  ctx.closePath();
  ctx.fillStyle = '#7c2d12';
  ctx.fill();
  ctx.fillStyle = '#1f2937';
  ctx.font = 'bold 18px sans-serif';
  ctx.fillText('Home', 135, 135);
</script>
ResultEdit the code, then press Run or Ctrl+Enter
Line-by-line explanation
  1. Line 1The fallback text describes the picture.
  2. Lines 4 to 5A sky-blue background over the whole canvas.
  3. Lines 7 to 9A sun drawn as a full circle.
  4. Line 11The walls of the house.
  5. Lines 12 to 18A triangular roof made from a path.
  6. Lines 20 to 21Text needs a font before it's drawn.
Example 2

A simple bar chart

<canvas id="chart" width="320" height="200">Bar chart of sales: Mon 12, Tue 19, Wed 7, Thu 15, Fri 22.</canvas>
<script>
  const data = { Mon: 12, Tue: 19, Wed: 7, Thu: 15, Fri: 22 };
  const ctx = document.getElementById('chart').getContext('2d');
  const max = Math.max(...Object.values(data));
  ctx.font = '12px sans-serif';
  Object.entries(data).forEach(([day, value], i) => {
    const h = (value / max) * 150;
    const x = 20 + i * 60;
    ctx.fillStyle = '#16a34a';
    ctx.fillRect(x, 170 - h, 40, h);
    ctx.fillStyle = '#111827';
    ctx.fillText(day, x + 8, 188);
    ctx.fillText(value, x + 12, 165 - h);
  });
</script>
ResultEdit the code, then press Run or Ctrl+Enter
Line-by-line explanation
  1. Line 1The fallback lists every value, so the chart's data is accessible.
  2. Line 5The largest value decides the scale.
  3. Line 8Each bar's height is proportional to its value.
  4. Line 11Bars are drawn upwards from a baseline at y = 170.
  5. Lines 13 to 14Labels for the day and the value.
Example 3

A gradient and an image

<canvas id="card" width="300" height="160">A greeting card with a sunset gradient and a small photo.</canvas>
<script>
  const ctx = document.getElementById('card').getContext('2d');
  const g = ctx.createLinearGradient(0, 0, 0, 160);
  g.addColorStop(0, '#f97316');
  g.addColorStop(1, '#7c3aed');
  ctx.fillStyle = g;
  ctx.fillRect(0, 0, 300, 160);
  const img = new Image();
  img.crossOrigin = 'anonymous';
  img.onload = () => ctx.drawImage(img, 190, 30, 90, 90);
  img.src = 'https://picsum.photos/seed/canvas/90/90';
  ctx.fillStyle = 'white';
  ctx.font = 'bold 22px sans-serif';
  ctx.fillText('Good evening', 16, 85);
</script>
ResultEdit the code, then press Run or Ctrl+Enter
Line-by-line explanation
  1. Lines 4 to 6A vertical gradient from orange to purple.
  2. Lines 7 to 8The gradient is used as the fill.
  3. Lines 9 to 12An image can only be drawn after it has loaded.
  4. Lines 13 to 15White text on top of the gradient.

Common HTML Canvas Mistakes

Sizing a canvas only with CSS

Don't
<canvas id="c" style="width: 600px; height: 300px"></canvas>   <!-- drawn at 300 by 150, then stretched -->
Do
<canvas id="c" width="600" height="300"></canvas>

CSS stretches the default 300 by 150 drawing, making it blurry and distorted.

Drawing an image before it loads

Don't
<script>
  const img = new Image(); img.src = 'photo.jpg';
  ctx.drawImage(img, 0, 0);   // nothing appears
</script>
Do
<script>
  const img = new Image();
  img.onload = () => ctx.drawImage(img, 0, 0);
  img.src = 'photo.jpg';
</script>

Canvas with no fallback text

Don't
<canvas id="chart"></canvas>
Do
<canvas id="chart">Sales rose from 12 orders on Monday to 22 on Friday.</canvas>

Screen readers can't see what's drawn, so describe it.

HTML Canvas in Real Projects

Canvas in the wild

Browser games, online whiteboards like Excalidraw, chart libraries such as Chart.js, photo editors, signature pads on delivery apps, and image compression tools all draw on canvas. Larger 3D experiences use the same element with the WebGL context instead of 2D.

HTML Canvas Exercises

Answer each one and check it instantly. If you get stuck, open the hint before the answer.

Fill in the blankseasy

Get the 2D drawing context.

<script>
  const ctx = canvas.('2d');
</script>
Multiple choiceeasy

Where is the point (0, 0) on a canvas?

Multiple choicemedium

What does arc(50, 50, 20, 0, Math.PI * 2) describe?

Multiple choicemedium

When is SVG usually better than canvas?

Fix the bugmedium

Nothing is drawn: the context is requested with the wrong name and the colour is set after drawing. Fix it so the square is green.

<canvas id="c" width="120" height="120">A green square</canvas>
<script>
  const ctx = document.getElementById('c').getContext('two-d');
  ctx.fillRect(10, 10, 100, 100);
  ctx.fillStyle = 'green';
</script>
ResultEdit the code, then press Run or Ctrl+Enter
Checks
  • The centre pixel is green
  • No errors

HTML Canvas Quiz

5 questions to check what you learned.

  1. What draws on a canvas?

    • JavaScript
    • CSS
    • HTML attributes
    • The browser automatically
  2. What do the width and height attributes of canvas control?

    • The number of drawing pixels
    • Only the visible size
    • The border
    • The zoom level
  3. Which method writes text on a canvas?

    • drawText
    • writeText
    • text
    • fillText
  4. Why do canvases look blurry on high-resolution screens?

    • Canvas doesn't support colour
    • Browsers compress them
    • Each canvas pixel is stretched over several screen pixels
    • The fallback text overlaps
  5. Why is canvas content not accessible by default?

    • It's always hidden
    • It's just pixels, with no elements or text that assistive technology can read
    • It requires a plug-in
    • Canvas can't be used with keyboards

HTML Canvas Challenge

Draw a flag

Draw on a canvas with id="flag", 240 by 160:

  1. Fill the whole canvas with blue (#1d4ed8)
  2. Draw a white rectangle across the middle
  3. Include fallback text inside the canvas element
ResultEdit the code, then press Run or Ctrl+Enter
Checks
  • Canvas is 240 by 160
  • Top-left area is blue
  • Middle is white
  • Has fallback text