ctx = canvas.getContext('2d')
const canvas = document.getElementById('c'); const ctx = canvas.getContext('2d'); // Match canvas pixels to display size canvas.width = canvas.offsetWidth * devicePixelRatio; canvas.height = canvas.offsetHeight * devicePixelRatio; ctx.scale(devicePixelRatio, devicePixelRatio);
Origin (0,0) is top-left. X→ right, Y↓ down.
ctx.fillRect (x, y, w, h); // filled ctx.strokeRect(x, y, w, h); // outlined ctx.clearRect (x, y, w, h); // erase → transparent
No beginPath() needed — these are standalone shortcuts.
ctx.beginPath(); // always start here ctx.moveTo(x, y); // move without drawing ctx.lineTo(x, y); // line to point ctx.arc(x, y, r, start, end); // angles in radians ctx.arcTo(x1,y1, x2,y2, r); // rounded corner ctx.bezierCurveTo(cp1x,cp1y, cp2x,cp2y, x,y); ctx.quadraticCurveTo(cpx,cpy, x,y); ctx.rect(x, y, w, h); // rectangle as path ctx.closePath(); // line back to start ctx.fill(); // fill current path ctx.stroke(); // stroke current path ctx.clip(); // use path as clipping mask
Full circle: arc(x, y, r, 0, Math.PI * 2)
ctx.fillStyle = '#e8ff5a'; // colour, rgba(), gradient… ctx.strokeStyle = 'rgba(255,255,255,0.4)'; ctx.lineWidth = 2; ctx.lineCap = 'round'; // butt | round | square ctx.lineJoin = 'round'; // miter | round | bevel ctx.globalAlpha = 0.8; // 0–1 ctx.globalCompositeOperation = 'source-over'; // Gradients const g = ctx.createLinearGradient(x0,y0, x1,y1); g.addColorStop(0, 'red'); g.addColorStop(1, 'blue'); ctx.fillStyle = g; // Shadow ctx.shadowColor = 'rgba(0,0,0,0.5)'; ctx.shadowBlur = 8; ctx.shadowOffsetX = 2; ctx.shadowOffsetY = 2;
ctx.font = 'bold 16px sans-serif'; ctx.textAlign = 'left'; // left|center|right|start|end ctx.textBaseline = 'top'; // top|middle|alphabetic|bottom ctx.fillText ('Hello', x, y); ctx.strokeText('Hello', x, y); ctx.fillText ('Hello', x, y, maxWidth); // optional clamp const m = ctx.measureText('Hello'); m.width; // rendered width in px m.actualBoundingBoxAscent; // useful for precise layout
// source can be: img, canvas, video, ImageBitmap… ctx.drawImage(src, dx, dy); ctx.drawImage(src, dx, dy, dw, dh); // scaled ctx.drawImage(src, sx,sy,sw,sh, dx,dy,dw,dh); // crop→scale // Pixel access const px = ctx.getImageData(x, y, w, h); // px.data — Uint8ClampedArray [r,g,b,a, r,g,b,a, …] ctx.putImageData(px, x, y);
Must be loaded first — use the load event or await createImageBitmap().
ctx.save(); // push state onto stack ctx.translate(x, y); // move origin ctx.rotate(angleInRadians); // rotate around origin ctx.scale(sx, sy); // scale (1 = normal) ctx.transform(a,b,c,d,e,f); // raw matrix multiply ctx.setTransform(a,b,c,d,e,f);// replace matrix entirely // … draw stuff … ctx.restore(); // pop state from stack
Always wrap in save/restore so transforms don't bleed into later drawing. Rotate around a point by translating to it first, then rotating, then drawing centred on (0,0).
let rafId; let lastTimestamp = performance.now(); function draw(timestamp) { ctx.clearRect(0, 0, canvas.width, canvas.height); // timestamp is ms since page load — use for time-based motion // deltaT can be used as a multiplier when things like speed // are stored as pixels per second const deltaT = (timestamp - lastTimestamp) * 0.001; // seconds lastTimestamp = timestamp; // … draw stuff … rafId = requestAnimationFrame(draw); } rafId = requestAnimationFrame(draw); // To stop: cancelAnimationFrame(rafId);
rAF syncs to display refresh (~60fps / 120fps). Prefer time-based values (sin(t), t * speed) over frame-count so motion is consistent at any refresh rate.
Rotating square uses save/restore + translate + rotate. Arc uses beginPath + arc + fill. Sine wave uses moveTo + lineTo in a loop.
The full drawing state is saved — not just the transform. This includes: fillStyle strokeStyle lineWidth lineCap lineJoin globalAlpha globalCompositeOperation font textAlign textBaseline shadowColor shadowBlur and the current clipping region. The stack can be nested as deep as needed.
Forgot beginPath() — previous sub-paths get re-drawn / re-filled.
Blurry canvas on HiDPI — multiply width/height by devicePixelRatio, then scale ctx.
Image not drawing — it hasn't loaded yet; wait for the load event.
Styles bleeding — set fillStyle / strokeStyle inside save/restore blocks.
Rotating around wrong point — translate to the desired pivot first, then rotate.
Canvas size ≠ CSS size — canvas.width sets pixel buffer; CSS sets display size.