PHOBOSLAB

Blog Home

Posts for 2012/06

X-Type – Making Of

For this year's Google IO, Google asked me to do a Chrome experiment for Mobile for them. They initially wanted me to vamp up Biolab Disaster – it's still a good game, but because of it's retro style it wouldn't be that impressive. Modern mobile browsers can do a lot more.

I suggested I would try to take another game of mine – X-Type – and make it work on mobile browsers. The game was made with my JavaScript Game Engine, so it mostly "just worked" on mobile browsers already. Yet, I still had a lot of work to do.

Have a look at X-Type over at chromeexperiments.com.

X-TYPE running on various mobile devices

Screen Size

One of the most difficult things for HTML5 games is dealing with the vast amount of different screen sizes and aspect ratios out there. I experimented a lot with different solutions and ended with a fairly simple one: the internal width the game's viewport is always 480px. These 480px get scaled to whatever is available on the device. The height of the viewport is variable, so that it fills the screen with the same scaling factor as the width.

// The internal width for our canvas is fixed at 480px.
// The internal height is set so that it fills the screen when scaled
canvas.width = 480;
canvas.height = window.innerHeight * (canvas.width / window.innerWidth);

// Scale the canvas via CSS to fill the screen 
canvas.style.width = window.innerWidth + 'px';
canvas.style.height = window.innerHeight + 'px';

In older browsers (Mobile and Desktop), scaling the <canvas> element was a horrible idea – it decreased performance to a tenth of what it would be unscaled. I'm happy to report that this is no longer true; Mobile Safari on iOS5 and the Chrome Beta on Android work just fine with scaled canvas. It still makes the game unplayable in Android's "Browser", though.

I also took care to only display the game in portrait mode and show a "Please Rotate the Device" message otherwise. Mobile Safari and Chrome both support the orientationchange event, which makes this easy. However, we can not rely on window.orientation, which reports the rotation in degrees (0, 90, 180 or 270), because some devices report 0° for portrait mode, while others report 0° for landscape. How convenient!

The solution is to just check if the window height is bigger than the width – if so, we're obviously in portrait mode! But as this would be too easy, Chrome's Browser offers another challenge for us: it only updates the window dimensions after it has fired the orientationchange event. So we listen for orientationchange and resize events. Sigh.

var wasPortrait = -1;
var checkOrientation = function() {
    var isPortrait = (window.innerHeight > window.innerWidth);
    if( isPortrait === wasPortrait ) { return; // Nothing to do here }
    wasPortrait = isPortrait;
    
    // Do your stuff...
};
window.addEventListener( 'orientationchange', checkOrientation, false );
window.addEventListener( 'resize', checkOrientation, false );

Performance

Since iOS 5 the <canvas> element is hardware accelerated and it really shows. You can draw hundreds of sprites on the screen without any slowdowns at all. The same is true for Chrome on Android – to a certain degree.

All drawing is scheduled via requestAnimationFrame and thus bound to the display's refresh rate. This works nicely on iOS, but Chrome refuses to draw at the 60hz even for the simplest scenes. You can use setInterval to process more frames, but only a portion of them is really presented on the screen.

So while Chrome's JavaScript engine is fast enough to process and render 60 FPS, it fails to display all of the rendered frames. I have no doubt that this bug(?) will get fixed.

While the game works in a multitude of other browser's on Android, such as Firefox, Dolphin or Opera, none of them provided good performance. I suspect the <canvas> element is not hardware acclerated in any of these, but didn't investigate further.

Controls

In the desktop version of the game you move the player with the arrow keys and aim and shoot with the mouse. Of course this doesn't work on touch screens, so I opted for dual virtual analog sticks. This worked out surprisingly well – with a bit of practice, you can control your spaceship quite precisely.

I also tried to make the analog sticks appear where you touch the screen first, so that you can always change the position. This made everything quite a bit confusing; it's easier to grasp the concept when the position of the analog sticks is fixed. Providing the dynamic positioning is probably more of "pro gamer" feature that should be optional, if at all.

Sound

This is the sad part. I complained about support for the <audio> element in mobile browsers last year already – and guess what: it's still the same shit. Apple hasn't done anything at all to improve the situation; same goes for Android's Browser. The Chrome Beta on Android seems to have some support for Audio, but it's not really usable for real time games at the moment. I'll investigate this further.

As always, I have high hopes though. Never give up, never surrender!

All in all, I'm very pleased with the results. Rendering performance in modern mobile browsers is really awesome and the quirks I encountered were workaroundable.

I learned a lot with this project and will use this new gained knowledge to make mobile browser support much more easy in the next version of Impact.

Measuring Input Lag in Browsers

For games, the time between a key-press and the appropriate action happening on the screen can often decide over virtual life or death.

It is no surprise that game developers go to great length to reduce input lag as much as possible. Guitar Hero for instance even has an option to compensate for lag. One of the biggest time hogs these days seem to be displays, but a poorly written application or game can introduce a lot of lag as well.

The typical path a key-press travels – Keyboard » USB Driver » OS » Application – is bad enough as each of these layer can introduce some lag. Yet, JavaScript games have to go through an additional layer they have no control over: the browser – which in itself may have several layers that introduce lag until a JavaScript callback for a keydown event can be called.

I decided to try and measure this lag for different browsers to see if there's room for improvement. The good news: most browsers do a good job delivering input to JavaScript events as fast as possible. The bad news: Chrome does not.

I build a simple website that draws to a canvas element at 60 frames per second, counts the number of frames and captures input events. I used my camera (a Samsung NX200) to record video at 120 frames per second, then slowed the video down even further to have a close look at the results.

The response time from the command prompt was used as a baseline (I measured the same lag with Quake 3 and other native games): 5 frames or about ~83ms. This is pretty awful to begin with. I'm not really sure where all this lag comes from, but I blame the display. Since I measured all (Windows) browsers with the same setup, results are still comparable.

Firefox 14 had the fastest response time of only 5 frames, followed by IE9, Opera 12 and Safari 5.1.5 (Mac) with 6 frames. The real surprise here is Chrome's response time of 8 frames or 133ms – 50ms more than Firefox. This doesn't sound like much, but if you directly compare a real JavaScript game in Chrome and Firefox, you can definitely feel the difference.

The input lag for Mobile Safari on iOS6 is around the 5 frame, ~83ms mark as well. I also tried to measure the Android's "Browser" and the Chrome Beta on my Galaxy Nexus, but couldn't get accurate results.

Chrome on Android refused to render at more than 20 frames per second and the "Browser", while proclaiming to render at 60 frames per second, only really presented every fourth frame. This is clearly visible in the slow motion video – 4 boxes appear at the same time, instead of one after the other. This is also why HTML5 games in the "Browser" (damn, I hate that "name") still seem to stutter, even though they are "rendered" at 60 frames per second.

Android has a lot of catching up to do.

I build another simple website that behaves like Guitar Hero's lag calibration. As humans, we try to compensate for the lag ourselves by pressing a bit earlier, so the results from this aren't as accurate as measured with a camera, but you can clearly see and feel the difference between Chrome and about any other desktop browser.

Try it here: phoboslab.org/inputlag/

Update June 26th 2012

As suggested by Filip Svendsen in the comments, I made another video to show mouse input lag in Firefox 14 and Chrome 22. To make the lag easier to spot, I wrote a Python script to move the mouse cursor at constant speeds.

From the video, it's quite obvious that the mouse lag in Chrome is much higher than in Firefox. However, the movement looks much smoother in Chrome. I guess you can't have everything - at least not yet.