The <canvas>
element is one of the most widely used standards for rendering 2D graphics on the Web. It is used widely in games and complex visualizations. However, as Web sites and apps push canvas to the limits, performance begins to suffer. This article aims to provide suggestions for optimizing your use of the canvas element, to ensure that your Web site or app performs well.
Performance tips
The following is a collection of tips to improve performance
Pre-render similar primitives or repeating objects on an off-screen canvas
If you find yourself with complex drawing operations on each frame, consider creating an offscreen canvas, draw to it once (or whenever it changes) on the offscreen canvas, then on each frame draw the offscreen canvas.
myEntity.offscreenCanvas = document.createElement("canvas"); myEntity.offscreenCanvas.width = myEntity.width; myEntity.offscreenCanvas.height = myEntity.height; myEntity.offscreenContext = myEntity.offscreenCanvas.getContext("2d"); myEntity.render(myEntity.offscreenContext);
Avoid floating-point coordinates and use integers instead
Sub-pixel rendering occurs when you render objects on a canvas without whole values.
ctx.drawImage(myImage, 0.3, 0.5);
This causes the browser to do extra calculations to create the anti-aliasing effect. To avoid this, make sure to round all co-ordinates used in calls to drawImage()
using Math.floor()
, for example.
Don’t scale images in drawImage
Cache various sizes of your images on an offscreen canvas when loading as opposed to constantly scaling them in drawImage()
.
Use multiple layered canvases for complex scenes
You may find you have some elements that are frequently changing and moving around whereas other things (like UI) never change. An optimization in this situation is to create layers using multiple canvas elements.
For example you could create a UI layer that sits on top of everything and is only drawn during user input. You could create game layer where the frequently updating entities exist and a background layer for entities that rarely update.
<div id="stage"> <canvas id="ui-layer" width="480" height="320"></canvas> <canvas id="game-layer" width="480" height="320"></canvas> <canvas id="background-layer" width="480" height="320"></canvas> </div> <style> #stage { width: 480px; height: 320px; position: relative; border: 2px solid black } canvas { position: absolute; } #ui-layer { z-index: 3 } #game-layer { z-index: 2 } #background-layer { z-index: 1 } </style>
CSS for large background images
If like most games you have a static background image, use a plain <div>
element with a CSS background
property and position it under the canvas. This will avoid drawing a large image to the canvas on every tick.
Scaling canvas using CSS transforms
CSS transforms are faster by using the GPU. Best case is to not scale the canvas or have a smaller canvas and scale up rather than a bigger canvas and scale down. For Firefox OS, target 480 x 320 px.
var scaleX = window.innerWidth / canvas.width; var scaleY = window.innerHeight / canvas.height; var scaleToFit = Math.min(scaleX, scaleY); var scaleToCover = Math.max(scaleX, scaleY); stage.style.transformOrigin = "0 0"; //scale from top left stage.style.transform = "scale(" + scaleToFit + ")";
Use the moz-opaque
attribute (Gecko only)
If your game uses canvas and doesn’t need to be transparent, set the moz-opaque
attribute on the canvas tag. This information can be used internally to optimize rendering.
<canvas id="mycanvas" moz-opaque></canvas>
More tips
- Batch canvas calls together (for example, draw a poly-line instead of multiple separate lines).
- Avoid unnecessary canvas state changes.
- Render screen differences only, not the whole new state.
- Avoid the
shadowBlur
property whenever possible. - Avoid text rendering whenever possible.
- Try different ways to clear the canvas (
clearRect()
vs.fillRect()
vs. resizing the canvas) - With animations, use
window.requestAnimationFrame()
instead ofwindow.setInterval()
. - Be careful with heavy physics libraries