My HTML Canvas game is having performance issues

Viewed 291

So, I have a natural selection simulation running on Javascript, and I'm using HTML Canvas for the graphics. The problem is that there're hundreds of objects (creatures), each one doing loops to search for food and predators, and for each of them there are dozens of genes (variables) that suffer mutation when they reproduce. Well, the graphics are very simple (they're just colorful cells shaped like circles or ellipses), but the whole nest of loops is killing the performance.

I'm using the animate() function, which calls itself through the requestAnimationFrame(animate). Well, inside this animate() function, besides doing the whole drawing and redrawing for every element on the screen every frame, I also loop the array of creatures to call the hunting() and update() methods to each of them. And there are 2 arrays of creatures (herbivores and carnivores), which contain hundreds of objects, and one of food, which can contain thousands. This means I'm looping through three arrays every frame, not to mention the loops that happen inside the creatures methods (for example, to look for food, a herbivore loops through all the foods in the screen, calculates the distance from all of them, and only then seeks it).

Bottom line: loops inside loops inside loops every frame. So my question is: are there any tips to improve the performance? Is there any tool that analyses the code and points which parts of it use the most memory? And are there (there probably are) more efficient ways of doing the animation?

Thank you in advance!

2 Answers

I would recommend using requestAnimationFrame() and inside the draw() part of your game loop, draw all separate objects to an offscreen canavs, and then do one get/put image to the visible canvas. While this may seem to be a performance hit, but when you have many objects being drawn, this overhead of get/put image is actually negligible.

The Canvas is just an image, and from my experience there is no difference between using a canvas or an image in terms of performance.

Offscreen or on the canvas is no different, they will both take advantage of the GPU wherever possible.

Using context.getImageData(), context.createImageData(), and context.putImageData() should be avoided for realtime rendering, It does not take advantage of the GPU and any processing you do to it will be done in main memory by Javascript. Though the data is stored in a typed array Uint8ClampedArray and can be converted to any type of typed array, such as a Uint32Array allowing you to handle a single pixel with one variable, rather than 4. There are also many native functions for typed arrays that provide much quicker array manipulation than the standard Javascript array.

The limiting factor for images (including canvas as image) is the amount of GPU RAM available, when you exceed the amount of RAM available the browser will start swapping images into GPU RAM as they are needed, when it does so this blocks the GPU's ability to render, and the transfer from Main RAM to the GPU RAM is slow in comparison to normal RAM access. When this happens you will instantly see a loss in frame rate. As there are a huge variety of platforms that the browsers can run on and no way to know the machines capabilities you should be careful when you publish realtime applications.

If you have written a game with high resolution images for a high end desktop machine, it will not perform very well on tablets and low end laptops. To mitigate this problem downsample the images to match the screen resolution. Using a hires background image on a device that is 1/8th the resolution is putting undue strain on the hardware. Devices are made to handle the resolution of their screens, going over this resolution will have a major unnecessary performance hit. This is where you can use an offscreen canvas to render the image at the native resolution of the device and then dump the original hires image. There will be no loss of quality, but a huge gain in performance, turning something that is unplayable into playable. This applies to all graphic resources. Never store and use images at a resolution higher then the device you are using can display.

Because there is such a variety of things you can do with the canvas the best way to find out what runs best is to experiment. Monitor the frame rate and try different approaches to the problem at hand. If the frame rate improves you have found a better way of doing thing, if the frame rate drops then you have used the wrong method.

are there any tips to improve the performance?

  1. Profile the running code to identify the real / main bottlenecks and address those.

  2. After you identify which specific part of your logic / algorithm takes the most time - you can consider techniques such as caching to optimize it. For example, does the distance from all food items need to be re-calculated in every frame? Maybe it's enough to recalculate every several seconds? Maybe keep an array of 10 relatively-close foods for each animal and ignore the thousands of other foods, and re-create this smaller array every few seconds?

Is there any tool that analyses the code and points which parts of it use the most memory?

  1. From your description it doesn't sound like the bottleneck would be memory. More likely CPU. But both can be profiled using Chrome's Dev Tools. There's plenty of material on that online. You can start here: https://www.html5rocks.com/en/mobile/profiling/

And are there (there probably are) more efficient ways of doing the animation?

  1. I can't help with that, but I suggest to first determine what the actual bottleneck is. To me it doesn't sound like it's the animation itself.
Related