I'm in some sort of a crisis of faith. This is a about project that I've put down two months ago, despite having plenty of energy and drive to put in new features. Having spent two full days performance tuning with Google's new web tracing framework, I'm still not very pleased with the performance I am getting out of my <canvas>.

Please help me get back my simple, readable code and increase the performance of my code.

I've uploaded one of the latest versions here: http://manadar.com/test/old/ This is the slightly older one, with still readable code and quite poor performance.

This is a newer version with all performance tuning in place: http://manadar.com/test/ (for the normal JS, just remove ".min")

I'm not sure if I'm allowed to ask questions on this subreddit, but these sort of questions on stackoverflow are often closed because they are "vague or overly broad". I'm mainly looking for some extra knowledge/help in performance-tuning the canvas related code.

Note that the Javascript is all pretty fast. I know there's plenty optimizations I can do, but I measured runtimes with google's web tracing framework and attacked the most costly parts (runtime * number of executions) - these were generally all canvas related.

Anyway, I'd love if some of you could take a moment out of your day and help a fellow coder out.

First, you should never use setInterval for canvas. Always use requestAnimationFrame.

Second, you're doing a lot (a LOT) of drawImage commands in your main loop. If you do nothing, the canvas won't change and the computer doesn't have to do any work. Only draw the tiles that change (perhaps create a queue of changed tiles).

Third, you draw the random background on every frame. Instead, have a second canvas behind your main canvas that contains the random background. Draw the random tiles a single time with a black background and position that canvas under your "main" canvas. Then, any time you clearRect a tile, it'll show the random tile that would have otherwise been there.

Have one canvas per z-level. Then you don't need to re-draw when you change levels.

This code is really bad because it creates one object per tile per layer per frame. Each of those objects gets put on the heap and needs to be garbage collected at some point. Instead of doing this, create a single one of these a single time when your code initializes and recycle it. That way, the garbage collector won't be a hinderance.

You draw all the chrome on every frame. It would be better to either make this all out of plain old DOM nodes (styled with CSS) or to paint it a single time on its own canvas. Plain old HTML is in no way performance-limiting.

Really, it comes down to "How much can I avoid doing on every frame?". If you can draw it once and not change it, do that. If you need layers, create multiple canvases. If you need to draw something complex many times, cache it in a canvas that's not in the DOM (you can drawImage with a canvas object instead of an image!).

This is exactly what I had been hoping for! Thanks a lot for taking the time out of your day and give solid general pointers.

It's given me back the confidence that this project will work. I'm starting with the setInterval problem, then onto creating layers (background + z-levels + chrome) then the map_temp variable and by then I am sure my code will have a whole new set of challenges.

It's hard to comment without more information about what is slow. When asking for help, prepare a test case or something, it's unrealistic to expect others to just drop everything and start deconstructing your entire tool. It seems plenty fast enough to me, barely any CPU usage.

I do notice you're using setInterval instead of requestAnimationFrame to do your drawing. On mobile, this could be an issue. It's also recommended not to do any drawing on mouse move but just store the data so it can be used when the next frame is drawn.

Thanks for having a look! The older version actually runs abysmally slow on Chrome on Android devices. When drawing large areas, my CPU (blazing i7) would go up to nearly 10% on Chrome on PC. Isn't that a lot for something as simple as this?

Again, thanks very much. I'm very stuck on this problem and any new perspective is much appreciated.

He's using raw <canvas> as it is, adding an abstraction layer won't make it faster. All a rewrite could offer is to force his code to follow any best practices implemented by the framework, which in the case of this project isn't going to make much (if any) difference.

All canvases render using the GPU in modern browsers, even if you're rendering in a 2D context. Also, CocoonJS's OpenGL ES bindings only apply if you're building a native app (OpenGL ES doesn't exist for desktops). In a browser context (what OP's site is built for), you would get comparable performance at best and worse performance in most cases. Applying it naively would result in a net loss in performance.