Dynamically Resizing the HTML5 Canvas with Vanilla JavaScript

How to automatically resize the canvas when the window size changes

Karl Lykken
Level Up Coding

--

Image by Author

Often, it is useful for the HTML5 canvas element to be able to dynamically change size to better fit the current window, such as when the window is restored down while running a full screen canvas-based browser game. This tutorial will discuss a simple technique for accomplishing this using vanilla JavaScript, focusing specifically on the case where we want our canvas to be as large as possible while fitting within the resized window. Note that this tutorial assumes basic familiarity with the HTML5 canvas — if you are unfamiliar with it, consider checking out this tutorial on basic canvas usage.

First off, we need to make sure everything drawn on our canvas is sized and positioned using fractions of the canvas width and/or height, rather than specifying sizes or locations in pixels. This way, it will be much easier to keep the relative spacing and sizing consistent when the canvas is resized.

var circleX = canvas.width/16;
var circleY = canvas.height/8;
var circleRad = canvas.height/32;

If we have any text elements written on the canvas, we need to make sure our font sizes are specified as fractions of the width and/or height as well.

var txtFnt = Math.round(canvas.height/32)+"px monospace"; 

Now, we need a way to detect when the window size changes. We can do this using the window object’s addEventListener method.

window.addEventListener("resize", resizeCanvas, false);

The first parameter specifies the type of event being listened for, in this case a resize event at the window level. The second parameter specifies a function to be called when this type of event occurs (we will define our resizeCanvas function shortly). The third parameter, useCapture, determines the ordering to be used if there are nested elements listening for this same event. In our simple example where we only specify one listener, we can stick with the default false value.

Now, when the window is resized, our resizeCanvas function will be triggered. Inside this function, we first need to adjust the canvas.width and canvas.height parameters to fit the new window. While we could just set them to match the window’s new width and height, this could cause issues if the width-to-height ratio of the new window is significantly different from that of our original canvas. Instead, we can try to maintain the same width-to-height ratio in our resized canvas that we use in our original, so whatever is drawn on our canvas will still look as intended.

In the example below, we try to maximize the canvas size while maintaining a 16:9 width-to-height ratio. If the new window height is greater than or equal to (9/16) times the new window width, then we know that we can set our canvas’ width equal to the window width and then be able to set our canvas’ height equal to (9/16)*canvas.width while still fitting within the new window. Otherwise, we know that the available window width must be more than (16/9) times the new window height, so we can set our canvas height equal to the new window height and then set our canvas width equal to (16/9) times the height.

function resizeCanvas() {
//resize canvas
if(window.innerHeight >= (9*window.innerWidth/16)) {
canvas.width = window.innerWidth;
canvas.height = Math.floor(9*canvas.width/16);
}
else {
canvas.height = window.innerHeight;
canvas.width = Math.floor(16*canvas.height/9);
}
ctx = canvas.getContext("2d");

We could use this same technique when we first define our canvas as well if we want to have our canvas use as much of the initial window as possible while maintaining our width-to-height ratio of choice. Otherwise, if we define our initial canvas size based on a number of pixels, we should make sure that the ratio used in our resizeCanvas function matches the ratio of our initial canvas size.

After we update our canvas’ width and height, we still need to update our other sizing and location variables to reflect these new values. We can set them equal to the same ratios as before (though those ratios will now translate into different pixel values), making this easy.

// update dependent variables
circleX = canvas.width/16;
circleY = canvas.height/8;
circleRad = canvas.height/32;
txtFnt = Math.round(canvas.height/32)+"px monospace";

Finally, we need to redraw everything on our canvas to reflect the new sizes and positions. This might be as simple as calling our various custom drawing functions with the revised size and position variables to recreate our image, but the specifics of what is required will vary with our use case. If our canvas reflects a game state dependent on user input, for instance, we may need to check the values of various game state variables to determine what exactly needs to be drawn.

  // re-draw image
drawCircle(circleX,circleY,circleRad);
drawText(txtFnt);
} //end of resizeCanvas function

At this point, window resize events will be automatically detected by our event listener, and our resizeCanvas function will be triggered to update the size of our canvas and redraw our image to fit this new size. Happy coding!

Resources

Source code for my free browser game (FrequenSee) which puts the above techniques into practice: https://github.com/sacrificialprawn/frequensee_game

MDN addEventListener documentation: https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener

Description of event ordering and the useCapture parameter: https://www.quirksmode.org/js/events_order.html

MDN canvas API documentation: https://developer.mozilla.org/en-US/docs/Web/API/Canvas_AP

MDN canvas tutorial: https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial

--

--