This section covers some general performance tips. These are mainly aimed at avoiding situations which cause unnecessarily poor performance. There are also some best practices to help avoid getting stuck with a poorly performing game without knowing why.
Generally the main problem with performance is getting your game to run well on mobile devices like phones and tablets. It's harder to get good performance on these devices because they often have much weaker hardware: slower CPUs, slower graphics chips, and less memory.
Modern desktop computers are very powerful, and most people develop on desktop devices. If you are developing a game for mobile, you must test on mobile from the start. Since your computer may be several times faster than your mobile device, you may inadvertently design a game that has no hope of running well on a mobile device and not find out until later. To avoid surprises, test regularly on the intended device to make sure it is still running fast enough. The Remote Preview Paid plans only feature can make this quick and easy.
Testing regularly on your target devices also allows you to quickly spot any changes which have a big performance impact and revise it to be faster. If you don't realise your game is slow until a long way in to development, it can be very difficult to identify what is making it slow. Being able to identify which change caused poor performance is essential to help you design a well-performing game. You may need to design simpler games for mobile devices to match their lower speed and resources.
Many games run in to performance issues due to GPU fill rate. The GPU is the Graphics Processing Unit, the chip responsible for rendering graphics on the display. Since all computing resources are finite, this chip has a limited memory bandwidth, and it can be quite a low limit on low-end devices. Drawing pixels on the screen (also known as "filling in" pixels) requires writing them to memory. Drawing more images, and larger images, requires writing more pixels to memory. The data rate of writing all this pixel data to memory is called the fill rate. Once the fill rate exceeds the GPU memory bandwidth (the rate at which data can be written), the game will start to slow down as the GPU cannot keep up. Note this is a hardware limitation, not a limitation in Construct or any other software on your device.
To fully understand fill rate, it's important to also know that Construct renders games back-to-front. This means it starts by drawing the background (the objects lowest down in Z order) and progressively drawing everything else on top until it reaches the front. Since the objects at the top of Z order are drawn last, they appear on top. However when objects overlap, this involves writing to the same pixels repeatedly. This is called overdraw and it still uses uses up fill rate, i.e. the object underneath still consumes memory bandwidth, even though it is later covered up by something on top. Therefore the worst-case scenario for fill rate is a stack of large overlapping images.
There are a couple more points to consider about fill rate:
- Transparent areas of images still use up fill rate. In other words, transparent pixels are still rendered, they simply have no visual effect.
- Layers which use their own texture (indicated by Uses own texture in the Properties Bar) must be copied to the screen after the layer finishes rendering. This means every layer that uses its own texture is equivalent to rendering a screen-sized sprite that covers up everything. Having too many layers which use their own texture can quickly use up your available fill rate.
To avoid wasting fill rate, consider the following tips:
- Avoid objects with large areas of transparency. Crop all images you use to remove wasteful transparent space. (Shift+Crop in the animation editor will crop the entire animation.) Split up large objects with large transparent areas in to a series of smaller objects. For example, adding a window border using a screen-sized transparent sprite with borders drawn at the edges will perform poorly as it still has to fill a large transparent area in the middle. Splitting it in to four separate objects for each edge is much more efficient since a smaller area is rendered.
- Avoid large areas of overlap between objects. The overlapped area will have the pixels rendered to repeatedly, which wastes fill rate.
- Avoid too many layers which use their own texture. Enabling Force own texture, changing the opacity or blend mode, or adding an effect, all cause the layer to render to its own texture, which uses a lot of fill rate. While this is necessary in some cases to get the visual effect you want, avoid doing it too many times with layers in the same layout.
You can get a rough approximation of how busy the GPU is with the GPUUtilisation system expression. This is based on timer measurements so does not directly correlate to fill rate, but in general the higher the GPU usage, the more work the GPU is having to do.
Common causes of poor performance
Some of the most common things causing slowdowns other than fill rate are listed below. This list is not exhaustive. There may be other reasons your game is running slowly not listed here. These are just some suggestions to help you look out for common mistakes.
No hardware acceleration
While almost all modern devices support hardware-accelerated rendering, some older devices have unstable graphics drivers. Browser makers turn off the use of the GPU to avoid crashes on these devices (referred to as "GPU blacklisting"). This causes the game to revert to software rendering, which can be a lot slower.
You can check the hardware-acceleration status in Chrome by visiting chrome://gpu. You should see WebGL listed as Hardware accelerated in green. Construct supports both WebGL and WebGL 2; it is sufficient if either is hardware accelerated, since Construct will pick that one. If neither is hardware accelerated (indicating Disabled or Software only), you will likely get slow software-rendering. Try installing any available system updates, or updating your graphics drivers.
Note browser makers maintain their blacklists separately. Just because Chrome isn't using hardware acceleration doesn't mean other browsers will follow suit. Switching to an alternative like Firefox can provide better performance if it can use hardware acceleration.
Too many objects using Physics
The Physics behavior is very CPU intensive. Using too many objects with the Physics behavior can cause considerable slowdown. You should design your games to use a few large Physics objects rather than many small Physics objects.
Creating too many objects
While modern computers are very fast, they still have a limited processing capacity. Creating thousands of objects will probably cause your game to slow down. Try to design your game to use fewer objects. The system expression objectcount can tell you how many objects you are using.
Using too many effects
Effects are visually impressive, but can slow down the game's performance if over-used. Using an effect on objects with many instances is especially slow - often it is much more efficient to place all the objects with the effect on their own layer, then apply the effect to that layer instead. This allows the effect to process everything in one go, rather than having to inefficiently re-run the effect for a small object over and over again. Some effects also have more performance impact than others: blurs and glows tend to be the slowest, distortion and background blends having a medium impact, and the color-changing effects like Grayscale, Tint and Adjust HSL being the easiest for the GPU to process.
Unnecessary use of effects
Never use effects to process a static effect on an object. For example, do not use the Grayscale effect to make an object always appear grayscale. This will degrade performance when you could simply import a grayscale image to the object and not use any effects at all.
Using too many particles
The Particles object can easily create hundreds of particles. Each particle is like a sprite, so it can rapidly use up available processing power. Avoid using more than a couple of hundred particles if possible - generally the lower the particle rate you can set, the better. Use the ParticleCount expression to check how many particles you have created.
Using Sprites instead of Tiled Backgrounds
Creating too many objects can cause slowdowns, and a common mistake is to use grids of Sprite objects instead of Tiled Background objects. This is similar to using tilemaps inappropriately. For example, a 20x20 grid of sprites has 400 objects, which is a significant impact on the object count. A single tiled background can replace the grid of sprites and it only counts as a single object. Tiled backgrounds are specially optimised for repeating their texture so they are far more efficient for this purpose. Always use Tiled Backgrounds instead of repeating Sprites wherever possible.
Inappropriate usage of the Tilemap object
It is not appropriate to use the Tilemap object to display large images. The tilemap object draws tile-by-tile. If you paste a large image in to the Tilemap object, and then stamp a grid of tiles to display the large image, there is far more rendering work involved than simply using a Sprite. The Tilemap object should only be used for tile based games, where a single tile is a useful level design block by itself, rather than part of a larger image.
Changing large Text objects every tick
Change the size or text of a Text object every tick can produce poor performance. The problem is even worse if the text object is large. Text rendering is very fast as long as the object is not changing, but upon changing the object must do a relatively expensive redraw of the text and replacement of the cached texture. Try to use small Text objects that do not change regularly. If you must change text regularly, consider using a Sprite Font instead, which is fast even when it changes every tick.
Running too many events
If you have a very large project with hundreds or even thousands of events, those events will all need to be run, which has a significant processing overhead. Usually most of the events don't need to be checked all the time. You can significantly reduce the number of events that need to be run by organising them in to event groups, and disabling the groups that are not currently needed. (Events in disabled groups are skipped entirely.)
Using too many loops
This is rarer, but using too many loops like For, For Each and Repeat can cause the game to slow down. Nested loops are especially likely to cause this. To test if this is the problem, try temporarily disabling the looping events.
The following things are often accused of affecting performance but probably have little or no effect:
- Off-screen objects are not still rendered. Construct does not issue draw calls for objects that do not appear in the window, and the GPU is also smart enough to know not to render any content that appears outside the window - even when a single image is only partially on-screen.
- Image formats (e.g. JPEG or PNG) affect the download size but have no effect on runtime performance or memory use. All images are decompressed to 32-bit bitmap on startup.
- Audio formats also only affect the download size but have no effect on runtime performance.
- Number of layers usually has no effect. Layers which use their own texture have a performance overhead, as described in the Fill rate section above. However a layer with the default settings does not use its own texture, and has no performance overhead by itself. You can use as many of these layers as you like.
- Number of layouts also is unlikely to have any effect other than the download size. The layout size also does not have any direct effect; larger layouts do not use more memory or require more processing, unless you use more objects.
- Angle or opacity of objects and floating-point positions (e.g. positioning a sprite at X = 10.5) generally has no effect, since modern graphics chips are very good at handling this, even on mobiles. Very large sprites can still slow down weak devices; see the section on Fill rate above.
For more information and advice on performance, see the blog post Optimisation: don't waste your time. There are also some useful tips about keeping your events efficient in the blog post Common mis-used events and gotchas.
Measuring and testing performance
From the start of your project you should use a framerate indicator and keep an eye on performance, particularly when testing on weaker devices. This allows you to notice if a particular change you have made has seriously affected performance.
Test your project on as many devices and browsers as possible.
Measure, measure, measure
Don't make guesses about performance. Modern computer systems are highly complex, and it is surprisingly difficult to identify the real cause of performance problems. Even experienced engineers frequently guess wrong. Instead, use a more scientific approach: make performance measurements and prove the cause.
For example if you think something is causing a performance problem in your project, it is usually easy to test the theory: back up your project, remove the thing in question, and see if the framerate has improved. If it improves, the thing you removed was reducing performance; if not, it was irrelevant.
You can do even better using the debugger's profiler tab Paid plans only. This can show you a breakdown of where the CPU is spending most of its time. This helps you quickly identify the most important area, such as a group of events, to make performance improvements.
Displaying performance in-game
While the debugger shows some basic performance information about your game, it is a complex diagnostic tool which actually has a significant performance overhead by itself. Therefore unless you're using the profiler, it's best to check performance in normal preview mode, without the debugger.
The following three system expressions provide basic performance measurements:
- fps - returns the current frames per second rate. The maximum framerate depends on the display refresh rate, but is usually 60.
- GPUutilisation - returns the current estimated utilisation of the GPU (Graphics Processing Unit). This is a rough indication of how much time is spent rendering the game's graphics. This is also an approximation based on timer measurements. It ranges from 0 to 1, so the expression
round(gpuutilisation * 100) will return a percentage. Note this measurement may not be available on some devices (in which case it will say NaN, which stands for Not A Number).
You can create an in-game display of these values with a Text object to keep an eye on performance while testing your game, using an action to update it Every tick:
Set text to
fps & " FPS, " & round(cpuutilisation * 100) & "% CPU, " & round(gpuutilisation * 100) & "%
This will display a string like
60 FPS, 30% CPU, 40% GPU indicating the framerate and approximate CPU and GPU usage.
Interpreting performance numbers
Modern computing devices have two major processors: the CPU (central processing unit), responsible for running the logic of the game, and the GPU (graphics processing unit), responsible for rendering the graphics in the game. If the game is performing poorly, it can be due to exceeding the resources of either or both of these. The CPUUtilisation and GPUUtilisation measurements can help you identify which you need to focus on optimising if the framerate is poor. If the CPU utilisation is very high (e.g. 80%+), the performance problem is probably due to the CPU struggling to run all the game logic. You probably need to focus on optimising your events, using fewer objects, etc. Otherwise if the GPU utilisation is very high, the performance problem is probably due to the GPU struggling to render the game. You probably need to focus on reducing fill rate (see the previous section on fill rate), using fewer visual effects, etc. If both are very high, you probably need to optimise both!
Remember the framerate (FPS) is the ultimate performance measurement: as long as the framerate is good, then the overall performance is OK. The other measurements are there to help you diagnose what the problem might be when the framerate is dropping.