How to load an image in an HTML canvas

By

Learn how to load an image into a canvas in Node.js with the canvas package, using loadImage() which returns a promise, then rendering it with drawImage().

~~~

To load an image into a canvas in Node.js, use the loadImage() function from the canvas npm package, then draw it with drawImage().

I was using this package to draw an image server-side using the Canvas API. My use case was generating social media images for blog posts: a 1200x630 image with the post title and my logo on it. That’s why you’ll see those numbers below.

Note: this is how to work with images in a canvas in Node.js, not in the browser. In the browser it’s different.

The canvas package implements the same Canvas API you know from the browser, but on the server. The difference is how images get loaded. In the browser you have Image objects and onload events. In Node.js the package gives us loadImage() instead.

Load the loadImage() function, along with createCanvas:

const { createCanvas, loadImage } = require('canvas')

Create the canvas:

const width = 1200
const height = 630

const canvas = createCanvas(width, height)
const context = canvas.getContext('2d')

Then call loadImage(), which returns a promise when the image is loaded:

loadImage('./logo.png').then(image => {

})

You can also use, inside an async function:

const image = await loadImage('./logo.png')

loadImage() accepts a local file path, like here, or a URL of a remote image.

Once you have the image, call drawImage and pass it with the x, y, width and height parameters:

context.drawImage(image, 340, 515, 70, 70)

The first two numbers are where the top left corner of the image goes on the canvas. The last two are the size to draw it at. Here the logo is drawn 70x70 pixels, near the bottom of the canvas.

Be careful with the width and height values. drawImage() scales the image to whatever you pass, without preserving the aspect ratio. If your logo is 500x300 and you draw it at 70x70, it will look squeezed. The fix is to compute one dimension from the other, using image.width and image.height:

const logoWidth = 70
const logoHeight = (image.height / image.width) * logoWidth
context.drawImage(image, 340, 515, logoWidth, logoHeight)

One more thing to watch: if the file path is wrong, the promise rejects. Inside an async function, wrap the call in a try/catch or the error will crash your script:

try {
  const image = await loadImage('./logo.png')
} catch (err) {
  console.error('could not load the logo', err)
}
~~~

Related posts about js: