computer keyboard

HTML5 Game Development: Using sprite sheets for better performance (and protecting your server)

When I started developing the HTML5 version of Command and Conquer, I never expected more than a few close friends to look at the game. (Based on the experience with my last game, Breakout).

Command and Conquer Javascript ScreenshotSo while I spent a lot of time making the game look as close to the original Command and Conquer as possible, I never spent much time optimizing the code. Most of my development and testing was on my own Macbook, and I didn’t pay attention to the image loading time or network traffic.

Unfortunately, this game uses a lot of images.

Each unit can face 32 different directions. This means at least 32 different images for each unit (more if there are animations like ‘harvesting’).

Buildings need a whole set of images for each state – under construction, regular – with different sets of images for healthy and damaged buildings, and for any additional states (like ‘unloading a harvester’). The Construction Yard for example needs 82 different images for it’s animations.

As a result, when the game loaded, it made nearly 1,000 HTTP requests to load all the assets (including images and sounds).

Since most browsers only make a few simultaneous requests at time, downloading all these images took a lot of time, with an overload of HTTP requests.

While this wasn’t a problem when I was testing the code locally, it was a bit of a pain when the code went onto the server. My patient friends ended up waiting for the game to load for 5-10 minutes (sometimes longer) before they could actually start playing.

AdityaRaviShankar.com - Traffic Screen ShotThe problem came when my game hit the front page of Hacker News and Wired.com on the same night, resulting in a slight spike in traffic :). It then got worse when the game got 1,000+ tweets and 1,000+ FB likes in just a few hours. The last time I checked, searching for my game gave over 5,000 google results.

What this meant was my shared hosting server was getting close to 14,000,000 HTTP requests in one day from just my domain.

I don’t think too many shared hosts are designed to take this kind of load, which resulted in my account being disabled with this interesting email from my provider.

Hello,

Your account adityaravishankar.com on the server quebec.unisonplatform.com was recently found to be causing high load that resulted in slowness/outages of various system services. In order to ensure quality of service to the other clients on this server we regret to inform you that the account had to be disabled to prevent any further interruption of service to our other clients.

If nothing can be done to lower the resource usage you may need to look into purchasing a dedicated server or cloud server which you can find more information about at http://www.eleven2.com. Please contact us as soon as possible to resolve this issue.

I was able to find temporary hosting on another server but the biggest priority was to optimize the way I stored images, which of course brings us to sprite sheets.

Hand of Nod Sprite SheetSprite sheets store all the sprites for an object in a single large image file.

When displaying the images, we calculate the offset of the sprite we want to show and use the ability of the drawImage() method to draw only a part of an image.

//Before: (Load individual images and store in a big array)
// Three arguments: the element, destination (x,y) coordinates.
var image = imageArray[imageNumber];
context.drawImage(image,x,y);

// After: (Load single sprite sheet image)
// Nine arguments: the element, source (x,y) coordinates, 
// source width and height (for cropping), 
// destination (x,y) coordinates, and 
// destination width and height (resize).

context.drawImage(this.spriteImage, 
this.imageWidth*(imageNumber), 0, this.imageWidth, this.imageHeight, 
x, y, this.imageWidth, this.imageHeight);

Creating these sprite sheets is incredibly simple using ImageMagick’s montage command line tool. This single command will convert a folder full of images into a single row sprite sheet.

montage -background transparent -tile x1 -geometry +0+0 construction-yard/*.gif construction-yard-sprite-sheet.png

After comparing PNG and GIF, I found that PNGs tend to compress the sprite sheets a lot more.

The advantages of using Sprite Sheets?

  1. Fewer HTTP requests – The Command Center went from 81 requests to a SINGLE HTTP request
  2. Better Compression – An advantage of storing the images in a single file is that the header information doesn’t repeat and the combined file’s size is much smaller than the sum of the individual files. The command center went from 496KB in 81 files to only 37KB in a single file. (Less than 8% of the original size, which is incredible)
  3. Easier Manipulation – With all the sprites in a single image file, it became easier to do RGB color manipulations, and I was able to optimize the drawing code for performance.

From almost a 1,000 requests to 120 requests in one simple code rewrite. And the total download size went from a few MBs to around 200KB.

Game load time went from 10+ minutes to under a minute. The bandwidth usage dropped to a tenth of the original. The number of requests dropped to a tenth of the original. And now my shared hosting can survive a very decent amount of traffic.

Moral of the story? When developing a large game that is image heavy, track your network usage and when possible use sprite sheets :)