The HTML <canvas> element gives you a pixel-level drawing surface directly in the browser. No plugins, no dependencies. If you have ever built a website but never touched Canvas, you are in the right place.
This guide walks you through the 2D Canvas API from the ground up, with interactive examples you can play with as you read. By the end you will know how to draw shapes, build paths, render text, animate particles, and manipulate image pixels.
Setting Up a Canvas
Every Canvas project starts with a <canvas> element and a rendering context. The element itself is just an empty rectangle. All the drawing happens through the 2D context object you get from getContext('2d').
<canvas id="myCanvas" width="600" height="360"></canvas>
<script>
const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');
</script>Canvas sizing
A common mistake is sizing the canvas with CSS alone. CSS changes the display size but not the internal resolution, which produces blurry output. Always set width and height as element attributes. These define the actual pixel buffer.
For sharp rendering on Retina and HiDPI displays, multiply the buffer size by window.devicePixelRatio and then scale the context back down:
const dpr = window.devicePixelRatio || 1;
canvas.width = 600 * dpr;
canvas.height = 360 * dpr;
canvas.style.width = '600px';
canvas.style.height = '360px';
ctx.scale(dpr, dpr);The coordinate system
Canvas uses a standard screen coordinate system. The origin (0, 0) is at the top-left corner. X increases to the right, Y increases downward. Hover over the canvas below to explore coordinates, and click to place marker dots.
Drawing Shapes
Canvas provides built-in methods for rectangles and circles. Rectangles are the only shape with direct methods. Everything else goes through the path API, which we will cover in the next section.
Rectangles
Three rectangle methods exist:
fillRect(x, y, width, height)draws a filled rectangle.strokeRect(x, y, width, height)draws the outline.clearRect(x, y, width, height)erases a rectangular area.
Circles and arcs
There is no circle() method. Instead you use arc(x, y, radius, startAngle, endAngle) inside a path. A full circle is an arc from 0 to Math.PI * 2.
ctx.beginPath();
ctx.arc(200, 150, 60, 0, Math.PI * 2);
ctx.fill();Use the playground below to experiment with both shapes. Toggle between Rectangle and Circle, adjust the position, size, rotation, and shadow, and switch between Fill and Stroke modes.
Paths and Lines
Paths are the foundation of complex Canvas drawing. A path is a list of points connected by line segments or curves. You start with beginPath(), add segments, and then fill or stroke the result.
The key methods are:
moveTo(x, y)lifts the pen and moves to a new starting point.lineTo(x, y)draws a straight line from the current position.quadraticCurveTo(cpx, cpy, x, y)draws a curve using a control point.closePath()draws a line back to the starting point.
ctx.beginPath();
ctx.moveTo(50, 50);
ctx.lineTo(200, 50);
ctx.lineTo(200, 200);
ctx.closePath();
ctx.stroke();Click on the canvas below to place points and watch the path form. Switch between straight lines and curves, and toggle the close-path option to see how closePath() works.
Colors and Gradients
Every fill and stroke operation uses the current fillStyle or strokeStyle. These accept any valid CSS color string. Canvas also supports linear and radial gradients as fill styles.
Linear gradients
A linear gradient is created with createLinearGradient(x1, y1, x2, y2). The four parameters define the start and end points of the gradient line. You then add color stops at positions between 0 and 1.
const grad = ctx.createLinearGradient(0, 0, 600, 0);
grad.addColorStop(0, '#FFB42F');
grad.addColorStop(0.5, '#840d32');
grad.addColorStop(1, '#2a5d8f');
ctx.fillStyle = grad;
ctx.fillRect(0, 0, 600, 360);Radial gradients
createRadialGradient(x1, y1, r1, x2, y2, r2) defines two circles. The gradient radiates from the inner circle to the outer one. If both circles share the same center, you get a concentric effect.
Use the designer below to experiment with both gradient types. Adjust the angle or radius and pick different color stops to see how gradients work.
Text and Transformations
Canvas can render text directly onto the drawing surface using fillText() and strokeText(). You control the font, size, alignment, and baseline through context properties.
ctx.font = '32px sans-serif';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText('Hello Canvas', 300, 180);The transformation stack
Transformations let you move, rotate, and scale the entire coordinate system. The three core methods are translate(), rotate(), and scale(). Canvas applies them in the order you call them.
The critical pattern is save() and restore(). These methods push and pop the current state (transforms, styles, clipping) onto an internal stack. Without them, every transformation would accumulate and affect all subsequent drawing.
The golden rule: always wrap transforms in a
save()/restore()pair. This keeps each drawing operation isolated and prevents transform leaks.
Try the demo below. Type your own text, adjust the font size, rotation, and scale to see how the transformation stack works. Toggle the Glow button for a neon shadow effect.
Animation
Canvas does not animate on its own. You create animation by clearing the canvas and redrawing everything in a loop using requestAnimationFrame().
The basic pattern looks like this:
function animate(timestamp) {
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Update positions
// Draw everything
requestAnimationFrame(animate);
}
requestAnimationFrame(animate);Delta time
For consistent animation speed across different refresh rates (60Hz, 120Hz, 144Hz), use delta time. This means calculating the time elapsed since the last frame and scaling all movement by that value. Without delta time, your animations will run faster on high-refresh-rate displays.
The particle system below demonstrates a full animation loop. Each particle has velocity, gravity, and a limited lifespan. Hover the canvas to repel particles away from your cursor.
Image Manipulation
This is where Canvas truly separates itself from CSS and SVG. The getImageData() method gives you direct access to every pixel as an array of RGBA values. You can read, modify, and write back individual pixels to create filters and effects that would be impossible with CSS alone.
Drawing images
Use drawImage() to place an image onto the canvas. The image source can be an <img> element, another canvas, or a video frame.
const img = new Image();
img.src = 'photo.jpg';
img.onload = function() {
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
};The pixel data API
Once the image is on the canvas, getImageData() returns an ImageData object containing a flat Uint8ClampedArray. Every four consecutive values represent one pixel: Red, Green, Blue, Alpha. For a 600×360 canvas, that is 864,000 values.
const imageData = ctx.getImageData(0, 0, w, h);
const data = imageData.data;
for (let i = 0; i < data.length; i += 4) {
data[i] = 255 - data[i]; // Invert Red
data[i + 1] = 255 - data[i + 1]; // Invert Green
data[i + 2] = 255 - data[i + 2]; // Invert Blue
// data[i + 3] is Alpha, left unchanged
}
ctx.putImageData(imageData, 0, 0);The photo editor below applies real-time pixel manipulation. Each slider modifies the pixel data through a different algorithm. The grayscale, sepia, and invert toggles show classic filter techniques. Pixelate demonstrates a spatial operation that groups pixels into blocks.
Performance Tips
Canvas performance matters once you start animating or processing large amounts of pixel data. Here are the key practices that keep things smooth.
| Technique | What it does | When to use |
|---|---|---|
| Offscreen canvas | Pre-renders static content to a hidden canvas, then copies it with drawImage() | Complex backgrounds, tiled patterns, cached text |
| Batch by style | Groups all shapes of the same color into one path before calling fill() | Many same-colored shapes (particles, charts) |
Avoid getImageData() in loops | Reads the entire pixel buffer, which is slow | Call once, process the array, write back once |
requestAnimationFrame | Syncs to the display refresh rate | Always use this instead of setInterval |
| Integer coordinates | Prevents sub-pixel anti-aliasing overhead | Pixel art, grids, UI elements |
Avoid calling save() and restore() inside tight loops when you can manually reset only the properties you changed. And if part of your scene is static, render it once to a separate canvas and composite it with drawImage() each frame rather than redrawing everything.
FAQs
Common questions about working with the HTML Canvas element.
width and height attributes to your desired resolution, then use CSS max-width: 100% and height: auto on the canvas element. This scales the display without changing the drawing buffer. For dynamic resizing, listen for the resize event and update the buffer dimensions.width and height attributes by window.devicePixelRatio, set the CSS dimensions to the original size, and call ctx.scale(dpr, dpr). This gives you a higher-resolution drawing buffer while keeping the coordinate system in logical CSS pixels.<canvas> tag and use ARIA attributes. For SEO, remember that text drawn on canvas is not indexable, so important content should be in the DOM.Summary
The HTML Canvas API gives you direct control over every pixel on screen. Starting from basic shapes and paths, you can build up to gradients, text rendering, animation loops, and full image processing pipelines. The seven demos in this guide covered the core building blocks. From here, you can explore the full Canvas API on MDN for advanced topics like compositing, clipping paths, and the Path2D object.

