Canvas Clipping
Learn how clip() carves the canvas into a restricted drawing region, letting you mask images, panels, and reveal animations into any shape.
What clip() Does
clip() takes whatever path you most recently built with beginPath(), moveTo/lineTo/arc, etc., and turns it into a mask: every drawing operation that happens afterward — fillRect, drawImage, gradients, text — only affects pixels that fall inside that path. Anything outside the path is left completely untouched, as if it were never drawn.
Example 1: Circular Clip Around a Photo
<!DOCTYPE html>
<html>
<body>
<canvas id="scene" width="300" height="300" style="border:1px solid #ccc;"></canvas>
<script>
const canvas = document.getElementById('scene');
const ctx = canvas.getContext('2d');
const photo = new Image();
photo.src = 'assets/photo.jpg';
photo.onload = () => {
ctx.save();
ctx.beginPath();
ctx.arc(150, 150, 80, 0, Math.PI * 2);
ctx.clip(); // everything below is confined to this circle
ctx.drawImage(photo, 70, 70, 160, 160); // the square photo is cropped to a circle
ctx.restore(); // remove the clip so later drawing is unrestricted
};
</script>
</body>
</html>Clipping Regions Intersect, They Don't Replace
Calling clip() a second time does not swap in a new region — it intersects the new path with whatever clip is already active, so the visible area can only shrink, never grow, until you restore() back to a wider (or absent) clip. This makes clipping composable: you can narrow a large region down step by step, for example clipping to a rounded rectangle and then further clipping to a diagonal band inside it.
Example 2: Rounded-Rectangle Avatar Mask
<!DOCTYPE html>
<html>
<body>
<canvas id="scene" width="200" height="200" style="border:1px solid #ccc;"></canvas>
<script>
const canvas = document.getElementById('scene');
const ctx = canvas.getContext('2d');
function clipRoundedRect(x, y, w, h, r) {
ctx.beginPath();
ctx.moveTo(x + r, y);
ctx.arcTo(x + w, y, x + w, y + h, r);
ctx.arcTo(x + w, y + h, x, y + h, r);
ctx.arcTo(x, y + h, x, y, r);
ctx.arcTo(x, y, x + w, y, r);
ctx.closePath();
ctx.clip();
}
const avatar = new Image();
avatar.src = 'assets/avatar.jpg';
avatar.onload = () => {
ctx.save();
clipRoundedRect(40, 40, 120, 120, 18);
ctx.drawImage(avatar, 40, 40, 120, 120);
ctx.restore();
};
</script>
</body>
</html>Clipping vs. Compositing vs. CSS overflow
clip() is a canvas-only, pixel-precise mask that works with any path shape, including curves and text outlines, and it costs nothing extra to combine with transformations. CSS overflow: hidden only clips to rectangles and only affects DOM boxes, not canvas drawing operations. The globalCompositeOperation value 'destination-in' achieves a similar masking look by blending two already-drawn layers together instead of restricting a path — reach for clip() when you know the mask shape in advance, and compositing when the mask itself is a drawn image or gradient.
Example 3: Clipped Photo Grid
<!DOCTYPE html>
<html>
<body>
<canvas id="scene" width="330" height="220" style="border:1px solid #ccc;"></canvas>
<script>
const canvas = document.getElementById('scene');
const ctx = canvas.getContext('2d');
const thumbSize = 100;
const gap = 10;
const photos = [
'assets/photo1.jpg',
'assets/photo2.jpg',
'assets/photo3.jpg',
'assets/photo4.jpg',
'assets/photo5.jpg',
'assets/photo6.jpg'
].map((src) => Object.assign(new Image(), { src }));
Promise.all(photos.map((photo) => new Promise((resolve) => {
photo.onload = resolve;
}))).then(() => {
photos.forEach((photo, i) => {
const x = (i % 3) * (thumbSize + gap);
const y = Math.floor(i / 3) * (thumbSize + gap);
ctx.save();
ctx.beginPath();
ctx.rect(x, y, thumbSize, thumbSize);
ctx.clip();
ctx.drawImage(photo, x, y, thumbSize, thumbSize);
ctx.restore();
});
});
</script>
</body>
</html>- Circular or rounded-rectangle avatar and thumbnail masks
- Custom-shaped panels and cards that aren't plain rectangles
- Reveal/wipe transition animations that grow a clip region over time
- Cropping a video frame or sprite into a non-rectangular viewport
- Confining a busy background pattern or texture to a text outline
Exercise: Canvas Clipping
What does calling clip() do on the current path?