Difficulty: Advanced
The HTML5 <canvas> element provides a drawable surface that you can use to render graphics, animations, games, data visualizations, and image manipulations using JavaScript. Unlike SVG, which uses a declarative markup approach, canvas is an immediate-mode rendering API - you issue drawing commands that paint pixels directly onto a bitmap, and the canvas does not retain any knowledge of the objects you have drawn.
To start drawing on a canvas, you first get a reference to the element in the DOM, then call getContext('2d') to obtain a CanvasRenderingContext2D object. This context object exposes all the drawing methods: fillRect(), strokeRect(), arc(), lineTo(), fillText(), drawImage(), and many more. The canvas uses a coordinate system where (0, 0) is the top-left corner, x increases to the right, and y increases downward.
The canvas drawing model revolves around paths and fill/stroke operations. You begin a path with beginPath(), define its shape using methods like moveTo(), lineTo(), arc(), and bezierCurveTo(), then render it with fill() (solid fill) or stroke() (outline). You control appearance with properties like fillStyle, strokeStyle, lineWidth, lineCap, lineJoin, font, and textAlign. Styles can be solid colors, gradients (createLinearGradient, createRadialGradient), or patterns (createPattern).
Text rendering on canvas uses fillText(text, x, y) and strokeText(text, x, y). You control the font with the font property (which accepts CSS font shorthand), alignment with textAlign ('left', 'center', 'right'), and baseline with textBaseline ('top', 'middle', 'alphabetic', 'bottom'). Measuring text width before drawing is possible with measureText(text).width, which is essential for centering or wrapping text.
Canvas is pixel-based, which means it does not scale well on high-DPI displays by default. To render sharp graphics on Retina screens, you need to set the canvas width and height attributes to the display size multiplied by window.devicePixelRatio, then scale the context accordingly. Also, since canvas does not retain drawn objects, you must manage your own scene graph if you need hit detection, animation, or interactivity.
<canvas id="myCanvas" width="400" height="300"></canvas>
<script>
const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');
// Filled rectangle
ctx.fillStyle = '#3b82f6';
ctx.fillRect(20, 20, 150, 100);
// Stroked rectangle (outline only)
ctx.strokeStyle = '#ef4444';
ctx.lineWidth = 3;
ctx.strokeRect(200, 20, 150, 100);
// Clear a portion (eraser)
ctx.clearRect(50, 50, 50, 30);
</script>
fillRect draws a solid rectangle. strokeRect draws an outline. clearRect erases pixels in the specified region. The coordinate system starts at (0,0) in the top-left corner.
<canvas id="pathCanvas" width="400" height="300"></canvas>
<script>
const canvas = document.getElementById('pathCanvas');
const ctx = canvas.getContext('2d');
// Draw a triangle
ctx.beginPath();
ctx.moveTo(200, 20); // Top vertex
ctx.lineTo(300, 180); // Bottom-right
ctx.lineTo(100, 180); // Bottom-left
ctx.closePath(); // Connect back to start
ctx.fillStyle = '#10b981';
ctx.fill();
ctx.strokeStyle = '#064e3b';
ctx.lineWidth = 2;
ctx.stroke();
// Draw a circle
ctx.beginPath();
ctx.arc(200, 240, 40, 0, Math.PI * 2); // x, y, radius, startAngle, endAngle
ctx.fillStyle = '#f59e0b';
ctx.fill();
// Draw a semicircle
ctx.beginPath();
ctx.arc(320, 240, 40, 0, Math.PI); // Half circle
ctx.fillStyle = '#8b5cf6';
ctx.fill();
</script>
Paths are defined with beginPath, shape commands (moveTo, lineTo, arc), and rendered with fill/stroke. arc() takes center x/y, radius, start angle, and end angle in radians. Math.PI * 2 is a full circle.
<canvas id="textCanvas" width="400" height="200"></canvas>
<script>
const canvas = document.getElementById('textCanvas');
const ctx = canvas.getContext('2d');
// Filled text
ctx.font = 'bold 32px Arial';
ctx.fillStyle = '#1e293b';
ctx.textAlign = 'center';
ctx.fillText('Hello Canvas!', 200, 50);
// Stroked text (outline)
ctx.font = '28px Georgia';
ctx.strokeStyle = '#3b82f6';
ctx.lineWidth = 1.5;
ctx.strokeText('Outlined Text', 200, 100);
// Measuring text
ctx.font = '16px monospace';
const text = 'Measured width demo';
const metrics = ctx.measureText(text);
ctx.fillStyle = '#64748b';
ctx.textAlign = 'left';
ctx.fillText(`${text} (${metrics.width.toFixed(1)}px wide)`, 20, 160);
</script>
fillText renders solid text; strokeText renders outlined text. The font property uses CSS font shorthand. textAlign controls horizontal alignment relative to the x coordinate. measureText returns the pixel width of the text.
<canvas id="hdCanvas" width="400" height="200"></canvas>
<script>
const canvas = document.getElementById('hdCanvas');
const ctx = canvas.getContext('2d');
// High-DPI fix
const dpr = window.devicePixelRatio || 1;
const rect = canvas.getBoundingClientRect();
canvas.width = rect.width * dpr;
canvas.height = rect.height * dpr;
ctx.scale(dpr, dpr);
canvas.style.width = rect.width + 'px';
canvas.style.height = rect.height + 'px';
// Linear gradient
const gradient = ctx.createLinearGradient(0, 0, 300, 0);
gradient.addColorStop(0, '#3b82f6');
gradient.addColorStop(0.5, '#8b5cf6');
gradient.addColorStop(1, '#ec4899');
ctx.fillStyle = gradient;
ctx.fillRect(20, 20, 300, 60);
// Radial gradient
const radial = ctx.createRadialGradient(170, 140, 10, 170, 140, 60);
radial.addColorStop(0, '#fbbf24');
radial.addColorStop(1, '#ef4444');
ctx.beginPath();
ctx.arc(170, 140, 60, 0, Math.PI * 2);
ctx.fillStyle = radial;
ctx.fill();
</script>
The High-DPI fix scales the canvas buffer to match the display's pixel ratio, ensuring sharp rendering on Retina screens. Gradients are created as reusable fill styles with color stops at positions between 0 and 1.
Canvas Element, getContext, Drawing Shapes, Canvas Text, Coordinate System