• Posted on: Mar 9, 2012
  • Tag:
  • Reactions: 11

> Hexagame, the making of an HTML5 game

hexagame

I was disappointed by canvas performance on mobile devices but CSS animations seemed good enough even for CPU intensive applications. I wanted to find out if I could build a video game out of pure HTML5 and CSS so I started working on Hexagame.

First things first. You can play (a beta buggy version of) Hexagame at http://hexaga.me/ (on chrome, safari, firefox, opera, iPad). The source code is on Github and it’s MIT licensed. No external libraries have been used, the whole code is custom crafted and resides in just 15kb (minified+gzipped). You’ll see a lot of spaghetti in the javascript, but it’s a side project and I proceeded a bit unorganized. Hope you’ll forgive the mess.

Initially the game was meant to be released on the iPad only as a native application wrapped around Cordova/Phonegap, later I was convinced to make it desktop and mobile friendly. After all it’s HTML5 what could possibly go wrong?

Myth: code once deploy everywhere

It might be true for very simple applications, or if you don’t care about optimization or user experience, but for anything serious you’ll spend a lot of time porting the very same code base to multiple platforms.

We have to deal with three very different realms: desktop, mobile (smartphones) and tablet. The main factors to take into consideration are: screen resolution, user interaction, performance.

Screen real estate is not a problem on desktop and the user interacts primarily with a mouse but also with trackpad. Extremely easy actions taken with the mouse might be a pain on the trackpad and vice-versa.

To give you an example, Hexagame has a mini-game where you have to drag and drop letters from one place to another. This simple task becomes extremely frustrating with a trackpad so I will have to change the interaction on that part. On the other hand on a touch enabled device the drag and drop sequence feels completely natural.

Performance on the desktop browser is not usually an issue… as long as you are on Chrome. At the time of writing CSS animations are velvet smooth on webkit only. If you are targeting the desktop browser, go for canvas, don’t be fool and spare yourself a lot of headache. Firefox and Opera give barely acceptable results, lurking the code you’ll see an isGoodBrowser variable, which basically takes care of reducing eye candies on less fortunate browsers. Internet Explorer (not completely supported yet) has decent speed but CSS support is lacking, so more work is needed just for him.

Tablets are a mixed world. Very close to desktop for screen resolution, they are actually closer to smartphones for user interaction. And then of course you have portrait and landscape orientation. You may decide to lock one of the two orientations but user is king and I believe you should really let her decide how she wants to play your game.

Performance-wise the iPad 2 is a pretty powerful beast. The animations are smooth as far as you move few elements at a time and you do not try to refresh big portions of the screen. On Android instead… well, you have to strip out each and every visual effect.

On smartphones the game needs to be redesigned. The game map on Hexagame is 9×8, to have the same grid size on mobile I should have reduced the tiles too much making them difficult to grab. So I’ll make the grid on mobile 7×6.

While it is not horribly complicated to parametrize the grid and the tile size, it has a series of nasty consequences to the leaderboards and partially to the game logic (having fewer letters to choose from makes the game harder). I have to slightly change the game rules for the mobile device and save multiple leaderboards based on the grid size.

Let’s recap. We need one desktop version, that is divided into multiple versions based on the browser capabilities. Then we have the tablet version, split between landscape and portrait orientation plus multiple versions based on the tablet capabilities. And lastly the mobile version with a different set of rules, in both landscape and portrait mode, with dedicated versions based on the OS (mainly android and iOS). Code once and deploy everywhere my a$#.

There’s of course a great advantage by developing in HTML5: you speak the same language on all the platforms, you don’t have to learn java for Android and Object-C for iOS. So our new motto should be “Code in one language and deploy everywhere”.

Start from the boring stuff

Hexagame -as a side project- took me a couple of months (and there would be another week to exit beta). Do you know how long it took to build the core game alone? 2 days.

In just two days the game was ready, the mini-game took me twice as long. The boring stuff is where you’ll be spending most of the time. The leaderboards, user login, viewport initialization, eye candies, graphics for all the platforms, finding the right dictionaries, optimization, testing, …

If I had to start over I’d totally build the outer box first and the game itself at the very end.

requestAnimationFrame vs native CSS animations

This was a tough decision. Go for 100% CSS native animations or take the requestAnimationFrame route.

Initially the game was meant to run on the iPad only and I tried to work with CSS. Despite the animations are particularly smooth on mobile Safari, there are some difficulties in working with pure CSS in videogames.

First of all easing is limited to ease-in/out. You can’t have bounce or elastic easing natively (even though it seems it will be possible soon). This means that you have to use pre-calculated animation frames, which is not that bad, but you end up with a transitions+animations mixed environment. Not to mention that animations are not widely supported yet.

But the truth is that CSS is not for gaming, as much as it is not for designing graphics. CSS Animations are useful to spice up your otherwise static website, but I didn’t find them solid enough for a video game.

On the other hand requestAnimationFrame has been designed exactly for graphically intensive applications, it may not be 60fps steady on some browsers, but at the end it seems rather consistent and -with setTimeout fallback- widely supported.

I therefore decided to leave the comfortable harbors of CSS and meet the raw power of requestAnimationFrame. The drawback? Lower framerate on iPad.

A better solution might be a mixed renderer which switches from one engine to the other based on the browser and the kind of animation (eg: a linear animation can be done with CSS transform), but that would be too much work for a side project :)

Give timeout

This is probably the most important lesson I’ve learned making this game. The browser renderer is slow, especially on mobile. If you make any changes to the DOM or to elements styles you have to give the browser a rest before performing other actions. I know many of you will flee in terror when you’ll read the following, but to avoid all sort of refresh glitches you have to execute your code inside a setTimeout.

A common trick to force the browser repaint is by calling the offsetHeight property of the incriminated element. Eg:

var el = document.getElementById('theId');
el.style.background = '#aaa';
el.offsetHeight;	// The element is now refreshed
...

This most of the times is enough but sometimes I found that you need to give the rendered a good rest before performing the next action. So in some occasions I had to do something like this:

