Six weeks ago we set out to see if we could build a real-time, “twitch” 3D game in the browser using HTML5. We built a few games and were pleasantly surprised with what we achieved.
You can test one of our creations here: Air Hockey
WebGL has pretty good browser support at the moment, and three.js is an essential library because of the WebGL boilerplate it handles for you. It has a clean API, built-in functionality, and community support, but documentation is sparse. However, there are so many examples that you’ll inevitably find one which shows how to build what you’re trying to do.
Many of the examples make use of stats.js and dat.GUI for monitoring the frame rate and adjusting settings, which are good tools to include when developing your game. dat.GUI was especially helpful in fine-tuning our latency compensation algorithm and tweaking the game’s appearance, such as light strength and other material properties.
We bought the air hockey, mallet and puck models from TurboSquid, and getting them into the browser was easy thanks to three.js’s Blender exporter plugin, which exports Blender scenes as JSON. We honed our Blender skills to separate the models, tweak the materials and reduce the number of polygons to make the resulting file sizes smaller. (One humorous bug was that the puck wasn’t touching one side of the air hockey table — after an hour of debugging the physics engine we found that the 3D puck model was actually offset a few units on the X and Y axes and wasn’t centered on the origin. Oops.)
If you’re new to 3D, WebGL and three.js can be daunting since there’s a lot of terminology and unfamiliar concepts. Since none of us are 3D artists it took a lot of time to get lighting and materials just right. We did a lot of experimentation by tweaking the graphics, reloading the game and playing against ourselves. A better approach, which we realized when we watched Mr.doob’s presentation on the RO.ME project, is to separate parts of the presentation into small, stand-alone demos which can be tweaked quickly and easiliy before being included in the final result.
We wanted to see if we could make a responsive, low-latency game that challenges players’ reflexes, and air hockey seemed like a good fit. Moving mallets and a puck around requires snappy networking performance, and we knew that the game would be unplayable if the networking was even a little bit slow. Fortunately, WebSockets provided great performance.
Our approach to networking was relatively unsophisticated but it was good enough to prove our point: “twitch” gaming in HTML5 is totally possible! Our networking code worked like this:
Of course, latency complicates this process. If it takes 50ms for a packet to get from the server to the client, then the puck position that the player sees will be 50ms out of date. This makes it harder for the player to hit the puck. Here’s how we solved this problem:
There are other modern, well-established techniques for lag compensation that could improve the playability of our game even more, but we didn’t use any of them for our demo. Using only ’90s-era game networking techniques was enough to build a fun, real-time, multiplayer browser game.
Also, it’s worth noting that the server is authoritative about the state of the game. All real-world multiplayer gaming has to be done this way to prevent cheating.
We thought that developing an HTML5 frontend with a Node.js backend would simplify development time because we could share JavaScript code between the client and server. This is especially important for real-time games where the same simulation needs to run on both ends. Compared to a previous project in which we had separate codebases for client and server logic, we think we cut development time down by somewhere around 30%.
We tried using RequireJS to write client-side code, but wrapping all of our code in AMD-style module definitions felt clumsy and alien, and we couldn’t get the whole shared-code thing to work. Later on we discovered Browserify, which was life-changing. Not only did Browserify work great, but it provides wrappers for many standard Node libraries so we could write events = require 'events'
and class Game extends events.EventEmitter
and have it work on the client-side. The only thing we couldn’t get to work with Browserify was Backbone.js, so we wrote 50 lines of CoffeeScript to replace the parts we needed from it.
We chose CoffeeScript over JavaScript because it allowed us to write the same logic in fewer characters, and all of the administrative Node tools, like nodemon and forever, seem to support it. We were worried that CoffeeScript would be hard to debug, but it turned out that the compiled JavaScript was dead easy to navigate. Browserify also makes use of the new source map standard, which helps a lot with navigation in the Chrome Developer Tools, and we think it won’t be long before someone will make it easy to debug CoffeeScript in the browser directly.
As already mentioned, we run our game server on Node.js. We felt that there was no other sensible option given that Node.js gives us the ability to run the same game code on the client and the server. So far we’ve been pretty pleased with Node’s performance, and in our load tests we found that we can run about 100 concurrent games on a single 512MB Rackspace VM before performance starts to degrade. We spent almost no time optimizing our code so this performance was a rather pleasant surprise.
Any discussion of HTML5 performance will inevitably include garbage collection, and we were initially concerned that GC pauses might cause noticeable pauses in game play. With Chrome this turned out not to be a problem. When playing Air Hockey on Firefox, unfortunately, there are noticeable blips in smoothness which we think is related to garbage collection. However, we made no effort at all to reduce the amount of garbage we generate, and we think it’s probably possible to get great performance on Firefox by being more careful about how much garbage your code generates.
The biggest thing holding back browser game development is the lack of tools. We see a lot of frameworks, but they either constrain you into making your game a certain way, or they get acquired and are never released. There are some great libraries and components, but it’s still up to you to string them together.
Flash and Unity have well-established development environments, and iOS and Xbox Live Arcade are platforms which provide end-to-end solutions for building and publishing a game. But the browser is still very, very new, and building an HTML5 game is still analogous to assembly language. In our game there’s no slider we can use to tweak the color of the puck or adjust the brightness of a light — if we wanted any of that we’d have to build it or buy in (literally) to a heavier framework.
We are excited about the possibilities in terms of bringing near-console-like experiences into the browser with synchronous, multiplayer gaming using HTML5. As fans of StarCraft and Halo ourselves, we’re ready to see those experiences come into the browser, and we hope we can inspire you to want them as well.
If you would like to get early access to our games and game development tools, please sign up at http://artillerygames.com.
Share: Y
blog comments powered by Disqus