This guide has a lot of code samples to help you load an image and write text on top of the image for a wide variety of situations.
Some of the topics covered will be:
- Loading an image from a URL or image tag
- Sizing the canvas based on the image size
- Adding text to an image like a watermark
- Solving the “word wrap” problem when adding text to an image using canvas
- Writing text with a custom font (loaded via CSS or JS)
The Main Issue / Challenge / Trick That Comes Up
When trying to add text on top of an image, the main issue that people usually face is the sequence of events..
- We need to first load the image
- After that is complete…
- We need to write the text on top of the image
This seems obvious, but the way the JS code is structured gets tricky..
So, since both the steps are interrelated, this is going to be an “in-depth guide” to both loading the image and THEN writing text on top of it.
Step 1: Loading An Image (A Few Different Ways, Based On Your Needs)
Option 1: Loading An Image From A URL & Setting The Width & Height Of The Canvas To Match The Image
The plan for us to do this is as follows:
- Create a new “Image” via JavaScript (docs from MDN) (let img = new Image();)
- For that Image, write a function to say what happens after the loading of the image is completed. (image.onload = ..)
- Once the image is loaded, we get the width and height of the image, so we can use that to set the width and height of the canvas
- Finally, we draw the image on top of the canvas using the “drawImage” function. (docs from MDN)
The code..
let loadImageOnCanvasAndResizeCanvasToFitImage = (canvas, imageUrl) => { // Get the 2D Context from the canvas let ctx = canvas.getContext("2d"); // Create a new Image let img = new Image(); // Setting up a function with the code to run after the image is loaded img.onload = () => { // Once the image is loaded, we will get the width & height of the image let loadedImageWidth = img.width; let loadedImageHeight = img.height; // Set the canvas to the same size as the image. canvas.width = loadedImageWidth; canvas.height = loadedImageHeight; // Draw the image on to the canvas. ctx.drawImage(img, 0, 0); }; // Now that we have set up the image "onload" handeler, we can assign // an image URL to the image. img.src = imageUrl; };
An here is the live working example..
See the Pen
Canvas Loading A Image From URL by Khoj Badami (@livefiredev)
on CodePen.
The “drawImage” function..
As you can see above, the “drawImage” function is used to draw the text on top of an image.
There are a few different ways you could use the function like:
- drawImage(image, dx, dy)
- drawImage(image, dx, dy, dWidth, dHeight)
- drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight)
Now, in the above code, we have use the first method. But, in other situations, we will have to use the other function signatures.
What the different parameters mean..
This image is from the MDN docs about the function.
As you can see you have a lot of control. Using the parameters, you can take even a small part of the image and place it anywhere on the canvas.
You probably guessed it, but the “s” prefix means “source”. Also the “d” prefix means “destination”.
So, the different parameters mean the following:
- dx: When placing the image on the canvas (in its destination) what should be the x coordinate of the top right corner.
- dy: When placing the image on the canvas (in its destination) what should be the y coordinate of the top right corner.
- dWidth & dHeight: What should be the width and height of the image on the canvas.
- sWidth & sHeight in combination with sx, & sy: What should be the “window” (in terms of width and height, starting at sx and sy) we crop out of the source image to place it on the canvas.
The method signature we have used above is:
drawImage(image, dx, dy)
In this form, we are saying: “I want the whole image. And place it at the dx, dy coordinates on my canvas” (As you can see from the the image above, the 0,0 point is the top left corner.)
Option 2: Loading An Image From An IMG Tag & Setting The Width & Height Of The Canvas To Match The Image
So, here we will assume that you already have an IMG tag on your page with the image. This is not likely, because then you would have 2 places on your page with the same image. But still, lets make that assumption.
Let’s look at the code..
function loadCanvasWithImageBasedOnImagTag(imageID, canvasID) { // get the image let theImage = document.getElementById(imageID); // get the canvas let theCanvas = document.getElementById(canvasID); // get the canvas 2D context let ctx = theCanvas.getContext("2d"); // Create a function to run after the image is loaded let runAfterImageIsLoaded = () => { // set the canvas width and height to the image theCanvas.width = theImage.width; theCanvas.height = theImage.height; // Draw the image on the canvas ctx.drawImage(theImage, 0, 0); } // If we can access the image width, then the image must already be loaded! if ( theImage.width ) { runAfterImageIsLoaded(); } // Just incase the image is not loaded, then we will set up the function to be run // as soon as the image gets loaded.. theImage.onload = runAfterImageIsLoaded; }
And here is a live demo..
See the Pen
HTML5 Canvas Loading An Image From An Image Tag by Khoj Badami (@livefiredev)
on CodePen.
Option 3: Loading An Image Where The Canvas Has A Fixed Size & The Image Is Larger & We FIT The Image Inside The Canvas Without Cropping
In this situation, the image has a width or height that is bigger than the canvas. So, we have scale the image down and centre it and place it in the middle of the canvas.
For example:
- Our image of Mona from Wikipedia is 402×599
- We want to stick it into a canvas of: 400×400
So, the end result will look like this…
The plan to achieve this is as follows:
- Figure out how much we need to “scale down” the image.
- Reduce the width and height of the image by that “scale factor”
- Place the image in the centre of the canvas
If you want a detailed explanation of how this code works check this article. It explains how the “scale image to fit / fill canvas” problem is solved.
The code to achieve all this is as follows:
img.onload = () => { // Once the image is loaded, we will get the width & height of the image let loadedImageWidth = img.width; let loadedImageHeight = img.height; // get the scale // it is the min of the 2 ratios let scale_factor = Math.min( canvas.width/img.width, canvas.height/img.height ); // Lets get the new width and height based on the scale factor let newWidth = img.width * scale_factor; let newHeight = img.height * scale_factor; // get the top left position of the image // in order to center the image within the canvas let x = ( canvas.width/2 ) - ( newWidth/2 ); let y = ( canvas.height/2 ) - ( newHeight/2 ); // When drawing the image, we have to scale down the image // width and height in order to fit within the canvas ctx.drawImage(img, x, y, newWidth, newHeight); };
And here is the working demo…
See the Pen
Loading An Image Where The Canvas Has A Fixed Size, We FIT The Image Inside The Canvas Without Cropping by Khoj Badami (@livefiredev)
on CodePen.
Option 4: Loading An Image That Is Larger Than The Canvas, And Filling The Canvas With The Image (some cropping of the image will occur)
First let us look at how the result looks when an image of size: 402×599 is made to “fill up” a canvas of size: 400×400
The code the achieve this is almost exactly the same as before. The only difference is when we try to calculate the “scale” we look for the max ratio instead of the min. (see line 9 below)
If you want a detailed explanation of how this code works check this article. It explains how the “scale image to fit / fill canvas” problem is solved.
Below is the code:
// Setting up a function with the code to run after the image is loaded img.onload = () => { // Once the image is loaded, we will get the width & height of the image let loadedImageWidth = img.width; let loadedImageHeight = img.height; // get the scale // it is the min of the 2 ratios let scaleFactor = Math.max( canvas.width/img.width, canvas.height/img.height); // Finding the new width and height based on the scale factor let newWidth = img.width * scaleFactor; let newHeight = img.height * scaleFactor; // get the top left position of the image // in order to center the image within the canvas let x = ( canvas.width/2 ) - ( newWidth/2 ); let y = ( canvas.height/2 ) - ( newHeight/2 ); // When drawing the image, we have to scale down the image // width and height in order to fit within the canvas ctx.drawImage(img, x, y, newWidth, newHeight); };
And here is the code in action..
See the Pen
Untitled by Khoj Badami (@livefiredev)
on CodePen.
Step 2: Writing Text On Top Of An Image (A Few Different Ways, Based On Your Needs)
Now that the image is loaded, we can start to write text on top of it.
Below we will look at some code snippets to handle various text writing needs.
Adding Text To An Image Like A Watermark
Here is a function that can be used..
let loadImageOnCanvasAndThenWriteText = (canvas, imageUrl, textToWrite, textStyleOptions, xCordOfText, yCordOfText) => { // Get the 2D Context from the canvas let ctx = canvas.getContext("2d"); // Create a new Image let img = new Image(); // Setting up a function with the code to run after the image is loaded img.onload = () => { // Once the image is loaded, we will get the width & height of the image let loadedImageWidth = img.width; let loadedImageHeight = img.height; // Set the canvas to the same size as the image. canvas.width = loadedImageWidth; canvas.height = loadedImageHeight; // Draw the image on to the canvas. ctx.drawImage(img, 0, 0); // Set all the properties of the text based on the input params ctx.font = `${textStyleOptions.fontSize}px ${textStyleOptions.fontFamily}`; ctx.fillStyle = textStyleOptions.textColor; ctx.textAlign = textStyleOptions.textAlign; // Setting this so that the postion of the text can be set // based on the x and y cord from the top right corner ctx.textBaseline = "top"; // Finanlly addeing the text to the image ctx.fillText(textToWrite, xCordOfText, yCordOfText); }; // Now that we have set up the image "onload" handeler, we can assign // an image URL to the image. img.src = imageUrl; };
As you can see above, once the image is loaded, we start to draw the text on the image.
The code to call the function looks like this..
// Setting up the canvas let theCanvas = document.getElementById("myCanvas"); // Some image URL.. let imageUrl = "https://livefiredev.com/wp-content/uploads/2022/08/mona-lisa.jpeg"; let textStyleOptions = { fontSize: 30, fontFamily: "Arial", textColor: "rgba(255, 255, 255, 0.4)", textAlign: "left" }; let textToWrite = "© daVinci - Year 1503"; let xCordOfText = 10; let yCordOfText = 10; // Load image on the canvas & then write text loadImageOnCanvasAndThenWriteText( theCanvas, imageUrl, textToWrite, textStyleOptions, xCordOfText, yCordOfText );
Finally here is a working demo..
See the Pen
Adding Text To An Image Like A Watermark by Khoj Badami (@livefiredev)
on CodePen.
Writing Text On Top Of An Image With Word Wrap
HTML5 Canvas does not have any built in “line break” and “word wrap” system. However, we can use the “measureText“ function to figure out how much space the text to be drawn will take.
Below is a function you can use for this purpose..
function getLines(ctx, text, maxWidth) { var words = text.split(" "); var lines = []; var currentLine = words[0]; for (var i = 1; i < words.length; i++) { var word = words[i]; var width = ctx.measureText(currentLine + " " + word).width; if (width < maxWidth) { currentLine += " " + word; } else { lines.push(currentLine); currentLine = word; } } lines.push(currentLine); return lines; }
I have modified the code in the above example to use this function. The new function is as follows:
let loadImageOnCanvasAndThenWriteText = ( canvas, imageUrl, textToWrite, textStyleOptions, xCordOfText, yCordOfText, textBoundingBoxWidth ) => { // Get the 2D Context from the canvas let ctx = canvas.getContext("2d"); // Create a new Image let img = new Image(); // Setting up a function with the code to run after the image is loaded img.onload = () => { // Once the image is loaded, we will get the width & height of the image let loadedImageWidth = img.width; let loadedImageHeight = img.height; // Set the canvas to the same size as the image. canvas.width = loadedImageWidth; canvas.height = loadedImageHeight; // Draw the image on to the canvas. ctx.drawImage(img, 0, 0); // Set all the properties of the text based on the input params ctx.font = `${textStyleOptions.fontSize}px ${textStyleOptions.fontFamily}`; ctx.fillStyle = textStyleOptions.textColor; ctx.textAlign = textStyleOptions.textAlign; // Setting this so that the postion of the text can be set // based on the x and y cord from the top right corner ctx.textBaseline = "top"; // Get lines array let arrayOfLines = getLines(ctx, textToWrite, textBoundingBoxWidth); // Set line height as a little bit bigger than the font size let lineheight = textStyleOptions.fontSize + 10; // Loop over each of the lines and write it over the canvas for (let i = 0; i < arrayOfLines.length; i++) { ctx.fillText(arrayOfLines[i], xCordOfText, yCordOfText + ( i * lineheight ) ); } }; // Now that we have set up the image "onload" handeler, we can assign // an image URL to the image. img.src = imageUrl; };
As you can see, it now takes an extra parameter that defines the “width of the bounding box” around the text.
Lets have a look at all of this in a working demo..
See the Pen
Writing Text On Top Of An Image With Word Wrap by Khoj Badami (@livefiredev)
on CodePen.
Writing Text On Top Of An Image With Word Wrap (Center Align)
Almost everything here remains the same. The reason I am mentioning this is because you need to look out for some unexpected behaviour.
When you set the “textAlign” property as “center”, the middle of the text becomes the “x” and “y” cord of the text on which everything is based. So, you need to set it appropriately.
Below is a working example..
See the Pen
Writing Text On Top Of An Image With Word Wrap (Center Align) by Khoj Badami (@livefiredev)
on CodePen.
Writing Text On Top Of An Image With A Custom Font (Loaded Via CSS)
So, when you load fonts via CSS, something like..
<link href="https://fonts.googleapis.com/css2?family=Mouse+Memoirs&display=swap" rel="stylesheet">
In the “head” section of the document, the font can be used to write text on the canvas just like any other text.
But, there is an issue to look out for…
You might find that “sometimes, the font loads and sometimes it does not“. This is happening because the font is loaded after the JS is run.
You can fix this by doing the following….
// Use the JS font load function as below // and run all your canvas setting up code only after // the font is completed loading.. document.fonts.load('100px "Mouse Memoirs"').then(() => { // Setting up the canvas let theCanvas = document.getElementById("myCanvas"); // Some image URL.. let imageUrl = "https://livefiredev.com/wp-content/uploads/2022/08/mona-lisa.jpeg"; let textStyleOptions = { fontSize: 25, fontFamily: "Mouse Memoirs", textColor: "rgba(255, 255, 255, 0.9)", textAlign: "left" }; let textToWrite = "The Mona Lisa is one of the most valuable paintings in the world."; let xCordOfText = 10; let yCordOfText = 10; let textBoundingBoxWidth = 350; // Load image on the canvas & then write text loadImageOnCanvasAndThenWriteText( theCanvas, imageUrl, textToWrite, textStyleOptions, xCordOfText, yCordOfText, textBoundingBoxWidth ); });
In the previous examples, we were doing all of our canvas code after the document was loaded. As you can see above. Now, we are doing it after the custom fonts are loaded.
Below are the MDN docs for the above “load” function.
Here is a live example..
See the Pen
Writing Text On Top Of An Image With A Custom Font (Loaded Via CSS) by Khoj Badami (@livefiredev)
on CodePen.
Writing Text On Top Of An Image With A Custom Font (Loaded Via JS)
Some times, you might need to load the font you want to use on the canvas “dynamically”. Maybe the use is selecting the font from a drop down. So, lets see how to tackle that situation next.
So, in order to get this done, you can use the Web Font Loader JS libray.
Here is a CDN link for the the same..
<script src="https://ajax.googleapis.com/ajax/libs/webfont/1.6.26/webfont.js"></script>
Using the library to load Google fonts is quite simple. See the sample code below..
WebFont.load({ google: { families: ["Mouse+Memoirs:400"] }, active: () => { doAllTheCanvasStuff(); } });
As you can see you just have to specify the font name and weights you want and also what should happen after the fonts are loaded using the “active” key.
Note: This JS library NOT ONLY supports Google Fonts, but many other font sources.
Here is a working example..
See the Pen
Writing Text On Top Of An Image With A Custom Font (Loaded Via JS) by Khoj Badami (@livefiredev)
on CodePen.
Bonus Tip: Add A Dark Overly To Make The Text More Readable When Writing Text Over The Image
Before we write the text, lets just create a slight overlay on top of the image. This will help the text stand out.
All you have to do is create a rectangle and fill the canvas with it after the image is loaded and before you write the text.
// Create a rectangle and fill the canvas with it ctx.fillStyle = 'rgba(0, 0, 0, 0.5)'; ctx.fillRect(0, 0, loadedImageWidth, loadedImageHeight);
Here is the live example.
See the Pen
Add A Dark Overly To Make The Text More Readable When Writing Text Over The Image by Khoj Badami (@livefiredev)
on CodePen.