utils.animate(el, {
	from: { … }
	to: { … }
	onCompletion: {
		setTimeout(function () {
			utils.animate({ … });
		}, 300);	// the culprit, gives 300ms rest
	}
}

This makes a lot of difference on mobile devices, and sometimes literally solves strange bugs you couldn’t get your head around.

I used localStorage, so sue me

Another problem to solve was where to store the database. I already talked about that in a recent post. I wanted to make the game playable offline so I couldn’t store the database on the server.

The only one solution I could thing of was localStorage. There are alternatives such as webSQL (deprecated) and indexedDB (not well supported yet), but the easiest to use and most widely supported solution seems to be localStorage. So actually I had no choice.

Many nasty things have been said recently about localStorage, most of them I agree with, but at the end it worked out quite well on Hexagame and surely localStore is not the performance bottleneck in this kind of application.

At startup I fill up two localStorage variables with approx 400kb of data. It’s a Trie-like compressed word list. Every time you compose a word a regex is fired upon one of the two localStorages to check if the word exists. I must say that I am rather impressed by the performance of this thing and I don’t see it as a show stopper.

Sign me in

User sign in is a necessity if you want to save high scores. Many HTML5 games just ask for a username and save the leaderboard with whatever name you give. That is not an option for me. I wanted people to be able to play it on desktop and later switch to iPad without loosing their high scores.

A solution is to build your own sign in system. You have then to handle user registration, password recovery, email validation, change password, and of course user log in. To avoid the hassle I’m using Google OAuth API to let the users in. In the future I might add other gateways such as Twitter and Facebook, but for the purpose of this beta Google seems fair enough.

Registering in a new web service is always boring so I decided to make sign in optional. You are asked to give your credentials at the end of the first game, but if you don’t want to send your high score you may just ignore the popup. If instead you want to pimp your ego in the leaderboards you just have to allow Hexagame in your Google profile. One click and you’re done.

Security and cheating

Both stealing user’s session and sending fake high score are relatively easy tasks for a motivated hacker. Hexagame is little more than a tech demo so it’s not a big issue, but security should be top priority on your application.

I don’t know how big names handle cheating, I thought about it and a solution might be by token validation.

Every time a user starts a new game you give her a token. You may also renew that token at the end of each level or every X points or every Y seconds. On game over you should check the token and save the score only if it is valid.

First of all you should check if the token (stored in a cookie or localstorage) is present in your database. Then you check if the data sent is consistent. For example if user reached level 3 but the token has been updated only once it means that the user is cheating (because we are updating the token at each level). Also we know that each level lasts at least 3 minutes so you can start to accept a level 3 highscore only after 9 minutes. The more checks you add the harder will be cheating, even though admittedly you won’t be never 100% safe especially on HTML5 games.

What next?

A lot of work is needed to see the end of the tunnel.

First of all code clean up and more optimization on iPad. Then I need to fix IE glitches and Android compatibility. Add a game tutorial and options page. Of course we need sounds! Lastly mobile compatibility and more sign in gateways (Twitter, Facebook, BrowserId, …).

I’ll never underestimate the work needed to develop even the simpler HTML5 game again!

/Share the joy

/Reactions

    • Author: RaphaelDDL
    • Posted on: 2012/03/09
    • At: 16:58

    Matteo,

    That was an amazing piece of work, to be done in so few time and making it alone (i believe). I already though of you as an Ninja-Master coder because of your work (iScroll, WP7 demo, etc) and now i think of you as semi-god, no kidding :D

    About the text, the ‘give timeout’ tip is so useful and soooo true. You have already said it on iScroll usage and it REALLY work. Some bugs that we can’t figure out sometimes fixes themselves with a single 100~300ms timeout before chaining the next event.

    Mobile browsers are evolving fast html5/css3 wise but for JS they seem like an old fart who can’t keep running for long times, needing a time to breath everytime. Maybe that can not be simply a browser factor but all the hardware and OS, but still a semi-fail in intense apps.

    And for the new motto, here’s an add:
    “Code in 3 languages {html/css/js} and deploy everywhere {after spending lots of time on browser quirks}”.

    Keep on the great projects, you rock.

    Best Regards,
    RaphaelDDL

  • Thaks for the write up Matteo, great to see real world lessons learned, tips and tricks.

    I reckon that sign in service as a library could be very popular ;-)

    Look forward ot the finished product, but this looks awesome already

    john

  • Fantastic write up for a great side project. I love the “give the browser a rest” advice.

    Those poor little mobile browsers just run out of steam :)

  • Great post! I understand every problem you talked about. We spent months of developement before being able to make our engine truly crossplatform. And with every new release of Android we still need to add hacks to deal with different weird thigs that might happend.

    Regarding CSS vs Canvas performance it’s still unclear for me what is the best. We decided to go with DOM, but since there are tools like Direct Canvas, and the desktop browser adding hardware acceleration for canvas operation probably soon canvas will be faster the DOM.

    • Author: Luis
    • Posted on: 2012/04/14
    • At: 18:00

    What about libraries like JQuery? Are they too slow for gaming purposes? It includes many easing functions, it makes it really easy to navigate DOM, it also handles user interface (drag&drop and so on) and it works across browsers… Would it make sense using them?

    • Since I’m targeting good-browsers only it makes no sense to me using jQuery. Also consider that once minified/compressed the whole game is just 17kb and I need no externat dependencies. I like it this way, not saying that you can’t reach good results with jQuery as well.

      • Thanks for pointing this out. I work with so many developers who treat JQuery as a must-have, and if you’re putting together a webkit-based mobile site or app, I’ve always thought it’s mostly dead weight.

        • Author: Tosh
        • Posted on: 2012/09/01
        • At: 12:07

        Just want to add a note to the jQuery discussion. Have a look at zepto.js. It’s jquery toned down, meant for mobile. You can get it down to 5-10 kb depending on what you need.

    • Author: rudy
    • Posted on: 2012/08/04
    • At: 09:53

    Hey Dude.
    You don’t know me. I stumbled upon your site tonight while looking up some info. Sorry, but I have to take issue, as I have seen this s0o0o0o0o many times. This is NOT pure HTML5. Not even close. The majority of your game is executed through JavaScript. I am curious as to why you passed up props for JavaScript? You wouldn’t have been able to create the game otherwise. (I’ll give you the local storage bit, but again, you need JavaScript to push it in and out.)

    Sure, HTML5 is a good step forward, but it isn’t doing the heavy lifting. People forget about JS. Canvas is a perfectly good example. HTML5 lays out the Canvas, but it it JavaScript who makes it come to life. In fact, CSS3 performs many more tricks than HTML5 (and quite spectacularly so).

    For your Hexagame, HTML5 provided you the canvas, but JavaScript and CSS were the ones who brought the paints and brought the party to life. :)

    And Dude… embrace your obvious JavaScript chops. That is much more impressive than HTML5. We’re talking night and day, in regards to skill level.

    Cheers.

      • Author: Joe
      • Posted on: 2012/08/13
      • At: 23:24

      The HTML5 spec now includes CSS and Javascript, so referring to HTML5 refers to all three.

    • Author: Upinesay
    • Posted on: 2012/08/26
    • At: 16:01

    Totally agree with your opinion on write once deploy everywhere being unrealistic. Although it can be done, it will be with a lot of work considering the different interpretations of CSS, for example, you will always need to write exceptions in your code because of all the different browsers. Thanks for sharing your experiences!