Skip navigation

Rendering

Disclaimer: this is a technical article but with some pictures.


HTML logo

Why bother with Canvas?

I tried with Crajsh to make a game that would run on current PC browsers with outdated hardware. Making the game reasonably fast was a design goal from day 1 and I learned a lot about optimizing Javascript and HTMLCanvasElement during the process.

While I had been struggling to make my native OpenGL games work in most of today’s Windows PCs, Crajsh was reported to run correctly on an EeePC and 10 year old laptops.

OpenGL is much work

I went to HTML5 mostly because I got tired with OpenGL drivers out there. The ATI R500 was prematurely abandoned, and some integrated chipsets are still spread in the PC gaming world like a cancer. The stability of OpenGL drivers is not a given and it’s not always safe to direct your users towards the newest drivers with some vendors.

OpenGL development on the PC is frustrating. There is all these fancy features marked supported but you can’t use them because it does not work with card X + driver Y. Problem is: there is a lot of X and a lot of Y to test for. And eventually you can’t plug X and can’t find Y.

What happens next: you end up with multiple rendering paths, ugly work-arounds and uncertainty. OpenGL gives you much work in the real world and I wouldn’t advise anyone to use WebGL until browsers wrap OpenGL ES into software/DirectX where needed.

All of this prepared me to go back to software rendering. Now I think the HTML5 Canvas is suitable for writing cross-platform 2D games, provided you agree to pay the price of optimization.

Know about your users

If your game runs in a browser you can easily get the number of players, for how long do they play, etc…

A good thing is that they will always play the latest version. They are also more likely to update their browsers than display drivers, which is a nice bonus.

Using the CSS engine for UI layout

Implementing a GUI, or adapting an existing one for a game can get pretty complicated. The CSS engine and libraries like jQuery UI makes it straightforward. I suppose localization is easier too since the browser has been thought for that, while with native games it’s notoriously tricky.

When NOT to use the Canvas

  • You want deffered rendering, SSAO, shadow maps, etc… anything fancy
  • You want both high framerates and accurate controls.
  • You want full control over sound, not just playback samples and choose volumes.
  • You don’t want to spend time optimizing.
  • You can’t get yourself to use Javascript.

How to make Canvas fast?

Draw calls with Canvas are known to be slow and there seems to be several ways to work-around this sluginess.

Limit the size of the Canvas

This is what we see in a lot of current HTML5 games. Full updates become possible with a small-enough area. The obvious flaw is that a small game might be less compelling and immersive.

Partial updates

Another solution is to use a larger, screen-sized Canvas and make partial updates. This is suitable to board games, tower-defense, tetris, etc… If you want scrolling you’ll have to complicate the rendering.

Layer several Canvas

This is what some games like Canvas Rider and Biolab Disaster iOS do. The level is prerendered in a big Canvas and the player is drawn in another Canvas with a different z-index. Scrolling is achieved by offsetting the background canvas.

This method use the browser compositor which is likely to be very efficient. It’s probably the most suitable approach for most 2D games: tiled RPG-style games, platformers, scrolling shoot’em ups…

However it wouldn’t work with a few games like Crajsh which have a large tiled-world (up to 1024×1024 tiles) and lots of level updates. In this case maintaining a large Canvas is likely to be slower than maintaining an array of tile indices and a smaller Canvas. Also the memory consumption would be too high.

canvas rider

Canvas Rider

Vector graphics

One technically impressive game recently came to my attention: TankWorld.

Its creator implemented 3D-like rendering in Canvas to avoid WebGL shortcomings. Browsers seem to be efficient with vector graphics, probably thanks to SVG.

I think this is the best current method for 3D games until WebGL is ready. An obstacle to overcome is that browsers have varying abilities at drawing triangles, eg. TankWorld works faster in Chrome.

tankworld

TankWorld


The Crajsh way

My first naive version would draw each tile with a call to drawImage. This was obviously slow so I made the renderer track currently displayed tiles in the Canvas and update tiles as they change. As most of the world is empty blue tiles, I thought it would provide quite a speed-up.

How to render a large, rapidly changing tiled world?


This was indeed faster, but would slow down dangerously in crowded area with lots of non-empty, different tiles.

So I added an optional step which takes the previous Canvas content and offset it to follow the camera movement. This brought more speed and stability. The method works like this:

  1. Copy the main Canvas to an offscreen Canvas
  2. Blit the latter to the former with a drawImage call and the right offset
  3. With this move, 95% of the tiles are drawn in the right position
  4. Update the tiles which actually changed since the last frame (world updates)
  5. Force the update of some tiles to account for floating UI elements, and player animation in the main Canvas

Reusing the last frame leaves few draw calls to be made.


There is more tricks going on. Profiling with Firebug reports that the game bottleneck is the rendering function with less than 1 ms on average, and 10 ms at worst. As the callback is called every 50 ms, a frame skip is pretty rare.

Overall the game runs at a stable 20 FPS, which is not exactly smooth but works with a plain background. In my opinion a low, stable framerate is better than 60 FPS with random pauses in-between. I had a painful trade-off to make between framerate, lagging controls and gameplay. It made the game a bit more difficult that I wanted.


The next article will be about the specifics of Javascript optimization for games.

Optimizing Crajsh – Part 2 – Javascript

Advertisements

2 Comments

  1. Cool, interesting post #ponce!

    I’m not a huge fan of javascript… I would prefer a lot to do this kind of 3D things in C#/silverlight for example! (but ok, running only natively on win and macosx)

    But hey, funny about the canvas trick to blit the screen and display only the deltas… that was actually what was used on any demo and game on oldschool machine! (I remember to do this for text scrolling in amiga demo as well as for shoot-them-up prototype). True that with current OpenGL/DirectX power, most of the computer graphics developer are missing this kind of “ancient” optimization! 🙂

    • Hi @lx, I don’t like Javascript that much either 🙂 the language is half-beautiful, half-ugly. But all browsers are a bit the same to me and overall it’s a joy to make a crossplatform game that easily, without multiple builds.

      You are right about the optimizations, it feels a bit “oldschool” to have to optimize everything because the underlying platform is slow if we don’t care.


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: