Canvas Images

Learn how to load images asynchronously and paint them onto the canvas with drawImage(), from simple placement to scaling and sprite-sheet cropping.

Getting an Image onto the Canvas

The canvas API cannot draw a file path directly — it draws pixel data from an already-decoded image source such as an <img> element, another <canvas>, a <video> frame, or an ImageBitmap. The most common pattern is to create an Image object in JavaScript, point its src at a file, wait for the browser to decode it, and only then call drawImage().

Example 1: Load and Draw

<!DOCTYPE html>
<html>
<body>

<canvas id="scene" width="300" height="200" style="border:1px solid #ccc;"></canvas>

<script>
const canvas = document.getElementById('scene');
const ctx = canvas.getContext('2d');

const logo = new Image();
logo.src = 'assets/logo.png';

// drawImage() must not run until the browser has decoded pixel data
logo.onload = () => {
  ctx.drawImage(logo, 20, 20);
};
</script>

</body>
</html>

The Three drawImage() Signatures

SignatureWhat It Does
drawImage(image, dx, dy)Draws the image at its natural pixel size, with the top-left corner placed at (dx, dy).
drawImage(image, dx, dy, dWidth, dHeight)Scales the whole image to fit a dWidth x dHeight box placed at (dx, dy).
drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight)Crops an sWidth x sHeight rectangle starting at (sx, sy) from the source image, then draws that crop scaled into the destination box.

Scaling and Cropping in Practice

Because drawImage() always scales the source rectangle to fit the destination rectangle, the same method handles two very different jobs: shrinking or stretching a whole photo to fit a canvas (the four-argument form), and pulling a single animation frame out of a sprite sheet before enlarging it on screen (the nine-argument form). Both examples below reuse the same canvas and image-loading pattern from Example 1.

Example 2: Scale to Fit and Center

<!DOCTYPE html>
<html>
<body>

<canvas id="scene" width="300" height="200" 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 = () => {
  const scale = Math.min(
    canvas.width / photo.width,
    canvas.height / photo.height
  );
  const w = photo.width * scale;
  const h = photo.height * scale;
  const x = (canvas.width - w) / 2;
  const y = (canvas.height - h) / 2;

  ctx.drawImage(photo, x, y, w, h);
};
</script>

</body>
</html>

Example 3: Sprite Sheet Cropping

<!DOCTYPE html>
<html>
<body>

<canvas id="scene" width="340" height="300" style="border:1px solid #ccc;"></canvas>

<script>
const canvas = document.getElementById('scene');
const ctx = canvas.getContext('2d');

const sheet = new Image();
sheet.src = 'assets/hero-sheet.png';

const frameWidth = 64;
const frameHeight = 64;
let frameIndex = 0;

sheet.onload = () => {
  setInterval(() => {
    ctx.clearRect(0, 0, canvas.width, canvas.height);

    const sx = frameIndex * frameWidth;
    ctx.drawImage(
      sheet,
      sx, 0, frameWidth, frameHeight,             // source rectangle
      100, 100, frameWidth * 2, frameHeight * 2   // destination rectangle
    );

    frameIndex = (frameIndex + 1) % 6;
  }, 120);
};
</script>

</body>
</html>
Note: Reading image.width before decode finishes returns 0. Prefer image.decode() over onload when you can: it returns a promise that resolves only once the image is fully decoded and safe to draw, which plays nicer with async/await than the older onload event.
Note: Images loaded from a different origin without a proper CORS response taint the canvas — drawImage() still works, but getImageData() and toDataURL() will throw a SecurityError afterward. Set image.crossOrigin = 'anonymous' before setting src if the server sends Access-Control-Allow-Origin.
  • Generating thumbnails and avatar previews from a larger source image
  • Playing sprite-sheet animations frame by frame in 2D games
  • Watermarking or stamping a logo onto user-uploaded photos
  • Building simple image editors that redraw a photo after each filter or crop
  • Compositing background art and textures behind UI elements drawn with the canvas API

Exercise: Canvas Images

What must happen before drawImage() reliably draws an image element onto canvas?