Canvas API — 2D Drawing
debt(d7/e3/b5/t7)
Closest to 'only careful code review or runtime testing' (d7). The common mistakes listed — missing beginPath(), wrong canvas sizing, devicePixelRatio issues — produce silent visual bugs (blurriness, unexpected fills, merged paths) that are invisible to compilers and linters. No detection_hints.tools are specified, and no standard linter rule catches these canvas-specific behavioral issues. They only surface when a developer visually inspects the rendered output or runs in a retina environment.
Closest to 'simple parameterised fix' (e3). The quick_fix is a small pattern (ctx.save/ctx.restore wrapping), and the common_mistakes each have a targeted one-to-few-line fix: multiply dimensions by devicePixelRatio, add ctx.beginPath(), set width/height attributes instead of CSS, move computation out of requestAnimationFrame. No single fix is purely one-line since each requires locating all call sites and understanding context, so e3 rather than e1.
Closest to 'persistent productivity tax' (b5). Canvas API applies broadly to any web graphics work. Its global mutable context state (transforms, styles, path accumulation) means every developer touching canvas code must understand the stateful model, ctx.save/restore discipline, and sizing conventions. This is a persistent tax across all canvas-using components, but it doesn't reach into non-graphics parts of the codebase, keeping it from b7.
Closest to 'serious trap' (t7). The misconception field explicitly states developers assume Canvas and SVG are interchangeable, which is a significant architectural wrong turn. Additionally, the stateful path model (beginPath() required) contradicts how most drawing APIs work, and CSS-based sizing silently producing a blurry bitmap contradicts standard web sizing conventions. Multiple documented gotchas contradict how similar web concepts behave elsewhere, warranting t7.
Also Known As
TL;DR
Explanation
A <canvas> element provides a bitmap drawing surface. JavaScript obtains a 2D rendering context via canvas.getContext('2d') and draws using methods like fillRect(), arc(), drawImage(), and fillText(). Canvas is immediate mode — you draw commands execute immediately and the result is just pixels; there is no retained object graph like SVG. This makes Canvas fast for many operations but means you must redraw everything on each frame for animations. The CanvasRenderingContext2D API includes path drawing (beginPath, moveTo, lineTo, arc), fill and stroke styles (colours, gradients, patterns), text rendering, image compositing, and transformations (translate, rotate, scale). For 3D, use WebGL or WebGPU via getContext('webgl2') instead.
Watch Out
Common Misconception
Why It Matters
Common Mistakes
- Not accounting for devicePixelRatio — canvas renders blurry on retina screens; multiply width/height by devicePixelRatio and scale the context.
- Forgetting ctx.beginPath() before drawing paths — paths accumulate; without beginPath(), new shapes join the previous path, producing unexpected fills.
- Setting canvas size with CSS instead of the width/height attributes — CSS scales the bitmap (blurry); the attributes set the actual resolution.
- Doing heavy computation inside the animation loop — move data processing outside requestAnimationFrame and only draw inside it.
Avoid When
- Building interactive diagrams or charts where you need clickable elements, tooltips, or accessibility — use SVG or DOM elements instead.
- Creating scalable graphics that must render at multiple resolutions without pixelation — SVG is resolution-independent.
- Rendering text-heavy documents or requiring complex text layout and selection — the DOM is better suited.
- When you need automatic browser zooming or responsive scaling that works across device pixel ratios without manual management.
When To Use
- You need to render charts, graphs, or data visualizations that update frequently and require pixel-perfect control without the overhead of DOM manipulation.
- Building games or animations where you control every frame and need direct pixel access for collision detection, sprite rendering, or particle effects.
- Performing real-time image manipulation (filters, transformations, compositing) on photos or video frames where SVG's vector approach is unsuitable.
- Creating interactive drawing or annotation tools where immediate-mode rendering and low latency matter more than maintaining an editable object model.
Code Examples
// ❌ Leaking context state, not clearing between frames
const canvas = document.getElementById('c');
const ctx = canvas.getContext('2d');
ctx.fillStyle = 'red'; // Never reset — affects all subsequent draws
ctx.translate(100, 100); // Cumulative! Each frame moves 100px further
function draw() {
// Missing: ctx.clearRect(0, 0, canvas.width, canvas.height)
ctx.fillRect(0, 0, 50, 50); // Draws over previous frame
requestAnimationFrame(draw);
}
// ✅ Proper canvas animation loop
const canvas = document.getElementById('c');
const ctx = canvas.getContext('2d');
let x = 0;
function draw() {
// Clear the entire canvas each frame
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Save/restore to isolate state changes
ctx.save();
ctx.fillStyle = 'royalblue';
ctx.translate(x, 100);
ctx.fillRect(-25, -25, 50, 50); // Centred rect
ctx.restore(); // fillStyle and translate are gone
x = (x + 2) % canvas.width; // Loop across canvas
requestAnimationFrame(draw);
}
// Set canvas resolution to match device pixel ratio (crisp on retina)
const dpr = window.devicePixelRatio || 1;
canvas.width = canvas.offsetWidth * dpr;
canvas.height = canvas.offsetHeight * dpr;
ctx.scale(dpr, dpr);
draw();