In short
The JavaScript Canvas API lets you draw shapes, text, images, charts and animations on a web page. Learn getContext('2d'), paths, arcs, colors and requestAnimationFrame. What is the HTML canvas element?
The JavaScript Canvas API lets you draw shapes, text, images, charts and animations on a web page. Learn getContext('2d'), paths, arcs, colors and requestAnimationFrame.
What is the HTML canvas element?
A <canvas> is an empty rectangle on your page that you can draw on with JavaScript, like a blank sheet of paper. HTML gives you the paper, and the Canvas API gives you the pens, brushes and paint.
To draw, you first get a drawing context with getContext("2d"). The context is an object full of drawing methods. Everything you draw is turned into pixels straight away. The canvas does not remember "there is a circle here"; it only remembers colored dots.
The canvas uses a coordinate system: x counts pixels from the left, and y counts pixels from the top. So (0, 0) is the top-left corner, and y grows as you go down, which is the opposite of the graphs you drew at school.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Canvas basics</title>
<style> canvas { border: 1px solid #ccc; } </style>
</head>
<body>
<canvas id="board" width="400" height="250">
A drawing of a blue square and an outlined orange rectangle.
</canvas>
<script>
const canvas = document.getElementById("board");
const ctx = canvas.getContext("2d"); // the "pen" we draw with
ctx.fillStyle = "royalblue"; // color for filled shapes
ctx.fillRect(20, 20, 100, 100); // x, y, width, height
ctx.strokeStyle = "darkorange"; // color for outlines
ctx.lineWidth = 4;
ctx.strokeRect(150, 40, 200, 120); // an outlined rectangle
ctx.clearRect(45, 45, 50, 50); // erase a hole in the blue square
</script>
</body>
</html>
You will see a blue square with a white hole in the middle and an orange outlined rectangle next to it. fillRect paints a solid box, strokeRect draws only the border, and clearRect erases part of the canvas.
Common mistake: Setting the canvas size with CSS only. The
widthandheightattributes set how many pixels you can draw on. If you only stretch it with CSS, your drawing looks blurry or squashed. Set the attributes, and use CSS only for borders and layout.
How to draw lines, paths and arcs
For any shape that is not a rectangle, you draw a path. A path is like drawing with a pencil before inking: you plan the lines, then you stroke() (draw the outline) or fill() (paint the inside).
| Method | What it does |
|---|---|
beginPath() | Starts a new path (forgets the old one) |
moveTo(x, y) | Lifts the pen and moves it without drawing |
lineTo(x, y) | Draws a straight line to this point |
arc(x, y, radius, start, end) | Draws part of a circle; angles are in radians |
closePath() | Draws a line back to the start point |
stroke() / fill() | Draws the outline / fills the inside |
fillText(text, x, y) | Draws text |
drawImage(img, x, y, w, h) | Draws an image |
Angles in arc use radians, not degrees. A full circle is Math.PI * 2, and half a circle is Math.PI.
<canvas id="shapes" width="400" height="220">A house, a sun and a smile.</canvas>
<script>
const ctx = document.getElementById("shapes").getContext("2d");
// A triangle roof
ctx.beginPath();
ctx.moveTo(40, 120); // left corner
ctx.lineTo(110, 50); // top
ctx.lineTo(180, 120); // right corner
ctx.closePath(); // back to the left corner
ctx.fillStyle = "firebrick";
ctx.fill();
// The house body
ctx.fillStyle = "#f4d58d";
ctx.fillRect(55, 120, 110, 80);
// A sun: a full circle
ctx.beginPath();
ctx.arc(320, 60, 35, 0, Math.PI * 2);
ctx.fillStyle = "gold";
ctx.fill();
// A smile: part of a circle, outline only
ctx.beginPath();
ctx.arc(320, 60, 20, 0.2 * Math.PI, 0.8 * Math.PI);
ctx.strokeStyle = "#333";
ctx.lineWidth = 3;
ctx.stroke();
// Text
ctx.fillStyle = "#333";
ctx.font = "bold 18px system-ui, sans-serif";
ctx.fillText("My house", 200, 190);
</script>
You will see a red triangle roof on a yellow house, a gold sun with a smile, and the words "My house". Notice that every new shape starts with beginPath(). Forget it, and the next fill() or stroke() draws all the old path parts again too.
Colors work like CSS: names ("gold"), hex ("#f4d58d"), rgb() or hsl(). You can also make gradients with ctx.createLinearGradient().
To draw an image, wait until it has loaded, or nothing appears:
const ctx = document.querySelector("canvas").getContext("2d");
const img = new Image();
img.src = "photo.jpg"; // any image in the same folder
img.onload = () => {
ctx.drawImage(img, 10, 10, 160, 120); // x, y, width, height
};
Build a simple bar chart with canvas
Canvas is great for small charts. Here we loop over data and draw one bar per item. The tallest value fills the chart height, and the others are scaled to match.
<canvas id="chart" width="420" height="260">
Monthly sales: Jan 120, Feb 90, Mar 150, Apr 70, May 180.
</canvas>
<script>
const canvas = document.getElementById("chart");
const ctx = canvas.getContext("2d");
const sales = [
{ month: "Jan", value: 120 },
{ month: "Feb", value: 90 },
{ month: "Mar", value: 150 },
{ month: "Apr", value: 70 },
{ month: "May", value: 180 }
];
const chartHeight = 200; // space for the bars
const barWidth = 50;
const gap = 30;
const max = Math.max(...sales.map((s) => s.value));
ctx.font = "14px system-ui, sans-serif";
ctx.textAlign = "center";
sales.forEach((item, i) => {
const barHeight = (item.value / max) * chartHeight; // scale to fit
const x = gap + i * (barWidth + gap);
const y = 220 - barHeight; // bars grow up from y = 220
ctx.fillStyle = "#4f7cff";
ctx.fillRect(x, y, barWidth, barHeight);
ctx.fillStyle = "#222";
ctx.fillText(item.month, x + barWidth / 2, 245); // label under the bar
ctx.fillText(item.value, x + barWidth / 2, y - 6); // value above the bar
});
</script>
You will see five blue bars with the month names below and the numbers on top. May is the tallest bar because it has the highest value. Because y counts from the top, we start each bar at 220 - barHeight so all bars stand on the same base line.
A basic canvas animation with requestAnimationFrame
An animation is just many drawings shown quickly, like a flip book. Each frame you clear the canvas, move things a little, and draw again. requestAnimationFrame (which you met in the performance lesson) runs your function right before the browser paints the screen, usually 60 times per second.
<canvas id="game" width="400" height="200">A ball bouncing inside a box.</canvas>
<script>
const canvas = document.getElementById("game");
const ctx = canvas.getContext("2d");
const ball = { x: 50, y: 50, r: 15, dx: 3, dy: 2 }; // position, radius, speed
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height); // 1. clear the old frame
ball.x += ball.dx; // 2. move
ball.y += ball.dy;
if (ball.x + ball.r > canvas.width || ball.x - ball.r < 0) ball.dx *= -1; // bounce
if (ball.y + ball.r > canvas.height || ball.y - ball.r < 0) ball.dy *= -1;
ctx.beginPath(); // 3. draw
ctx.arc(ball.x, ball.y, ball.r, 0, Math.PI * 2);
ctx.fillStyle = "tomato";
ctx.fill();
requestAnimationFrame(draw); // 4. ask for the next frame
}
requestAnimationFrame(draw);
</script>
You will see a red ball bouncing off all four walls. When the ball reaches an edge, we flip its speed with *= -1, so it moves the other way.
Tip: On high-resolution screens, a canvas can look a bit soft. Multiply the
widthandheightattributes bywindow.devicePixelRatio, keep the CSS size the same, and callctx.scale(devicePixelRatio, devicePixelRatio)once to get sharp drawings.
Canvas vs SVG, and making canvas accessible
SVG is another way to draw on the web. The difference is how the drawing is stored:
| Canvas | SVG | |
|---|---|---|
| How it works | You paint pixels with JavaScript | You describe shapes as elements in the page |
| After drawing | Shapes are forgotten, only pixels remain | Each shape is still an element you can style and click |
| Scaling | Can get blurry when enlarged | Always sharp at any size |
| Best for | Games, many moving objects, photo editing, big data | Icons, logos, simple charts, diagrams |
| Accessibility | Needs extra work | Text inside stays readable |
Because a canvas is only pixels, screen readers cannot "see" what is on it. Always add fallback content between the <canvas> tags, as in the examples above. It describes the drawing for assistive technology. For an important image or chart, you can also add role="img" and an aria-label to the canvas, or show the same data in a table next to it.
For more methods, see the MDN Canvas API guide.
Practice exercise
Draw a simple traffic light: a dark gray rectangle with three circles inside, red at the top, yellow in the middle and green at the bottom. Then add a click listener on the canvas that changes which light is bright and makes the other two darker.
Hint: write a drawLight(active) function that clears the canvas and draws everything again, using a bright color for the active light and a dark one for the others. Call it again inside the click listener.
Key takeaways
- A
<canvas>is a blank drawing area;getContext("2d")gives you the drawing tools. xgrows to the right andygrows downward from the top-left corner.- Use
fillRectfor boxes, andbeginPath,moveTo,lineToandarcwithfill()orstroke()for other shapes. - Animate by clearing, moving and redrawing inside a
requestAnimationFrameloop. - Canvas is only pixels, so add fallback text or an
aria-labelto describe it; choose SVG for simple, scalable graphics.