Signing you in...Google Developers needs your permission to do that.
Try again Cancel
Okay. Let’s start by saying that I’m a developer that does mostly web stuff.
This means that a majority of the code I write will end up displaying something in a browser. Of course I do interact with APIs, write background jobs, add some security, optimize databases and so on… the point here is that, professionnally, I do not write entire projects that have nothing to do with a web app.
My friend André on the other hand has a different background as he mainly develops games. One evening we started talking about how complex it would be to create an online multiplayer game from scratch. Being the web guy, I felt that I should know how all this was supposed to work… after all, the multiplayer appens over the internet, right?
I Can Code That With My Eyes Closed…?
On paper it seemed pretty simple.
People connect to a server. This server keeps the game board and broadcasts it to everyone when needed. Each player keeps some kind of state on its side for caching. Everything related to drawing the game board as well as UI should be handled by the client.
Of course I knew it couldn’t be that simple and I was 100% sure I was disregarding most of the problems that can occur when creating an online game.
I already blogged about how side projects are the best response to such situation, therefore at that point I pretty much had to create some kind of game to try it out!
Stack
Time being a factor, I didn’t want to go all out game development with C++, Unity or something like that. Instead I kept it in my “safe” zone by using technologies I had a bit of experience with:
Building The Game
I wanted a simple game so that I could focus on the online aspect of it. The rules I came up with are:
- There are n players represented as squares on a board.
- 1 player is the target and earn 1 point per 30 movements.
- If another player hits the target, she becomes the target and win 1 point.
I also added a simplistic chat system so that players could talk to one another during the game.
The first version of the multiplayer code was pretty simple:
- When the player connects she gets the latest version of the game board.
- Each time the player moves it sends an event to the server that updates the world and broadcasts it to everyone connected.
- Client side javascript renders the board in the canvas at each change.
This is when I started to see why it wouldn’t be as simple.
Oh Wait. Problems.
Speed
My first version was hosted on Heroku and lagged like crazy. This is because right now Heroku doesn’t support Websockets and forces socket.io to fall back to its polling mode. After some research I moved to DotCloud and got an impressive boost in speed.
I also had to rewrite a lot of code using Express because it would make deployment way easier.
Now that hosting and basic interactions were a bit more optimized, I started to see another problem: passing around the whole board was still too costly network-wise.
At that moment of development the board was just a JSON object containing all the players, their names, positions, scores and other meta data regarding the game. It seemed pretty small but it wasn’t, and as the game would grow the problem would only get worse.
The solution here would be to only broadcast what changed instead of redrawing everything. This was annoying because it meant that you need to be able to draw the board from scratch (when you connect) as well as incrementally (while you play). Twice the logic.
I also noticed a lag for the player’s own actions. This was because it would always go via the server before updating the board. As a matter of fact there wasn’t even a board on the client side, just its rendering on the HTML5 Canvas.
Soooo to fix this the client code should also be able to know the state of the world and update it locally.
Fun, Gameplay & Complexity
A game needs to be fun, or else there is no point. And a couple of laggy squares moving around is clearly not fun. This seems trivial, but when you spend your time working on web apps, you don’t spend a lot of time thinking about whether the app is fun or not. Making it efficient, fast and user friendly is complicated enought!
I added some rules and gameplay elements to make it feel more like something one would like to play, including:
- Feedbacks when you get hit
- Scoreboards during game
- Invicibility frames when you get hit
- Board boundaries
- Changing a player’s name
- Tweaks in the player’s speed & size
It’s also good to know that some of the gameplay elements are based on constants. For instance you always move of X, and your player is Y pixel-wide. When you get hit you are invicible for Z frames and so on. To tweak it I had to add a debug mode to change everything while playing to quickly see how it felt.
All these changes made my simple game complex and it became harder to maintain the code, even only after something like 6 hours of work in. I had to throw away a lot of code and start again fresh.
Concurent Access
When you have a lot of players connected and modifying the same board, you’re bound to run into some concurent access issues. For instance let’s say two players touch the target at the same time. Who gets +1 point?
To fix this I hacked together a basic mutex system to handle the simple problems that kept on happening. This did the trick after an hour of code, but it wasn’t something I’d put in production!
Conclusion
Overall I’m pretty happy with the results. Even with my lack of experience in the subject I still got something running and gained a better grasp of why game developers tremble when you mention online multiplayer.
Now I think the next step would be to design a game taking the constraints discovered into account in order to simplify development. You don’t want a game where precision and timing is key. You could instead create a massively online turn-by-turn game and save yourself major headaches while still producing something fun! It all depends if you seek a technical challenge or if you just want to create a cool project.
Also I’ll need to find a graphic designer because my game looked like shit, but that’s another story.
Please enable JavaScript to view the comments powered by Disqus. comments powered by
README.md Websockets Experiments This is just random code trying out the capabilities of websockets, so do not take it too seriously! Run server with supervisor -w app/app.js app/app.js
Where we're at with HTML5 games so far
HTML5 games have come a long way in the past year or so. We've gone from being extremely cautious with the technology to going full-on crazy with it. We've gone from making very basic retro-esque 2D games to creating rich 3D first-person experiences. We're basically experiencing the beginnings of the Web as a viable platform for games without plugins.
Loads of games
2 years ago I could have counted the amount of decent HTML5 games on one hand, perhaps even on one finger. Today I have the opposite problem – I just don't have enough fingers to count them on! There are now many games out there that feel great, look great, and perform great. Here are some of my favourites…
There are plenty more out there but they give a good overview of the quality of HTML5 games that we're starting to see. And the best bit? We're not even seeing the full potential of graphics and possible functionality in these games.
If you're interested in discovering more HTML5 games then you can check out places like Scirra, GameSalad, and HTML5games.com
Better tooling and frameworks
One of the reasons that we're seeing so many new, decent HTML5 games is that the tooling for creating them is also getting increasingly better. As well as that, we're now seeing developers getting used to these tools and learning how to push them to their limits.
There are a huge number of game engines and frameworks out there today. Here are some of the best…
We've been trying to validate the platform
The sad thing about the state of HTML5 games, at least to me, is that we're currently stuck in the mindset that we need to prove that the Web is 'good enough' to make games like you see on iOS, desktop, and console. We're constantly porting games to HTML5 then comparing them to those on existing platforms and acting surprised when they look different, feel different, perform different.
Guess what? A game built for a specific device or platform is definitely going exhibit some quirky behaviour when ported somewhere else – it doesn't mean that the port platform isn't able to handle games. I can see why we do it though, it's hard to create games from scratch and especially so when you're also trying to understand how to use a new platform. It's simpler and easier to bring over games from somewhere else that we know already works and is successful.
Another thing is that we're used to building and playing games on the Web with Flash, but we often forget that HTML5 is a completely different beast (arguably a much more interesting beast). For example, a game in HTML5 doesn't need to sit within a traditional rectangular box. Sounds silly? Perhaps, but there is inherently much freedom to be had with games created with Web technologies at their core.
Call to action for games created for the Web
The way I see it is that we'll be forever limiting and holding back the full potential of true Web-based games if we constantly aim to replicate experiences elsewhere. The Web should be seen as a unique platform in its own right, not as yet another channel doing the same thing. In short, we need to be building games for the Web, games that understand and use the Web to its full potential.
So what would games created for the Web look like? What kind of games could we create if we took what we know about building websites and Web applications and applied those skills to games?
Using URLs to link to in-game content
At the simplest level, we have the humble URL. Often taken for granted by Web developers, this most basic of features is what allows us to reference any piece of content on the Internet so it can be easily accessed by others. There's no alternative feature like this that works across multiple game platforms in the same way, let alone one that is as standardised and simple to use as the URL.
We're used to using URLs to link to entire pages and applications. Hell, we even use those fancy # URL fragments to link to specific pieces of content within a page. Yet we don't seem to be applying this same logic to games on the Web, at least not in the ways it could be done.
For example, imagine that you're fighting the boss in BrowserQuest and you decide that you're going to need your friends to help you out. Instead of just linking to the main game, and requiring your friends to travel all the way to the boss, how about if you could send them a link (via Twitter, perhaps) that takes them directly to your location within the game world.
It's such a simple feature but it's likely the most important one that we need to utilise more for games. All you'd need to do is convert the current player's coordinates / area position into a unique link that they can then send to whoever they want. Why convert the coordinates into a unique link? You wouldn't want to transmit raw coordinates as that could result in players abusing the system an teleporting all over the place. Not fun.
Going beyond position URLs, what about simply using URLs to link to levels and progress throughout a game? We tend to treat things like games as one-page experiences via a rectangular embed or canvas elements. That doesn't mean that you can't use URLs to dynamically change the content of that game. You could use the History API, for example.
User-generated content
Another 'feature' that's very much part of the Web is the idea of user-generated content. Now, you can sort of get this in other games but it's not woven into the fabric of the platform. The Web, on the other hand, is constructed from user-generated content – that's what it's all about.
How does this apply to games? Well, allowing players to create new content and customise their gaming experiences with ease. HTML and JavaScript are perfect for this and the concept of 'hackable games' is one that's just beginning to take off.
One great recently example is FightCode, where you code your own bot with JavaScript and share it to do battle with other bots from the community. It's fantastic fun and really highlights how games on the Web can be unique and user-generated.
Breaking free from embedded rectangles
It's fair to say that the vast majority of HTML5 games are displayed within a rectangular element (usually canvas) contained within the browser window. This is what we're used to from embedding Flash games, and the default behaviour of canvas sort of forces you into this approach.
There's nothing inherently wrong with this, particularly for simple games, but it's limiting and the experience isn't as immersive as it could be.
One alternative approach to take would be to expand your game to fill the entire browser window, or even use the Fullscreen API. This still constrains things within a rectangle but it's a much more immersive experience.
The second, more Web-like alternative is to forget about the rectangle and build games that can interact with other content on the page. For example, you could use DOM elements instead of the canvas and then have the ability to move them around, to interact with other DOM elements on the page.
I have seen the second approach only a few times, usually with a mini game that runs along the header or footer of a webpage, or perhaps in the background. The game is part of the site, rather than embedded within it. One cool example had a rocket ship you could fly around the page and blow up other DOM elements with.
Providing APIs to access in-game content
Something that we now expect from Web services is a decent API that allows us to access all the juicy data. Interestingly, this is rarely, if ever seen with games.
I would love to see games created that have rich APIs that allow for all sorts of data to be retrieved and analysed. This is one of the situations where we can take our knowledge of building services for the Web and apply that directly to games.
One great example of how this data can be useful is with DayZ. This Windows FPS doesn't itself provide the data to players, but if you run a server that hosts DayZ games then you can access the APIs and resulting data that is subsequently stored within a MySQL database. What this has resulted in is a blossoming set of tools and data visualisations that help the server admins keep an eye on the players, and for the players to keep tabs on their and their friends progression.
The beauty of this API-led approach is not that it becomes part of the game, more so that it opens up the possibility for countless modifications and tools to be created by the community. It is unlikely that this approach will succeed, let alone happen on any other platform than the Web.
Second-screen experiences
Coupled with the idea of APIs that provide in-game content is the idea of second-screen experiences. In a nutshell, this is about having a game playing on one screen (like your desktop) and a supplementary experience happening on another (like your mobile phone).
Going back to DayZ, many players inherently have second-screen experiences while playing this without even realising it. What happens is that they open up and use third-party mapping services on their phone that allow them to get more information about where to find the best gear in the game. This secondary experience within DayZ is not directly connected to the game (you could play without the map) but it directly enhances your game experience if you do use it.
If we create games that open up the data within them, then what other secondary experiences could we give players? Perhaps data visualisations of their journey, or a real-time map of their position using WebSockets, or a way to de-clutter the main screen by offloading UI elements.
This is still a relatively new area and one that we will start to see progression in with things like the Wii U.
Using mobile devices as controls for games on a separate screen
Related to second-screen experiences is the idea of using a separate device to control a game. Not a mouse, keyboard, or gamepad, but rather a device that we wouldn't normally associate with input. In this case, the idea is that mobile phones can be used as a powerful and customisable gamepad for any game that is playing on another device.
The concept is being explored in a few areas, with a fully-functional concept now available from Brass Monkey. It still requires some native code and plugins, but with the forthcoming WebRTC functionality in browsers I believe that we'll be able to replicate the experience with pure Web technologies.
Asymmetric gameplay and persistent data across devices
I've left my most demanding request till last, mostly because it's the one I want to see happen the most.
The concept of asymmetric gameplay is one that is starting to gain a lot of interest, though it's rarely been seen in the past. The idea is that the game experience on each device is entirely dependant on the features, performance, and capabilities of that device. In effect, the same game played on a desktop computer would look and feel different when played on a mobile device. A more fitting Web-related name is responsive game design.
Imagine for a moment that you're playing the latest WebGL version of Battlefield 3 (BF3, a hugely popular FPS) on your desktop computer. It's a great experience; it has rich HD graphics and it's using WebSockets for real-time multiplayer with your friends.
Should it even be playable on other platforms, like mobile? And if so, should it look and feel the same as it does on the desktop? My opinion is that it should work on other platforms, where necessary, but not look and feel the same – it should be asymmetric.
This is purely down to hardware performance, input methods, and general experience. It would be crazy to think that a bleeding-edge desktop HTML5 game would perform at the same level on mobile hardware – we don't expect this from other platforms, so why HTML5?
And even if it could run at the same performance, things like screen size and input methods would make the game unplayable – everything would be too small and your thumbs would be in the way.
So imagine again that you're playing the HTML5 BF3 on your desktop; you have a fullscreen, immersive first-person experience with complex mouse and keyboard controls, like any other native desktop shooter.
Your friends are also playing in the game via WebSockets and you can see them running around in the game world.
Now imagine you need to leave the house for a while (god forbid) and you don't want to stop playing the game.
On your way out of the door you open up HTML5 BF3 on your mobile phone, but instead of trying to replicate that epic immersive experience from your desktop you now jump into a mobile-optimised version of the game.
In this version you're still using WebSockets to connect to the same game that you're friends are still playing in, but instead of the intense 3D WebGL graphics and complex control system you get a 2D birds-eye view of the game world.
In this view you can see little green dots that represent your mates, and little red dots that represent the enemies.
From here you can basically act as the commander of the team in the same game you were in previously, while playing to the strengths of the device that you're currently on.
By clicking on each teammate you can send them a message that will show up on their HUD on the desktop, or by clicking on enemy players they will be highlighted in the 3D world that your friends are still playing in, or call in a airstrike by dragging out a target area on the map.
The point here is that you're still able to take part in the same game world that you were playing in previously while keeping a realistic and enjoyable experience by adapting to the device that you're using.
I came up with this concept quite a while ago, and not long after that I discovered that EA had already produced a working demo that they showed off at the last Google I/O.
They called it Strike Force and the idea was that you have desktop players competing in a rich 3D arena using the Gamepad API. From there, mobile players could jump into the same game but instead of seeing the rich 3D world they'd see a top-down map that allowed them to drop airstrikes and generally help or frustrate the other players.
It's a very promising concept and it appeals directly to the core of HTML5 and the promise of cross-platform games. I truly hope that we see more of these games in the future.
Where will we be in 2 years?
I hope it's clear by now that HTML5 games have made significant progress over the past few years, and that we have huge untapped potential with the Web for it to become a games platform in it's own right.
So what do the next 2 years have in store for Web games? Here are my predictions…
- We'll see some big games and announcements as a result of the huge interest from AAA (no, not the batteries) game studios and tool-makers
- Browsers will be even faster than they are today as a direct result of improvements made for games
- Mobile JavaScript performance will be significantly better than it is today, particularly with things like WebGL
- We'll be tantalisingly close to replicating near-native speed with JavaScript
- HTML5 games will move out of browsers and into 'native' experiences that show no sign of being a 'browser'
- HTML5 games will move to the TV as a result of things like Ouya
- We'll experience the first original HTML5 game (not a port) to become a huge success and make a bucket-load of cash
- The idea of seamless gameplay across devices and platforms will be every-day
Whatever happens, I'm hugely excited about the future of games on the Web. We've really not even scraped the surface of what's possible yet!
11 Dec 2012For the Node Knockout last month, we wanted to build a game in Light Table using ClojureScript. What we ultimately came up with was a fun little racing platformer called ChromaShift. All the code and assets were created during the 48 hours of the competition and in the end we did pretty well, coming in 7th overall. Here's a video of the game in action:
How do you build a game in ClojureScript?
I've built a couple games before in JavaScript that relied on the more standard inheritance based design. But that OOP-centric way of thinking about the entities and logic of the game doesn't fit very well with ClojureScript. Given how young it is, Clojure(Script) hasn't been used to create a lot of games and even if you broaden the search to functional languages, you'll find that it's still mostly an area of research. After some digging though, I did find a game design theory that actually fits very nicely: the Component-Entity-System engine. Much to my surprise, though, I couldn't really find a good explanation of the concepts on the internet, which among other things might include an actual example. So, let's fix that and go through the concepts behind how ChromaShift works.
The traditional way of thinking
The traditional way of thinking about games is fairly intuitive. You represent objects in the game as objects in code. So if you have a player, you'd create a player class that contains all the player's attributes; things like position, health, ammo, etc. Then you create an update() method that calls other methods like shoot() or jump() that read and change those attributes. To keep things DRY, you'll probably end up creating some base classes because entities in a game often have many different variations with small differences.
The problem
The problem with this, however, is that in order to actually reuse code as much as possible, you end up being forced into deep unnatural object hierarchies with lots of overridden methods. With thousands of entities in a game, you lose all sense of where things are defined, how they're changed deeper in the hierarchy, and where the best place to add something really is. This approach also means new combinations of functionality have to be written by programmers, forcing game designers to ask for different variations.
A different approach: Component-Entity-System
A CES engine addresses a number of the problems game devs ran into while trying to build complex games that required a lot of variation in the game objects. As we'll see, it also ended up being a great way to give content designers a lot more power in shaping the functionality of the game.
A CES engine has 3 parts as the name suggests; components, entities, and systems. I think the best way to go about understanding how it really works is to walk through each of these pieces individually and then see how they fit together.
Entities
We're actually going to start in the middle, because entities are the easiest thing to understand. They are nothing more than a unique ID. That's it. You use this ID to link together all of the state related to something in the game. That state is held in components.
Components
Components are little single purpose bits of state. Instead of representing a player as a monolithic object with lots of attributes to cover everything from position to number of lives, you break these different aspects apart into single intentioned objects. For example, a component for position would have an x, y, and maybe an angle of rotation, but nothing else:
(component position [x y a] :x x :y y :a (or a 0))All that does is basically create a function that returns a map with those values in it. So what we're doing is exploding all these monolithic objects apart, and turning entities in the game into simple groupings of components. A player for example, might look like this:
:player [(position 10 10) (walk 20) ;; set speed to 20 units (jump 50) ;; jump height to 50 (renderable render-player) ;; render the player ...]Here's a real level from ChromaShift, showing you a more complex example. The tremendous advantage you get from this approach is that you gain composition for free - no overriding, no deep hierarchies, just simple groups of components. As an example, let's say we have two bad guys in our game; one that walks fast and another that is slower but jumps every 3 seconds. That's just a matter of giving them both a walker component with different speeds and then giving the jumping one a jumper component:
[:fastguy [(position 10 10) (walker 20)] ;;really fast :jumpguy [(position 10 10) (walker 5) (jumper 3)]] ;;jump every 3 secondsSince objects are just groupings of components, at the core of the CES engine sits a datastructure that maps these entity IDs to the group of components that make them up. Fortunately, that's very easy to represent as a hash-map or dictionary in most languages. In ClojureScript you just use a map of integer ID to a vector of components:
{1 [{:name "position" :x 10 :y 10 :a 0} {:name "health" :lives 3 :health 5} ...]What's particularly interesting here is that we've turned our entire game into a datastructure. By being data-centric, we gain introspection, composition, runtime modification, and a host of other things for free. As I mentioned before, it's also really easy to build an editor that allows non-programmers to simply create new groupings of components and as a result create fundamentally new objects for the game.
So far, however, all we've talked about is state. There's been no logic in entities or components, which brings us to the final piece of the puzzle: systems.
Systems
Systems are single purpose functions that take in a set of entities which have a specific component (or set of components) and update them. Let's look at the concrete example of rendering to see what I mean. First off, we need a component to say that this entity is meant to be rendered. We'll call it renderable:
(component renderable [func] :fn func)The component just takes in a function that will be called when we render the object. So what our system needs to do is to iterate through every renderable entity, get its renderable component, and call that render function. Sounds simple enough, and the code ends up being as straightforward as that explanation:
;; define a function 'renderer' that takes all renderables (defn renderer [renderables] ;; for each renderable entity (doseq [e renderables] ;; get the entity's renderable component (let [rend (as e :renderable)] ;; call the stored render function with the entity ((rend :fn) e))))So now that we have a renderer function, we just need to call it in every tick of our game loop with all the entities that have the renderable component:
;;... other systems (renderer (all-e :renderable)) ;; get all entities that are renderableAnd that's it. Now any entity that gets the renderable component will start rendering with every tick of the game. By doing your logic this way, you ensure that it's single purpose, easy to modify, and freely available to any object in the game that may need it.
Data and simple functions
This approach turns a game into a data-structure with simple functions that work over it. It allows you to easily combine functionality in interesting and clever ways that you may not initially have realized, and keeps you out of the murky hell that is deep, highly overridden hierarchies. As a bonus, the engine is even quite easy to implement in basically any language. In ChromaShift's case the core structure and caches for the engine are actually written in JavaScript for performance reasons, while all of the game CES is written in ClojureScript.
A twist: CES and Light Table
The most interesting aspect to all of this, however, is that I haven't just been telling you how we built ChromaShift for the Node Knockout, but I've also been laying the conceptual groundwork to understand how Light Table itself is constructed. In my next post we'll see that the latest version of Light Table is actually heavily inspired by the concepts of CES engines and takes a novel approach toward applying some of these ideas to an event driven application.
Multiplayer and browsers
When you consider making multiplayer games, there are many methods available for creating a game that friends can play online. There is a good variety of multiplayer game types - take for example a card game you play synchronously with friends. Turns are made, information is exchanged in (semi) real time and the game progresses in discrete steps. Another example, Chess, can be asynchronous. Players take their time, contemplating possible actions and play their next move one week from now. These types of multiplayer games exist in browsers, and have for a long time. The nature of the browser itself makes it easy to make semi real time games, but we want more-visceral real time action.
Card games and Chess both usually require communication with a server and communication with the other players in order to work online. This is the foundation of a multiplayer experience to be possible - and for a long time this has existed via HTTP, where POST and GET have always been used to manage games.
The trouble with these methods is the delay, posting a message and waiting for a response each time is just too slow. It works for the semi real time and asynchronous games, but real time games require messages sent and received sometimes in the region of 33~66 times per second, something that is not quite possible with HTTP alone.
Luckily, in modern browsers we can take one step higher, and have a real time connection between a server and clients. The purpose of this discussion is to present one overview of how multiplayer games are made. We will look at input prediction, lag compensation, client interpolation and more importantly - how to do this in your normal browser using websockets. The article will present a playable demo with parameters to play with, showcasing the concepts discussed.
The technologies that we have chosen and why
Socket.io
socket.io is a powerful and flexible server-side and client-side component that enables real time networking in your browser. Not only does it support newer technologies like web sockets, but it also falls back safely onto a Flash networking layer, XHR or JSON long polling and even an HTML file transport layer. Most appealing about it perhaps is the simplicity and inherently asynchronous nature it brings, which is extremely useful when writing server and client code.
Another benefit of using socket.io is the fact that it ties into Node.js seamlessly. When coupled with Express, on connection, it can serve the client-side includes, game files, and data, making the integration clean and easy. Once you set it up, the amount of code between first connection and communication with a client is nothing short of amazing. And it would work in all browsers, mobile included.
Node.js
node.js is an easy to use, flexible and cross platform tool. It is like a swiss army knife of evented IO. It has a massive following of dedicated users, developers and module authors. It is supported on a good number of server hosting platforms for web applications, and is easy to install on a dedicated server of your own, so finding a host should not be a problem.
Among many of these great modules available for Node.js, is a web framework called Express. It covers serving files, complex routing, client authentication and sessions, and much more. It fits perfectly into the stack between socket.io and our clients, where socket.io can serve its files to the clients through Express and Express can handle the game content for us.
Canvas/HTML5
This article uses a 2D canvas to demonstrate the methods we are going to cover, which allows us to draw some text and boxes really easily.
Getting started with a real time multiplayer connection in your browser
Covering all possible options and installations and configurations for the above technology is a little out of scope for this article, but each of the above tools have their own documentation and should not take long to have set up and working together. For the sake of brevity we will dive right into making a simple game example instead. The information below is the minimum required to set up an empty canvas, connect to the socket.io server and receive messages.
Start with a simple web server, enter Express
The code for a simple Express server is really short and straightforward. It serves files on a port specified (4004 in this case) and it only serves files from the root folder (like index.html), and from a specific ‘game’ path we specify (like game/).
Socket.io, adding the real time component
Now we add the code for the socket.io part of the node.js server. It goes in the same file as the Express code, just below it as shown in the gist. The server will attach itself to Express so that it may serve the client files when requested. We do not handle any sessions in this example specifically, but you can learn about using those from this site: http://www.danielbaulig.de/socket-ioexpress/.
Index.html, connecting a client to the server
The client side needs very little code to be able to connect to the server. As we go further, it becomes more intwined, but this is all that is required to connect to the server and send or receive data.
Getting into the gameplay
What we need now is a simple interactive example for this to tie into, to get our feet wet. We are going to have two blocks running around in the same space. There is a lot of code and logic that goes into creating something that runs smoothly over the internet, and any game related code getting in the way is not as useful to this article. Instead, we focus on the multiplayer and use a simple example to demonstrate.
Some notes on developing real time games
Not all developers make games. There are some things that are new to developers that are entering the world of games that should be covered briefly.
Frame rate independence
When a block travels across the screen, it can be a simple line of code. block.position.x += 1; This 1 here, what unit is that measured in? Actually, this is one pixel - but it is moving 1 pixel per frame. Each time the loop runs, we move one pixel. That is - at 30 frames per second, it moves 30 pixels. At 60 frames per second, it moves 60 pixels. This is really bad for games, because hardware and performance can vary from one device or computer to another. Even between different browsers, this problem can be a huge difference. One player reaches the wall, the other barely moves at all.
In order to ensure that the block moves the same distance over the same amount of time, the concept of delta time is used. This value is the milliseconds per frame (mspf), which is measured while updating your game. It is how long one update takes. By taking the time at the start of your loop, and the time at the end of the loop, you can work out how long the update has taken.
At 30 frames per second (1/30) the delta is about 0.033s. One frame, every 33.3 ms. At 60 frames per second (1/60) the delta is about 0.016 or 16.66 ms per frame. So lets say that the ball is moving at 1 pixel per frame. To solve the problem of frame rate dependence, we multiply any change in position by the mspf value, balancing out the time, making the distance always the same.
Our example calculation becomes ball.position.x += (1 * deltatime);. With bigger delta (slower frame rate) the ball moves more pixels - reaching the destination at the same time as at a smaller delta (higher frame rate). This gives us concrete units that will act the same at any render speed. This is critical for animations, movements and pretty much any value that changes over time: they all should be scaled to the mspf.
Planning for change
Games are often a dynamic thing: they require tweaking and changing of many values to feel good. Iteration is a big part of getting this right. This is common sense for most programmers, but always try and design your code so that you have as many values and variables as possible to tweak. Then expose them in a really easy to use place, so you can constantly refine how the game feels and how it works without much digging and effort. Try and bring your iteration time down as much as possible.
In the demo accompanying this article, we have exposed our values via Dat.GUI so that you can change and interact with the demo in real time, and feel the effect of the changes as you make them.
Multiplayer games in real time
Games are a difficult thing to make. Getting the game play to feel good, physics to be smooth, collisions to be correct and controls to feel tight - all these things take hard work already. Adding a multiplayer component makes this far more complex, as there is now a server involved. Players need to be informed of other players’ actions but there is a network delay.
Networking on a high level, a simple lobby
The way we will approach networking our game example is fairly straightforward. Our game in this demo can only have two players in it, for simplicity. In our demo, a client connects to the server, then the server either gives them an existing game to join, or creates a game for someone else to join. Then the game is added to the list of games on the server to update, and the clients update their game on their end. This is illustrated below; it works like a very simple lobby system.
Networking and the game loops
When it comes to a real time game, we want to run the game logic itself on the server AND the client. This is due to the fact that the server needs to be the authority on the state of the game at all times, but the client needs to run the game locally too. Each frame on the server, input from the network will be processed and applied to players, and that change is sent to the other players at a fixed rate. On the client, input will be collected and sent to the server, and positions can be updated while waiting for the messages to come back from the server (client prediction).
The approach we will be implementing works as follows:
- Client presses the right key, client moves immediately right
- Server receives the input message and stores it for the next update
- The next server update, the player input is applied, moving him right on the server state
- All changes in state are sent to all clients
- Client receives the message immediately setting clients positions (authoritative)
- Client can smoothly correct mistakes in prediction from the first step
The game server set up
On the server, we have two updates running. The one update is run at a high frequency, which updates the physics and state of the game. We will call this the physics update loop, which is run every 15ms (about 66 updates per second). The second update we can call the server update loop, which is run at a slower rate, every 45ms (about 22 updates per second). In the server update loop we send the state of the server to all clients. Most of what we will implement is based on the theory presented in the networking of the Source Engine from Valve Software.
The server update loops looks like this: :
The server physics loop (15ms)
Don’t let the term physics scare you, in our example it is extremely simple linear motion. We take the input from the clients, and we move them according to what they pushed. If they press left, you move them left. When we add client side prediction, we need to also tell the clients which of their inputs we had processed last. So how does our server update the physics?
- Process input that we stored from the network
- Work out the direction they intended to move based on input stored
- Apply the changes of this direction to the player position
- Store the last processed input number
- Clear any inputs that we have stored
The server update loop (45ms)
The update loop sends the state of the server to all clients. This varies per game of course, and in our example the state consists of player positions, the inputs of the player we have already processed (the last processed input number), and the local server time.
What you send in the state update is up to you, and often more than one server update loop can be employed to lower the amount of traffic used. A simple example would be a day/night cycle. If the cycle was changing at a much lower rate than everything else, you can send the state of the sun every 5 seconds instead of every 45 ms.
The client set up and update loops
On the client we also run multiple loops, one for the physics/game at 15ms again, like the server. The second is the regular update loop, but instead this one runs at 60fps (preferably), or as fast as the client can run the game. In HTML5 we usually rely on RequestAnimationFrame to handle this update, calling whenever the browser updates the window. This update loop is detailed below, and is quite standard:
- Clear canvas
- Draw info/status
- Handle input (sends input messages to server)
- Update position (using client prediction)
- Move the other clients based on the server position (interpolation)
- Draw players on canvas
The client physics loop
The important thing about the client physics loop has to do with keeping the client positions in sync with what the server has decided our position to be. This means the physics has to match the server when it decides how far to move us, and this is why the physics is updated at a fixed rate. Both the server and client physics should arrive at the same conclusion, given the same inputs. If you have pressed right twice, the results should be almost identical to what the server will calculate your position to be. This is what makes client prediction possible when attempting to mask the delay in a network and the clients.
Important Networking Concepts
Client prediction
We have mentioned this before now, so lets take a look at what exactly it entails. In a naive approach to networking, you might try the following model:
- Client presses the right key, tell the server
- Message arrives at server sometime in the future ( 200ms latency )
- Server sends back the new position to the client
- Message arrives at the client ( 200ms latency )
- Client updates their position 400ms+ later.
This approach might work well over LAN connections where the latency is really low, but when connecting players to a server via the Internet, latency can be anywhere from 30ms to 800ms - rendering the game unplayable because of the delay. When you push a key the response is so badly delayed that it will not be a very good game to play at all. But how do we solve this?
Client prediction is the solution, and simply means acting on input immediately, predicting what the server will calculate as well. We apply input with the assumption that your results and the server results (whenever they arrive) will be the same. When a client presses the right key twice, and ends up at x = 2, the server will arrive at the same conclusion and tell you 600ms later - you are still in the correct place.
This is important for immediate feedback on the client side, and even though updates are running via a server, the client positions should match up.
Interpolation of other client positions
Now all we need to update is the other client positions, as they arrive from the network. Again, a naive approach would be to simply set their positions as soon as the message arrives from the server but this leads to extremely jerky rendering of the other clients.
The solution is to store the positions we get from the server and interpolate between them. This means that we draw them a few frames behind the server, but it allows for very smooth updates of all other client positions. In our demo, based on the Source Engine article listed above, we draw the other clients 100ms behind the actual server positions.
All of this is implemented in the demo and elaborated on below in code form, but for more information and very good diagrams on the topic, Gabriel Gambetta did an excellent three part series on the concepts presented - including client prediction, interpolation and the reasons why these work best for real time games. Most important for our example is that we store the input sequence of each input the player gives us. We use this to track where the server is in our list of inputs, and we re-process input that the server has not yet received.
Understanding the demo code
The demo code presented at the end of the article features a working set of the topics discussed, including some debug controls to tweak and see changes and differences in approaches. The demo looks something like this :
Now that we have seen the theory of the example, we can start to see how the code comes together.
How the code is structured
The code in the demo contains four files, each with different portions of the example. The files contain the following logic:
- client.js The logic for the game client setup in the browser.
- app.js The server side app to run on node. This handles all the node/express/socket.io set up and code.
- game.server.js The logic for the game server (‘lobby’).
- game.core.js The logic for the game play itself, both server and client.
Core game play code
The code inside of game.core.js is the important part of our example. The code is shared between both server (running on node.js) and the client (running in the browser). This allows the code to use the exact same functions and algorithms to process input, synchronise movement, and share data structures.
Classes of the core game play
The game.core.js file hosts three classes, described in detail below.
The game_core class
This class is the driving factor for the whole game state. It runs the update functions, it handles the outputs and inputs of the game and manages the game as it changes. The game core can be described as the game world. It contains two players, a boundary, and it runs the world logic. It makes sure the physics simulations are started up, it makes sure they run on time and it handles the logic of the players inputs.
The game world is where multiplayer happens. We want the game world to exist in three places (for this demo). We want to run a copy of the game world on each client, and one on the server, per game. This is what the lobby does in game.server.js - it creates a world for each set of players that join.
All the code is named according to the purpose served. If the function name starts with client_, this code will never be called on the server side. If the function begins with the term server_, similarly this code will not run on the client but the server only. All other functions on the game_core class is directly related to the game state that gets shared between server and client.
The game_player class
The player code is probably a lot lighter than you might have expected, but the player class simply maintains it’s own spatial properties and knows how to draw itself (if required, like on the browser client). On the server, of course, the draw function is just never called.
An example function that is shared between the client and server
Important functions in the multiplayer code
Some of the functions are more important to multiplayer than others. Let’s look at the important concepts outlined in code, to see how the flow works. The code examples are sometimes simplified to demonstrate the key concept.
Entity interpolation (other clients)
The interpolation/smoothing of the other clients. This is handled in this fashion:
- Store the network messages from the server about other clients, for at least 100ms
- Interpolate between a last known position, and a newer position in time (100ms behind the server time)
- Draw the interpolated clients at the interpolated position.
The way we achieve this is as follows :
Client prediction (local client)
The prediction takes place in two places, when receiving server authoritative responses, and before drawing we process our input as it happens locally. The logic for this is:
- Handle input from client
- Store input and the time of input for smoothing later
- Store the input sequence
- Send the inputs and input sequence to the server
- On confirmation from the server of last known input sequence,
- Remove inputs that the server has already processed
- Reapply inputs that still remain to be confirmed
Here is the code simplified to show the input handling:
Conclusion and demo (code and links)
Multiplayer is a complex thing, and hopefully this simple example has given you an insight into a world of having friends play together in real time.
View the demo
Get the code
https://github.com/FuzzYspo0N/realtime-multiplayer-in-html5
Know your enemy (further discussion)
Keep in mind while you venture into the world of real time multiplayer - it is both a challenging and rewarding experience. Do some more research outside of this article and find out just how your real time game needs to be structured. There are many great resources: Glenn Fiedler has extensive experience and generously shares a wealth of knowledge on the topic. Be sure to read all of the articles on multiplayer and programming games that run in real time. He covers the basics of What Every Game Programmer Needs To Know About Networking, and also just as important is his article and demo on Networked Physics.
Another set of good articles are from Forrest Smith, who worked on the Supreme Commander engine at Gas Powered Games. This article is aimed at Real time Strategy Games (RTS): The first part, Synchronous RTS Engines and a Tale of Desyncs and the second, Synchronous RTS Engines 2: Sync Harder.
X-Type – Making OfFor 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 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.
One of the more difficult aspects of creating our Luxahoy HTML5 game was figuring out how to combine two very cool frameworks: Easel.js and Box2dWeb. In this tutorial we will go over the basics of "actor" objects so that you'll be able to translate box2d measurements to the x, y, and rotation parameters of your display object in easel.Before starting this tutorial it is assumed that you have a basic knowledge of how box2d and easel both work. If you are new to the box2d world, please read through this awesome series of box2d orientation and tutorials from Seth Ladd. If you are new to easel, please check out the documentation so you can get an idea of how to add objects to the stage and understand the overall display hierarchy (hint: it shares several things in common with Flash coding).
Ok, now that that's out of the way, let's dig in. What we're going to create is create a simple demo that rains pink birds like so
View in browser - Download source codeSince we assume a basic understanding of box2d and easel we're going to skip the setup code and dive right in to combining the two frameworks. If you'd like a refresher on that simply download the demo zip or view the source on the demo page.
Now in Easel.js it's extremely simple to add an object to the stage at a specified position. Here's how we add birds to the screen from the ticker (loop) function
Note: all code below is non-functional excerpts from demo.js. For working code, download the zip.
1 2 3 4 5 6 7var tick = function(dt, paused) { birdDelayCounter++; if(birdDelayCounter % 10 === 0) { // delay so it doesn't spawn a bird on every frame birdDelayCounter = 0; birds.spawn(); } }This should look pretty familiar. This code simply adds a bird to the stage in a random x position at the top of the canvas. The bitmap object itself is quite versatile, you can tween it's position, set it's alpha, skew it, etc. However, if you want to apply physics to your asset you'll have to send it to box2d.
1 2 3 4 5 6 7 8var spawn = function() { var birdBMP = new Bitmap("images/bird.png"); birdBMP.x = Math.round(Math.random()*500); birdBMP.y = -30; birdBMP.regX = 25; // important to set origin point to center of your bitmap birdBMP.regY = 25; stage.addChild(birdBMP); }regX and regY (origin point) are important when tying your easel object into box2d. This is because box2d objects have an origin point in the center as opposed to the top left like easel display objects.
Now that we have our images being added to the stage it's time to send 'em over to box2d for further awesomeness.
1 box2d.createBird(birdBMP);The code above will create a box2d circle body at the same position and size as the bird bitmap object. Unfortunately though, the bird skin is still not mapped to the box2d object. Without debug on you would not see the box2d circle and the bird would remain stationary. So how do we tell the skin to follow the position of the box2d body?
1 2 3 4 5 6 7 8 9 10 11 12 13var createBird = function(skin) { var birdFixture = new b2FixtureDef; birdFixture.density = 1; birdFixture.restitution = 0.6; birdFixture.shape = new b2CircleShape(24 / SCALE); // half of bird.png width divided by world scale for right size var birdBodyDef = new b2BodyDef; birdBodyDef.type = b2Body.b2_dynamicBody; birdBodyDef.position.x = skin.x / SCALE; // divide skin x and y by box2d scale to get right position birdBodyDef.position.y = skin.y / SCALE; var bird = world.CreateBody(birdBodyDef); bird.CreateFixture(birdFixture); bodies.push(bird); }...Actors! Actors are the meat and potatoes of having a visual box2d demo. Basically they have to run during the game loop and translate the box2d body metric positions back to pixel positions. Lets create one.
1 2 var actor = new actorObject(bird, skin); bird.SetUserData(actor); // set actor as userdata of body so we can get at it later if we need toThis is a basic example of an actor object, if you wanted to you could extend it with almost anything (ids, hit count, out of bounds function, etc). Now that an actor is defined for the box2d body we need to tell it to update during the physics loop.
1 2 3 4 5 6 7 8 9 10var actorObject = function(body, skin) { this.body = body; this.skin = skin; this.update = function() { // translate box2d positions to pixels this.skin.rotation = this.body.GetAngle() * (180 / Math.PI); this.skin.x = this.body.GetWorldCenter().x * SCALE; this.skin.y = this.body.GetWorldCenter().y * SCALE; } actors.push(this); }The bitmap skin will now mirror whatever the box2d body does within the physics simulation.
1 2 3 4// within physics loop before world.step for(var i=0, l=actors.length; i<l; i++) { actors<i>.update(); }If you want to remove the body AND it's skin together at some point you can add the body to an array to be removed. Bodies must be removed before each world.step.
1 2 3 4 5 6 7 8 9 10 11// before step for(var i=0, l=bodiesToRemove.length; i<l; i++) { removeActor(bodiesToRemove<i>.GetUserData()); // get the actor object in the user data of the body and send to removeActor function bodiesToRemove<i>.SetUserData(null); world.DestroyBody(bodiesToRemove<i>); } // after step if(bodies.length > 30) { bodiesToRemove.push(bodies[0]); bodies.splice(0,1); }There you have it! There's a lot more powerful things you can do with an understanding of actors such as hit counts, box2d collision filter changes, out of bounds detection, etc. I hope to cover more in later tutorials.
1 2 3 4var removeActor = function(actor) { stage.removeChild(actor.skin); actors.splice(actors.indexOf(actor),1); }If anyone has any optimization suggestions or other was to accomplish this please don't hesitate to leave comments.
CDN
Using the CDN (Content Delivery Network) hosted versions of the CreateJS libraries in your project allows them to be downloaded quickly and cached across different sites using the same version of the libraries, which can reduce bandwidth costs and page load times. CDH hosting provided by Adobe.
download on github documentationwhat is boxbox?
boxbox is a library that makes it easy and fun to use the box2d physics engine.
no, i mean really, what is it
boxbox is a framework for box2dweb, a JavaScript port of box2dflash, an ActionScript port of box2d, a C++ library.
The box2d physics engine is hard to use for a beginner. Further, its C++ style API doesn't make sense to JavaScript programmers. boxbox tries to solve both problems while adding its own sugar along the way.
features
- simple API
- work with entities rather than fixtures, bodies, and shapes
- attach events to entities rather than the whole world
- reuse common configuration among similar entities
- built in canvas rendering with image support
demos
tutorial
File: Description: This release contains: - Box2dWeb - minified version - demo.html (as seen at "Project Home") - example.html (simple example which generates some random objects) Changelog: 3: - 3.b2 is the new stable release 3.b2: - Fixes an issue with properties in ff 3.6 3.b: - removes lots of redundant code - cleaner javascript - introduced IE 9 support - fixes compatibility-issues with prototype.js, node.js and processing.js (other frameworks, libraries, etc. may be affected, too)SHA1 Checksum: 315bb2b1c603f6dae2cf886b2ebc9ef242579ac6 What's this?
It’s a bold claim, but I’m going to make it and back it up with two example games… HTML5 is *the* best platform for rapid game development available right now.
Nick vs BusI’m a student at the University of Texas, and two weeks ago, another UT student was hit by a bus (video embedded below, he made it out without serious injuries). As soon as I saw the video start going viral, I decided I would make a game for it. 24 hours later, the game was complete and went viral itself. In just a few days, the game had 25,000+ unique visitors, 1,500+ likes, and articles on sites like Kotaku, News.com.au, and the Statesman. Here’s the link to the game: Nick vs Bus
The game makes use of <canvas>, SVG (Scalable Vector Graphics – which were remarkably easy to implement), and Clay.io. It’s written in CoffeeScript, here’s the source code (keep in mind it was made in <24 hours). Thanks to HTML5, it’ll run from your phone’s browser as well (assuming have a semi-modern smartphone).
Nick vs. Bus wouldn’t have received anywhere near as much response if I had released it two weeks after the incident, and HTML5 was the perfect language to crank it out in such little time. (Keep in mind when I say HTML5, what I really mean is <canvas> and JavaScript)
The graphics aren’t great, but that’s not my background (if you’re curious, I used Inkscape)
Word Wars
The second game that backs up my claim that “HTML5 is the best platform for rapid game development” is a game called Word Wars.
Word Wars was initially developed by a couple other students and friends during a 24 hour hackaton at UT. It was called Worldle at first, and had some good reception from Hacker News. One of the developers didn’t even know JavaScript going into the thing (pretty impressive).
What’s more impressive about Word Wars in relation to Nick vs Bus is it has a backend + multiplayer (nowjs), all done in less than 24 hours. This one was done in pure JavaScript rather than CoffeeScript like the first — it’s really just a matter of preference, CoffeeScript isn’t going to drastically reduce your development time.
Again, the game works from mobile devices as well
That’s two games developed in less than a day, both with a pretty good reception.
Bonus: Since it’s the development time is so little, I even made a game for TechCrunch in an attempt to get noticed for an article. That attempt wasn’t successful (a couple editors tweeted about it, but no article), but here is the game anyways.
Why HTML5?The best candidate to compare HTML5 to for rapidly developing and deploying games like Nick vs Bus and Word Wars is Flash. Java, C++, Unity, iOS and the rest all typically have longer development cycles, not to mention they simply wouldn’t have spread as quickly since people are forced to download the game (or use the terrible Java applet). Sure, you can create a game fairly quick in those languages if you’re very familiar with them, but I’d argue that, given an equal knowledge of all languages, HTML5 would win out on rapid game development. If you’re hoping to create the next Call of Duty (we definitely need more games like those /sarcasm), go with C++… WebGL is great but we’re probably still a couple years away from seeing console-quality games using it.
Why HTML5 over Flash?
Mobile Support
This is the big one in my eyes. Right now the main benefit of HTML5 is it’s “cross-platformness” by nature. 3,000 of the 25,000 visitors to Nick vs. Bus came from mobile devices. Most of those came from people clicking the link from Facebook’s app.
Sure, you can get your Flash game on the App Store (when I say App Store, I mean iOS, Android and WP7), but that takes time, and any update you make takes time as well… With HTML5, you can get your app on the App Store if you’d like, plus your game can be played straight from the browser.
For the Nick vs. Bus game, mobile support would just now be ready… two weeks later… if I had used Flash.
In addition to mobile support, HTML5 paves the way for games to be playable on smart TVs (in the hopefully near future), Windows 8 apps, and perhaps even consoles as Microsoft is very supportive of the new markup.
No compiling
Having to wait for a compiler sucks. With JavaScript you don’t have to. (Yeah, I use CoffeeScript, a compiled language; however, it’s very quick to compile and certainly not necessary). That also leaves open possibilities like this.
Easy Testing
As per the nature of the web, to test a game on different platforms, you just point your browser at wherever you chose to upload your game. Testing for mobile is the same, and since most browsers and mobile browsers have their own JavaScript console, it’s fairly easy to find the issue and debug quickly.
Same language for a backend
Should your game need a backend (if for example, you want a multiplayer component), you’re able to use the same language: JavaScript (node.js), which just makes sense. Why write a game in two different languages when you don’t have to? For one of our games, Slime Volley we run a separate instance of each game on the server. In our implementation, we simply share code between the client and server, so there’s no need to rewrite the mechanics in another language. The game’s source code can be found here.
It’s not a plugin
Okay, okay… pretty much everyone has Flash, but it’s hard to argue against a standard being better than a plugin. Apple has already taken a stance against Flash. Sure, none of the major players in browsers are going to publicly take a stance against Flash, but I can see HTML5 being favored over something that is completely beyond their control.
It’s not controlled by a single corporation
Adobe does fine work with their products, but in this case Google, Mozilla and Microsoft all competing against each other will be able to continue making something better. Chrome was the first to really make HTML5 games viable with the V8 engine, and now Firefox and even IE are doing their part to push the limits.
Great stuff is being pushed out like the Gamepad API and Notifications API.
More active development
Because it is new, more and more developers are building tools for HTML5. New doesn’t necessarily mean better… in fact, it means it’s likely less stable. What it does mean however is there is likely much more active development going on and much less legacy code / excess bulk.
HTML5 games get more attention
Honestly, HTML5 games get more attention just for being HTML5 – deserved or not. It’s still a buzz-word, and early adopters seem more likely to have a look, opposed to flash games which they’ve played hundreds, if not thousands, in the past 10+ years.
HTML5 Weak Spots
The two biggest criticisms of HTML5 are audio and security. Audio still needs work, I’m not going to beat around the bush on that. It shouldn’t be long though…
As for security, I just wrote an article for DZone about this. It’s not published yet, but you can have a look at the Google Doc for it here. Basically, you can make your game secure, you just need a backend that can be done fairly easily with node.js.
Finishing Words
I’ve written a three part series on HTML5 game development tips — things I’ve picked up on along the way. Part 1 is about style & performance tips, part 2 covers input methods (keyboard, mouse, touch, accelerometer, etc), and part 3 (link to Google Doc since it’s scheduled to be published on Monday) talks about security tips.
I’m betting on HTML5 which is why I’m building what’s essentially “Steam for HTML5″. It’s called Clay.io. You can have a look at more HTML5 games in the Clay.io store.
PhysijsExamples
Physics plugin for three.js
Physijs brings a very easy to use interface to the three.js framework. One of the reasons three.js is so popular is because it is so incredibly easy for graphics newbies to get into 3D programming. Physijs takes that philosophy to heart and makes physics simulations just as easy to run. In fact, there are just five easy steps that must be taken to make a 3D scene come alive.
How does Physijs work?
Physijs is built on top of ammo.js (although there is also a cannon.js branch) and runs the physics simulation in a separate thread (via web worker) to avoid impacting in your application's performance and taking up your your 3D rendering time.
A lot of effort has been made to keep the style of code the same when using Physijs. Apart from updating an object's position, all of the normal three.js conventions remain the same. If you are used to three.js, you already know how to use the Physijs plugin.
Who is this for?
You, hopefully. If you are familiar with three.js and want to add physics to your scene, this is the plugin for you. No mucking about with shape definitions, keeping objects in their correct positions, or identifying collisions - simply use a few Physijs objects in place of three.js's and you'll automatically have a dynamic environment.
If you need (or want) a feature not already included then add it to the issue tracker or implement it yourself and send over a pull request.
Features
- Support for multiple object shapes, including custom convex objects.
- Material system provides simple control over friction and restitution ("bounciness")
- Integrated collision detection and events
- Compound objects using the hierarchy system in three.js
- Rotations using either euler or quaternion systems - your preference
- Built seamlessly on top of three.js to keep the same convention and coding style
In the Future
- More (and better) optimizations
- Constraint systems such as point-to-point and hinge.
- Vehicle systems
- Heightfield/heightmap shape
- It would be awesome to have concave shape decomposition
From your friends at Pixel Lab
Built with kbuild
The MIT LicenseCopyright (c) 2010-2011 Pixel Lab
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Getting started
Basic SoundManager 2 set-up
Including the script, configuring soundManager.url (SWF file path) and registering an onready() callback:
<script src="soundmanager2.js"></script> <script> // where to find flash SWFs, if needed... soundManager.url = '/path/to/swf-files/'; soundManager.onready(function() { // SM2 is ready to play audio! }); </script>Upon execution of soundmanager2.js, SM2 will determine what Flash SWF to load (if any) based on the path defined in soundManager.url.
After successful initialization, soundManager.onready() will fire and sounds can be played.
By default, SM2 uses a Flash 8-based SWF build. Some additional features are available when using the Flash 9-based build.
For a live example, see the basic template demo.
A JavaScript Preloader for HTML5 Apps PxLoader is a Javascript library that helps you download images, sound files or anything else you need before you take a specific action on your site (like showing a user interface or starting a game). You can use it to create a preloader for HTML5 games and websites. It let's you monitor download status by providing progress and completed events and it lets you prioritize the order in which items are downloaded. You can even tag groups of files and then prioritize downloads or register for events by tag. We created PxLoader for the HTML5 version of Cut the Rope and out of the box works great with images and sound files (the types of resources we needed for the game), but it's designed to be extensible and work with any file type or other network action. Step by Step Instructions 1. Download the script and place the files in the JavaScript directory of your site. The files you need are: PxLoader.js (the main library file) PxLoaderImage.js (the image file downloader if you want to download images) PxLoaderSound.js (the sound file downloader if you want to download sounds)* PxLoaderVideo.js (the video file downloader if you want to download videos) Any other downloaders that you write or get in the plugins section of this page *The sound file downloader works with SoundManager 2 and allows it do the heavy lifting of the actual download. In fact, PxLoader always defers downloads to the downloader objects. That gives a complex sound library like SoundManager 2 the ability to choose how to download the file based on whether it's using Flash or HTML5 for the audio. 2. Add the core JavasScript file. <script type="text/javascript" src="js/PxLoader.js"></script> 3. Add plugins for the resource types you need. <!- images --> <script type="text/javascript" src="js/PxLoaderImage.js"></script> <!- sounds --> <script type="text/javascript" src="js/PxLoaderSound.js"></script> Download Images Before Drawing to a Canvas Images must be fully loaded before they can be drawn on an HTML5 canvas. In this example we wait for 3 images to download before drawing our picture. // Create the loader and queue our 3 images. Images will not // begin downloading until we tell the loader to start. var loader = new PxLoader(), backgroundImg = loader.addImage('images/headerbg.jpg'), treesImg = loader.addImage('images/trees.png'), ufoImg = loader.addImage('images/ufo.png'); // callback that will be run once images are ready loader.addCompletionListener(function() { var canvas = document.getElementById('sample1-canvas'), ctx = canvas.getContext('2d'); ctx.drawImage(backgroundImg, 0, 0); ctx.drawImage(treesImg, 0, 104); ctx.drawImage(ufoImg, 360, 50); }); // begin downloading images loader.start(); Run Clear Report Progress While Images Load HTML5 games typically download a large number of images. In this example, we'll report progress while the game loads 100 images. We've configured our server to delay each image by 1 second to simulate large images and so you can watch the progress. Don't worry, your browser will download batches of images in parallel so the demo won't actually take 100 seconds. // delay each image and append the timestamp to prevent caching var baseUrl = 'http://thinkpixellab.com/pxloader' + '/slowImage.php?delay=1&time=' + new Date, $log = $('#sample2-log').val(''), $progress = $('#sample2-progress').text('0 / 100'), loader = new PxLoader(); // add 100 images to the queue for(var i=0; i Run 0 / 100 Load Images in Groups Many HTML5 sites need to download resources for several different sections of the app. PxLoader lets you tag and prioritize resources. For this example we'll assume that we need one set of images to draw a game menu and second set of images for the gameplay. Since we need to show the menu first we'll start the download for those images first. PxLoader can provide progress updates to many listeners and will scope the updates and statistics to only the set of tags a listener is interested in. // delay each image and append the timestamp to prevent caching var baseUrl = 'http://thinkpixellab.com/pxloader' + '/slowImage.php?delay=1time=' + new Date, $log = $('#sample3-log').val(''), $menuProgress = $('#sample3-menuProgress').text('0 / 50'), $gameProgress = $('#sample3-gameProgress').text('0 / 50'), $totalProgress = $('#sample3-totalProgress').text('0 / 100'), loader = new PxLoader(); // queue 50 images for each section var addImagesForTag = function(tag, $progress) { for(var i=0; i Run 0 / 50 0 / 100 Load a Set of Sound Effects For this sample, we'll load 5 audio clips. Once all of the sounds are ready, we'll show a set of images that can be clicked to play each sound. The PxLoaderSound plugin relies on SoundManager2 to load and play audio files. First, add the soundManager2 and PxLoaderSound script references: <script type="text/javascript" src="soundManager2.js"></script> <script type="text/javascript" src="PxLoaderSound.js"></script> Next, you need to initialize the soundManager. Here are the settings we use: // initialize the sound manager soundManager.url = 'soundManager2/'; soundManager.flashVersion = 9; soundManager.useHighPerformance = true; // reduces delays // reduce the default 1 sec delay to 500 ms soundManager.flashLoadTimeout = 500; // mp3 is required by default, but we don't want any requirements soundManager.audioFormats.mp3.required = false; // flash may timeout if not installed or when flashblock is installed soundManager.ontimeout(function(status) { // no flash, go with HTML5 audio soundManager.useHTML5Audio = true; soundManager.preferFlash = false; soundManager.reboot(); }); soundManager.onready(function() { // ok to show the button to run the sound sample $('#sample4-run').show(); }); Finally, here is the code to load and play each audio clip: var soundNames = ['cow', 'pig', 'tractor', 'dino', 'r2d2'], loader = new PxLoader(), i, len, url; // queue each sound for loading for(i=0, len = soundNames.length; i Load Sounds Sounds (CC-BY): cow, pig, tractor, dino, droid Documentation for the Core API You can override the default settings when creating a new PxLoader instance. // create a loader using the default settings var loader = new PxLoader(); // create a loader with custom settings (shown with default values) var loader = new PxLoader({ // how frequently we poll resources for progress statusInterval: 5000, // every 5 seconds by default // delay before logging since last progress change loggingDelay: 20 * 1000, // log stragglers after 20 secs // stop waiting if no progress has been made in the moving time window noProgressTimeout: Infinity // do not stop waiting by default }); add(resource) Add a resource to the the loading queue. This generic method allows PxLoader to handle any type of resource that has a PxLoader plugin. // add an image with a url loader.add(new PxLoaderImage('/image1.png')); // add an image with a single tag loader.add(new PxLoaderImage('/image2.png', 'tag1')); // image with multiple tags loader.add(new PxLoaderImage('/image3.png', ['tag1', 'tag2']); // image with a priority (relative to another resource with the same tag) loader.add(new PxLoaderImage('/image4.png', ['tag1', 'tag2'], 1); Plugins may also add a convenience method to PxLoader which makes adding a new resource a little easier and more concise. // The PxLoaderImage plugin provides addImage which returns the img element // which can be used once the image has been downloaded var imgElement1 = loader.addImage('/image1.png'); imgElement2 = loader.addImage('/image2.png', 'tag1'); addProgressListener(callback, tags) Registers a callback that will be called anytime an event occurs for a resource with the specified tag(s). If the tags parameter is omitted then progress updates will be provided for every resource. // log the name of every resource as its loaded loader.addProgressListener(function(e) { console.log(e.resource.getName()); }); // the callback function receives an event object: var sampleEvent = { // the updated resource resource: {} // the resource parameter provided to loader.add() // status for the updated resource loaded: true, error: false, timeout: false, // stats for all resources that match the listener's tags completedCount: 42, totalCount: 100 }; addCompletionListener(callback, tags) Works in the same way as the progress listener API, except instead of receiving updates for each change you are notified once everything completes. // log when all resources have completed loader.addCompletionListener(function(e) { console.log('Ready to go!'); }); log(showAll) Writes a list of resources to the browser console. If showAll is true then all resources are logged, otherwise resources that have completed (loaded, error, or timeout) are omitted. // write a list of resources we are still waiting for loader.log(); isBusy() Returns true if the loader is still waiting for some resources to complete. start(orderedTags) Starts downloading all queued resources. Resources are ordered and started by tag first and then by priority. Its important to register all event listeners before calling start() because its possible for the loader to complete very quickly if resources are cached. // start loading resources according to resource priority alone loader.start(); // start resources by tag first and then by priority loader.start(['tag1', 'tag2']); Plugins Images PxLoaderImage loads images using JavaScript <image> elements. We listen to load and readystate change events. Some browsers won't send events for cached images so we also check the iscomplete status during the periodic poll initiated by the loader. Errors are reported to the loader, and details can be retrieved from the img element's errors property. SoundManager 2 PxLoaderSound loads audio using the SoundManager2 library. We're fans of this great little library because it supports HTML5 audio when available and falls back to flash when necessary. Videos Samples showing how to use this plugin are coming soon. Plugin API Writing a plugin so PxLoader can track another resource type is pretty easy. There are just a few functions that a resource should implement to communicate with the loader. Here is a skeleton that you can use to start. function PxLoaderResource(url, tags, priority) { var self = this; loader = null; // used by the loader to categorize and prioritize this.tags = tags; this.priority = priority; // called by PxLoader to trigger download this.start = function(pxLoader) { // we need the loader ref so we can notify upon completion loader = pxLoader; // set up event handlers so we send the loader progress updates // there are 3 possible events we can tell the loader about: // loader.onLoad(self); // the resource loaded // loader.onError(self); // an error occured // loader.onTimeout(self); // timeout while waiting // start downloading }; // called by PxLoader to check status of image (fallback in case // the event listeners are not triggered). this.checkStatus = function() { // report any status changes to the loader // no need to do anything if nothing has changed }; // called by PxLoader when it is no longer waiting this.onTimeout = function() { // must report a status to the loader: load, error, or timeout }; // returns a name for the resource that can be used in logging this.getName = function() { return url; } } Get Involved We'd love to hear what you think about PxLoader. It's a work in progress and we'd welcome any suggestions or code contributions. You can fork us on Github. There are also lots of opportunities to extend PxLoader to handle additional resource types. Video (contributed!) and JSON are a few we'd love to see. Please remember that not just some, but "all your resource are belong to us". Happy loading!
Oh my... I couldn't wait any longer...And many other cool ones here.
Now, if you want to know a bit of the background/making of...
Some months ago the guys at Instrument commissioned me for doing a experimental piece. This is the first time I get this kind of offer, I had total creative freedom (as long as the piece was cool :D). The experiment mainly had to demonstrate Google Chrome's performance.
First concept
The best thing to do here was to directly code it with Javascript. First needed to get a little framework done, a stats widget and then see where it could go. Then I started porting a super simple collision system in between circles I had in AS3, and mixed that with browser movement. So you could interact with a bunch of circles by shaking your browser. They liked the main concept so I continued developing it.
Google Gravity
At Hi-ReS! I was working on a experiment that consisted basically in the google homepage collapsing and you could play with the elements, throw them around and even search. It was done in flash with box2dflash and I did a custom editor for rebuilding the html layouts. Unfortunately I had to move to another project and the experiment development got stuck.
Back to this project, as I had a little physics engine going on they suggested using it for breaking elements of a site. At first I said that it couldn't be done because in html you can't rotate elements, then they replied with this link and I had to eat my words ;) Then I showed them the flash experiment and they seemed very excited about it. I was about to mention that still couldn't be done because I used Box2D and there wasn't a javascript version, but this time I googled first and, oh! surprise, a Japanese ported it! :D So it was all decided, it was the perfect chance to finish that experiment.
Javascript
It was all looking good then, it was all a matter of the browser being able to handle css rotations fast and at the same time use the box2d engine. And how surprised I was when I saw how fast it performed. Seriously, Javascript in Chrome almost reaches the speed of flash. (no, they didn't pay me to say this :P). Another good thing now was that it wasn't a flash hack anymore, meaning that in the original experiments I had images of the buttons, so in MacOS you would see the button using the Ubuntu skin. Not anymore as now uses directly the html elements \o/
Box2d-js improvements
Then it was time to do the usual process, the actual work, the hacks, refactoring, the addictiveness tests... Eventually faced the problem that box2d-js took a bit of time to load (5-10 seconds) because it loads many files... Well, not anymore! I managed to do a script to put all the files in one and also jsmined it, and I'll be sending the script to Yasushi later :D Glad to be able to contribute something back to box2d-js after all :)
Conclusion
Brilliant project, I may still submit another piece that it's half done, we'll see.
Now go and take a look at the code of each piece and feel free to reuse the code, in fact, I want to see a lot of websites "collapse". The code its really easy to reuse, just add the class "box2d" to any element you want to collapse, and the code will do the rest. If you do anything with it, post it on the comments down here please :)
three.jsJavaScript 3D library
The aim of the project is to create a lightweight 3D library with a very low level of complexity — in other words, for dummies. The library provides <canvas>, <svg> and WebGL renderers.
Usage
Download the minified library and include it in your html. Alternatively see how to build the library yourself.
<script src="js/Three.js"></script>This code creates a scene, then creates a camera, adds the camera and cube to the scene, creates a <canvas> renderer and adds its viewport in the document.body element.
<script> var camera, scene, renderer, geometry, material, mesh; init(); animate(); function init() { scene = new THREE.Scene(); camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 1, 10000 ); camera.position.z = 1000; scene.add( camera ); geometry = new THREE.CubeGeometry( 200, 200, 200 ); material = new THREE.MeshBasicMaterial( { color: 0xff0000, wireframe: true } ); mesh = new THREE.Mesh( geometry, material ); scene.add( mesh ); renderer = new THREE.CanvasRenderer(); renderer.setSize( window.innerWidth, window.innerHeight ); document.body.appendChild( renderer.domElement ); } function animate() { // note: three.js includes requestAnimationFrame shim requestAnimationFrame( animate ); render(); } function render() { mesh.rotation.x += 0.01; mesh.rotation.y += 0.02; renderer.render( scene, camera ); } </script>Change log
2012 04 22 - r49 (364,242 KB, gzip: 89,057 KB)
- Yet more ColladaLoader improvements. (ekitson, AddictArts and pblasco)
- Created documentation system. (mrdoob)
- Added some documentation. (mrdoob and sole)
- Added MorphBlendMesh. (alteredq)
- Added emissive component to WebGL Materials. (alteredq)
- Added DepthPassPlugin. (alteredq)
- Improvements to Path. (asutherland)
- Improvements to Curve. (zz85)
- Added ArrowHelper. (zz85 and WestLangley)
- Changed depth sorting in WebGLRenderer to use world positions. (alteredq)
- Improved physically based shading in WebGLRenderer. (WestLangley)
- Changed depth sorting in Projector to use world positions. (mrdoob)
- Added physical specular term also to normal map shader. (alteredq)
- Added TubeGeometry. (zz85 and WestLangley)
- Added needsUpdate flag to Material. (alteredq)
- Fixed GeometryUtils.triangulateQuads. (alteredq)
- Improvements to GeometryUtils.tessellate. (alteredq)
- Change PlaneGeometry from XY to XZ. (mrdoob)
- WebGLRenderer back to highp shader precision. (mrdoob)
- Added deallocateRenderTarget to `WebGLRenderer. (kovleouf)
- Support zIndex and scale into DOMRenderer. (ajorkowski)
- Improvements to CameraHelper. (zz85)
- Added 3D spline path extrusion support to ExtrudeGeometry. (zz85)
- MarchingCubes moved out of the lib into /examples/js folder. (alteredq)
- Added ImmediateRenderObject. (alteredq)
- Renamed __dirty* to *NeedUpdate. (valette and mrdoob)
- Added CustomBlending to Material and premultiplyAlpha to Texture. (alteredq)
- Improvements to CubeCamera. (alteredq and mrdoob)
- CanvasRenderer.setClearColor() and .setClearColorHex() now sets opacity to 1 when null. (mrdoob)
- Fixed broken UVs in SubdivisionModifier. (zz85)
- Renamed Matrix4's setTranslation, setRotationX, setRotationY, setRotationZ, setRotationAxis and setScale to makeTranslation, makeRotationX, makeRotationY, makeRotationZ, makeRotationAxis and makeScale. (mrdoob)
- Matrix4 static methods makeFrustum, makePerspective, makeOrtho to non-static methods. (mrdoob)
- Refactore handling of Matrix4 to Matrix3 inversion. (alteredq)
- Added GodRays postprocessing. (huwb)
- Added LinePieces support to Projector. (mrdoob)
- Fixed UVs handling bug in GeometryUtils.tessellate. (alteredq)
- Serious performance improvements to Matrix4, Matrix3 and Frustum. (gero3)
- Fixes to LatheGeometry. (zz85)
- Removed Vertex. Use Vector3 instead. (mrdoob)
- Implemented real Spotlights. (alteredq)
- Added ParametricGeometry. (zz85)
- Added basic OBJLoader in /examples/js/loaders folder. (mrdoob)
- Moved ColladaLoader and UTF8Loader to /examples/js/loaders folder. (mrdoob)
- Added VTKLoader to /examples/js/loaders folder. (valette and mrdoob)
- Blender exporter now supports linked groups. (Druidhawk)
- Added visible property to Material. (mrdoob)
- Removed Lamber+Texture support in CanvasRenderer. (mrdoob)
- Fixed normals in CylinderGeometry. (qiao)
- Added floating point textures support to WebGLRenderer. (mrdoob)
- Renamed AnaglyphWebGLRenderer and co. to AnaglyphEffect & co. and moved to /examples/js/effects. (mrdoob)
- Improvements to documentation system. (mrdoob and codler)
- Added AsciiEffect. (zz85)
2012 03 04 - r48 (393,626 KB, gzip: 99,395 KB)
- Added camera support to ColladaLoader. (jbaicoianu)
- More ColladaLoader improvements. (mrdoob, AddictArts, kduong)
- Updated IcosahedronGeometry and OctahedronGeometry with timothypratley's PolyhedronGeometry code which also brings TetrahedronGeometry. (mrdoob)
- LOD should now behave as expected from anywhere in the scene graph. (mrdoob)
- Added THREE.REVISION. (mrdoob)
- Fixed cancelRequestAnimationFrame polyfill. (also)
- Improvements to convert_obj_three.py. (alteredq)
- Fixes to Geometry's .computeBoundingBox and .computeBoundingSphere. (alteredq)
- Refactored ShadowMap shader. (alteredq)
- Fixed handling of meshes with multiple materials in SceneLoader. (alteredq)
- Changed Material's default ambient color to 0xffffff. (alteredq)
- Added normals support to MorphTarget. (alteredq)
- Added .setFrameRange and .setAnimationLabel to MorphAnimMesh. (alteredq)
- Added handling of named animation sequences to MorphAnimMesh. (alteredq)
- Extended MorphAnimMesh to be able to play animations backwards. (alteredq)
- Added .generateDataTexture to ImageUtils. (alteredq)
- Removed hierarchy support and .intersectScene() from Ray. (mrdoob)
- Added .triangulateQuads to GeometryUtils. (alteredq)
- Projector and WebGLRenderer now handles doubleSided lighting properly. (mrdoob and alteredq)
- Fixed MorphAnimMesh playback bug where the last frame didn't display. (alteredq)
- TrackballControls implements EventTarget. (mrdoob)
- Added .clone to Vertex, Face3 and Face4. (alteredq)
- Added .explode and .tessellate to GeometryUtils. (alteredq)
- Added .lerpSelf to Vector2, Vector3 and UV. (alteredq)
- Fixed DOMRenderer by using single-materials. (ajorkowski )
- Added .setPrecision to Ray. (mrdoob)
- Blender exporter now honors the "Flip YZ" option. (rectalogic)
- Added NoBlending to Material and WebGLRenderer. (kovleouf)
- Added .applyMatrix to Object3D. (mrdoob and alteredq)
- Added .attach and .detach to SceneUtils to retain position in space. (alteredq)
- Added .sign to Math. (alteredq)
- Implemented sphinx based documentation. (ivankuzev)
- Documented part of the API. (ivankuzev and alteredq)
- Replaced sphinx based documentation with compilation-less sytem. (mrdoob)
- Added default material to Mesh, Line and ParticleSystem. (mrdoob)
2012 01 14 - r47 (378,169 KB, gzip: 96,015 KB)
- Resurrected lens flares as custom WebGLRenderer plugin. (alteredq)
- Fixed typos in Matrix4's transpose() and getInverse(). (ekitson)
- "Pluginized" Sprites and ShadowMaps. (alteredq)
- Added Frustum class. (alteredq)
- ColladaLoader improvements. (ekitson, jterrace, mrdoob and alteredq)
- Lights in a hierarchy are now supported when using WebGLRenderer. (alteredq)
- Included requestAnimationFrame shim in the lib. (mrdoob)
- Started documentation effort in /doc (using sphinx). (jterrace)
- Changed default shader precission to mediump. (mrdoob)
- Added support for the format OpenCTM. (alteredq)
- Added BufferGeometry for direct rendering from typed arrays. (alteredq)
- Extended Texture class with format and type parameters. (alteredq)
- Automatically reducing texture to max size of WebGL hardware. (greggman and alteredq)
- Improved WebGLRenderer's Shadow Map code. (alteredq)
- Checking for xhr.overrideMimeType before using it (fixing IE support). (mrdoob and alteredq)
- Improved ATI and ANGLE support in across WebGLRenderer shaders. (alteredq)
- Added generateMipmaps property to Texture and RenderTarget. (alteredq)
- Frustum properly handling children with scaled parents. (avinoamr)
- Fixed Ray when dealing with big polygons. (WestLangley)
- Fixed WebGLRenderer bug where depth buffer was not cleared. (ekitson)
- Added CameraHelper objects for visualising both Perspective and Orthographic cameras. (alteredq)
- Improvements to Path. (zz85)
- Improvements to Postprocessing stack. (alteredq)
- Added shadows for DirectionalLights. (alteredq)
- Added Gyroscope object. (alteredq)
- Added alpha and premultipliedAlpha parameters to WebGLRenderer. (mrdoob)
- Ray properly handling children with scaled parents. (mrdoob)
- Renamed Axes object to AxisHelper. (mrdoob)
2011 11 17 - r46 (343.383 KB, gzip: 87.468 KB)
- Added reflections to Normal Mapping. (alteredq)
- Ray now checks also object children. (mrdoob)
- *Loader.load( parameters ) to *Loader( url, callback, texturePath ). (mrdoob and alteredq)
- Reworked scene graph setup. (mrdoob and alteredq)
- Fixed CanvasRenderer's SphericalReflectionMapping rendering. (mrdoob)
- Improved SubdivisionModifier. (zz85)
- Refactored *Controls to use externally supplied time delta. (alteredq)
- Improvements to CombinedCamera. (zz85)
- ColladaLoader doesn't create extra Object3D. (mrdoob)
- Improvements to Lambert and Phong materials. (alteredq)
- Removed multi-materials for simplicity reasons. (Multi-materials will come back with MeshLayerMaterial hopefully soon) (alteredq)
- Fixed Ray not considering edges. (mrdoob)
- Massive cleanup to WebGLRenderer. (alteredq)
- Ray optimisations. (mrdoob and alteredq)
- JSON file format is now worker-less (this was crashing Chrome/Firefox with dealing with many assets). (alteredq)
- Improved CubeGeometry, PlaneGeometry, IcosahedronGeometry and SphereGeometry. (mrdoob)
- Improvements to Curve. (zz85)
- Removed Collisions code and focusing on Ray. (mrdoob)
- Added cloneObject() method to SceneUtils. (alteredq)
2011 10 06 - r45 (340.863 KB, gzip: 86.568 KB)
- Object/Scene.add*() and Object/Scene.remove*() are now Object/Scene.add() and Object/Scene.remove(). (mrdoob)
- LOD.add() is now LOD.addLevel(). (mrdoob)
- Reworked CylinderGeometry. (mrdoob)
- Added .depthWrite and .fog to Material. (alteredq)
- Added .applyMatrix to Geometry. (mrdoob)
- Improved postprocessing stack in /examples/js/postprocessing. (alteredq)
- Added a realistic skin shading example. (alteredq)
- Started of a GUI for composing scenes and autogenerate code. (mrdoob)
- Added .center() to GeometryUtils. (alteredq)
- Fixed buggy scenegraph manipulation (adding/removing objects). (jsermeno, alteredq and skython)
- Renamed MeshShaderMaterial to ShaderMaterial. (alteredq)
- Fixed CanvasRenderer ignoring color of SmoothShadinged MeshLambertMaterial. (mrdoob)
- Renamed renderer.data to renderer.info. (mrdoob)
- Fixed ShadowMap aspect ratio. (kig and alteredq)
- Fixed Loader's extractUrlbase() incorrect output for short urls. (rectalogic and alteredq)
- Added .color and .visible support to Sprite. (alteredq)
- Added Face4, Vertex Colors and Maya support to ColladaLoader. (mrdoob)
- Rewrite of lighting shader code. (alteredq)
- Improved internal array concatenation approach. (pyrotechnick)
- WebGLRenderer performance improvements. (alteredq)
- Added lower level immediate rendering support to WebGLRenderer. (NINE78 and alteredq)
- Added CubeCamera for rendering cubemaps. (alteredq)
- Improved GeometryUtils's .mergeVertices() performance. (zz85)
- Removed Camera's .target. (mrdoob)
- WebGLRenderer's .clear() is now .clear( color, depth, stencil ). (mrdoob)
- Added .autoClearColor, .autoClearDepth and .autoClearStencil to WebGLRenderer. (mrdoob and alteredq)
- Added OctahedronGeometry. (clockworkgeek)
- Splitted Camera into PerspectiveCamera and OrthographicCamera. (mrdoob and alteredq)
- Special cameras are now *Controls. (alteredq and mrdoob)
- Added SubdivisionModifier. (zz85)
- Projector's unprojectVector() now also works with OrthographicCamera. (jsermeno)
- Added .setLens() method to PerspectiveCamera. (zz85)
- Added Shadow Maps, Texture's .offset and .repeat and reflections support to Normal Map shader. (alteredq)
2011 09 04 - r44 (330.356 KB, gzip: 84.039 KB)
- Added ColladaLoader. (timknip2)
- Improved ExtrudeGeometry. (zz85)
- Fixed CylinderGeometry normals. (alteredq)
- Fixed issue with WebGLRenderer.setTexture (rectalogic)
- Fixed TorusGeometry normals. (mrdoob)
- Fixed Ray behind-ray intersects. (mrdoob)
- Added OrthoCamera. (alteredq)
- Refactored postprocessing effects used in some examples. (alteredq)
- Added .deallocateObject() and .deallocateTexture() methods to WebGLRenderer. (mrdoob)
- Fixed a glitch in normal and phong shader. (evanw and alteredq)
- Added .frustumCulled property to Object3D. (alteredq and mrdoob)
2011 08 14 - r43 (298.199 KB, gzip: 74.805 KB)
- Improved Blender exporter - 2.58 (and 2.59) support, normals maps, specular, ao maps... (alteredq)
- Added CORS to ImageUtils. (mrdoob)
- Refactored TextGeometry and added Shape, Curve, Path, ExtrudeGeometry, TextPath. (zz85 and alteredq)
- Added handling of custom attributes for ParticleSystems. (alteredq)
- Fixed CanvasRenderer.setClearColor. (mrdoob, StephenHopkins and sebleedelisle)
- Improved uniform handling in WebGLRenderer. (alteredq)
- Implemented Shadow Mapping in WebGLRenderer. (alteredq)
- Added Spotlight light type. (alteredq)
- Fixed constructor-less prototypes. (pushmatrix)
- Added DataTexture. (alteredq)
- WebGLRenderer opaque pass now renders from front to back. (alteredq)
- Simplified Color. (mrdoob)
- Added preserveDrawingBuffer option to WebGLRenderer. (jeromeetienne)
- Added UTF8Loader for loading the new, uber compressed, UTF8 format. (alteredq)
- CanvasRenderer now supports RepeatWrapping, texture.offset and texture.repeat. (mrdoob)
- Removed Stencil Shadows and Lensflare code. (mrdoob)
2011 07 06 - r42 (277.852 KB, gzip: 69.469 KB)
- Added AnaglypWebGLRenderer and CrosseyedWebGLRenderer. (mrdoob, alteredq and marklundin)
- Added TextGeometry. (zz85 and alteredq)
- Added setViewOffset method to Camera. (greggman)
- Renamed geometries to *Geometry. (mrdoob)
- Improved Blender exporter. (alteredq, sweetfish and Jhonnyg)
- Added Blender 2.58 exporter. (georgik)
- Fixed Matrix4.multiply(). (thanks lukem1)
- Added support for additional Euler rotation orders in Matrix4. (rectalogic)
- Renamed QuakeCamera to FirstPersonCamera. (chriskillpack)
- Improved Normal Map Shader. (alteredq)
- Collision now supports Object3D.flipSided and Object3D.doubleSided. (NINE78)
- Removed most of SceneUtils methods. (mrdoob)
- Removed Sound object and SoundRenderer. (mrdoob)
2011 05 31 - r41/ROME (265.317 KB, gzip: 64.849 KB)
(Up to this point, some RO.ME specific features managed to get in the lib. The aim is to clean this up in next revisions.)
- Improved Blender Object and Scene exporters. (alteredq)
- Fixes on WebGL attributes. (alteredq and empaempa)
- Reduced overall memory footprint. (mrdoob)
- Added Face4 support to CollisionSystem. (NINE78)
- Added Blender 2.57 exporter. (remoe)
- Added Particle support to Ray. (mrdoob and jaycrossler)
- Improved Ray.intersectObject performance by checking boundingSphere first. (mrdoob)
- Added TrackballCamera. (egraether)
- Added repeat and offset properties to Texture. (mrdoob and alteredq)
- Cleaned up Vector2, Vector3 and Vector4. (egraether)
2011 04 24 - r40 (263.774 KB, gzip: 64.320 KB)
- Fixed Object3D.lookAt. (mrdoob)
- More and more Blender exporter goodness. (alteredq and mrdoob)
- Improved CollisionSystem. (drojdjou and alteredq)
- Fixes on WebGLRenderer. (empaempa)
- Added Trident object. (sroucheray)
- Added data object to Renderers for getting number of vertices/faces/callDraws from last render. (mrdoob)
- Fixed Projector handling Particles with hierarchies. (mrdoob)
2011 04 09 - r39 (249.048 KB, gzip: 61.020 KB)
- Improved WebGLRenderer program cache. (alteredq)
- Added support for pre-computed edges in loaders and exporters. (alteredq)
- Added Collisions classes. (drojdjou)
- Added Sprite object. (empaempa)
- Fixed *Loader issue where Workers were kept alive and next loads were delayed. (alteredq)
- Added THREE namespace to all the classes that missed it. (mrdoob)
2011 03 31 - r38 (225.442 KB, gzip: 55.908 KB)
- Added LensFlare light. (empaempa)
- Added ShadowVolume object (stencil shadows). (empaempa)
- Improved Blender Exporter plus added Scene support. (alteredq)
- Blender Importer for loading JSON files. (alteredq)
- Added load/complete callbacks to Loader (mrdoob)
- Minor WebGL blend mode clean up. (mrdoob)
- *Materials now extend Material (mrdoob)
- material.transparent define whether material is transparent or not (before we were guessing). (mrdoob)
- Added internal program cache to WebGLRenderer (reuse already available programs). (mrdoob)
2011 03 22 - r37 (208.495 KB, gzip: 51.376 KB)
- Changed JSON file format. (Re-exporting of models required) (alteredq and mrdoob)
- Updated Blender and 3DSMAX exporters for new format. (alteredq)
- Vertex colors are now per-face (alteredq)
- Geometry.uvs is now a multidimensional array (allowing infinite uv sets) (alteredq)
- CanvasRenderer renders Face4 again (without spliting to 2 Face3) (mrdoob)
- ParticleCircleMaterial > ParticleCanvasMaterial. Allowing injecting any canvas.context code! (mrdoob)
2011 03 14 - r36 (194.547 KB, gzip: 48.608 KB)
- Added 3DSMAX exporter. (alteredq)
- Fixed WebGLRenderer aspect ratio bug when scene had only one material. (mrdoob)
- Added sizeAttenuation property to ParticleBasicMaterial. (mrdoob)
- Added PathCamera. (alteredq)
- Fixed WebGLRenderer bug when Camera has a parent. CameraCamera.updateMatrix method. (empaempa)
- Fixed Camera.updateMatrix method and Object3D.updateMatrix. (mrdoob)
2011 03 06 - r35 (187.875 KB, gzip: 46.433 KB)
- Added methods translate, translateX, translateY, translateZ and lookAt methods to Object3D. (mrdoob)
- Added methods setViewport and setScissor to WebGLRenderer. (alteredq)
- Added support for non-po2 textures. (mrdoob and alteredq)
- Minor API clean up. (mrdoob)
2011 03 02 - r34 (186.045 KB, gzip: 45.953 KB)
- Now using camera.matrixWorldInverse instead of camera.matrixWorld for projecting. (empaempa and mrdoob)
- Camel cased properties and object json format (Re-exporting of models required) (alteredq)
- Added QuakeCamera for easy fly-bys (alteredq)
- Added LOD example (alteredq)
2011 02 26 - r33 (184.483 KB, gzip: 45.580 KB)
- Changed build setup (build/Three.js now also include extras) (mrdoob)
- Added ParticleSystem object to WebGLRenderer (alteredq)
- Added Line support to WebGLRenderer (alteredq)
- Added vertex colors support to WebGLRenderer (alteredq)
- Added Ribbon object. (alteredq)
- Added updateable textures support to WebGLRenderer (alteredq)
- Added Sound object and SoundRenderer. (empaempa)
- LOD, Bone, SkinnedMesh objects and hierarchy being developed. (empaempa)
- Added hierarchies examples (mrdoob)
2010 12 31 - r32 (89.301 KB, gzip: 21.351 KB)
- Scene now supports Fog and FogExp2. WebGLRenderer only right now. (alteredq)
- Added setClearColor( hex, opacity ) to WebGLRenderer and CanvasRenderer (alteredq & mrdoob)
- WebGLRenderer shader system refactored improving performance. (alteredq)
- Projector now does frustum culling of all the objects using their sphereBoundingBox. (thx errynp)
- material property changed to materials globaly.
2010 12 06 - r31 (79.479 KB, gzip: 18.788 KB)
- Minor Materials API change (mappings). (alteredq & mrdoob)
- Added Filters to WebGLRenderer
- python build.py --includes generates includes string
2010 11 30 - r30 (77.809 KB, gzip: 18.336 KB)
- Reflection and Refraction materials support in WebGLRenderer (alteredq)
- SmoothShading support on CanvasRenderer/MeshLambertMaterial
- MeshShaderMaterial for WebGLRenderer (alteredq)
- Removed RenderableFace4 from Projector/CanvasRenderer (maybe just temporary).
- Added extras folder with GeometryUtils, ImageUtils, SceneUtils and ShaderUtils (alteredq & mrdoob)
- Blender 2.5x Slim now the default exporter (old exporter removed).
2010 11 17 - r29 (69.563 KB)
- New materials API Still work in progress, but mostly there. (alteredq & mrdoob)
- Line clipping in CanvasRenderer (julianwa)
- Refactored CanvasRenderer and SVGRenderer. (mrdoob)
- Switched to Closure compiler.
2010 11 04 - r28 (62.802 KB)
- Loader class allows load geometry asynchronously at runtime. (alteredq)
- MeshPhongMaterial working with WebGLRenderer. (alteredq)
- Support for huge objects. Max 500k polys and counting. (alteredq)
- Projector.unprojectVector and Ray class to check intersections with faces (based on mindlapse work)
- Fixed Projector z-sorting (not as jumpy anymore).
- Fixed Orthographic projection (was y-inverted).
- Hmmm.. lib file size starting to get too big...
2010 10 28 - r25 (54.480 KB)
- WebGLRenderer now up to date with other renderers! (alteredq)
- .obj to .js python converter (alteredq)
- Blender 2.54 exporter
- Added MeshFaceMaterial (multipass per face)
- Reworked CanvasRenderer and SVGRenderer material handling
2010 10 06 - r18 (44.420 KB)
- Added PointLight
- CanvasRenderer and SVGRenderer basic lighting support (ColorStroke/ColorFill only)
- Renderer > Projector. CanvasRenderer, SVGRenderer and DOMRenderer do not extend anymore
- Added computeCentroids method to Geometry
2010 09 17 - r17 (39.487 KB)
- Added Light, AmbientLight and DirectionalLight (philogb)
- WebGLRenderer basic lighting support (philogb)
- Memory optimisations
2010 08 21 - r16 (35.592 KB)
- Workaround for Opera bug (clearRect not working with context with negative scale)
- Additional Matrix4 and Vector3 methods
2010 07 23 - r15 (32.440 KB)
- Using new object UV instead of Vector2 where it should be used
- Added Mesh.flipSided boolean (false by default)
- CanvasRenderer was handling UVs at 1,1 as bitmapWidth, bitmapHeight (instead of bitmapWidth - 1, bitmapHeight - 1)
- ParticleBitmapMaterial.offset added
- Fixed gap when rendering Face4 with MeshBitmapUVMappingMaterial
2010 07 17 - r14 (32.144 KB)
- Refactored CanvasRenderer (more duplicated code, but easier to handle)
- Face4 now supports MeshBitmapUVMappingMaterial
- Changed order of *StrokeMaterial parameters. Now it's color, opacity, lineWidth.
- BitmapUVMappingMaterial > MeshBitmapUVMappingMaterial
- ColorFillMaterial > MeshColorFillMaterial
- ColorStrokeMaterial > MeshColorStrokeMaterial
- FaceColorFillMaterial > MeshFaceColorFillMaterial
- FaceColorStrokeMaterial > MeshFaceColorStrokeMaterial
- ColorStrokeMaterial > LineColorMaterial
- Rectangle.instersects returned false with rectangles with 0px witdh or height
2010 07 12 - r13 (29.492 KB)
- Added ParticleCircleMaterial and ParticleBitmapMaterial
- Particle now use ParticleCircleMaterial instead of ColorFillMaterial
- Particle.size > Particle.scale.x and Particle.scale.y
- Particle.rotation.z for rotating the particle
- SVGRenderer currently out of sync
2010 07 07 - r12 (28.494 KB)
- First version of the WebGLRenderer (ColorFillMaterial and FaceColorFillMaterial by now)
- Matrix4.lookAt fix (CanvasRenderer and SVGRenderer now handle the -Y)
- Color now using 0-1 floats instead of 0-255 integers
2010 07 03 - r11 (23.541 KB)
- Blender 2.5 exporter (utils/export_threejs.py) now exports UV and normals (Thx kikko)
- Scene.add > Scene.addObject
- Enabled Scene.removeObject
2010 06 22 - r10 (23.959 KB)
- Changed Camera system. (Thx Paul Brunt)
- Object3D.overdraw = true to enable CanvasRenderer screen space point expansion hack.
2010 06 20 - r9 (23.753 KB)
- JSLinted.
- autoClear property for renderers.
- Removed SVG rgba() workaround for WebKit. (WebKit now supports it)
- Fixed matrix bug. (transformed objects outside the x axis would get infinitely tall :S)
2010 06 06 - r8 (23.496 KB)
- Moved UVs to Geometry.
- CanvasRenderer expands screen space points (workaround for antialias gaps).
- CanvasRenderer supports BitmapUVMappingMaterial.
2010 06 05 - r7 (22.387 KB)
- Added Line Object.
- Workaround for WebKit not supporting rgba() in SVG yet.
- No need to call updateMatrix(). Use .autoUpdateMatrix = false if needed. (Thx Gregory Athons).
2010 05 17 - r6 (21.003 KB)
- 2d clipping on CanvasRenderer and SVGRenderer
- clearRect optimisations on CanvasRenderer
2010 05 16 - r5 (19.026 KB)
- Removed Class.js dependency
- Added THREE namespace
- Camera.x -> Camera.position.x
- Camera.target.x > Camera.target.position.x
- ColorMaterial > ColorFillMaterial
- FaceColorMaterial > FaceColorFillMaterial
- Materials are now multipass (use array)
- Added ColorStrokeMaterial and FaceColorStrokeMaterial
- geometry.faces.a are now indexes instead of references
2010 04 26 - r4 (16.274 KB)
- SVGRenderer Particle rendering
- CanvasRenderer uses context.setTransform to avoid extra calculations
2010 04 24 - r3 (16.392 KB)
- Fixed incorrect rotation matrix transforms
- Added Plane and Cube primitives
2010 04 24 - r2 (15.724 KB)
2010 04 24 - r1 (15.25 KB)
HTML5 canvas! Hailed as a competitor to Flash, so much so that Apple is using it as an excuse not to allow the plug-in on iOS. But what everyone overlooks is that canvas is horribly slow and pretty much unusable on these devices. Is it even possible to create a Flash-like in-browser gaming experience on iPads and iPhones?
To answer that question I ran a JavaScript hack day for the finest programming minds at Plug-in Media where we had a single day to make a retro platform game : Infector!
So of course, with only a day to complete, it’s fairly unfinished but it works as a proof of concept and the performance is good, even on the iPad 1 (with the pre iOS update slower JavaScript engine).
It also works on Safari, Firefox and Chrome. Naturally with such a limited time scale we weren’t too worried about writing perfect code or cross browser issues. It was more to see what’s possible. And in theory, we should be able to get this working on older IE browsers (famous last words, I know
)
So what’s the answer to getting the performance on iOS? Forget HTML5 canvas, all the moving objects in this game are HTML div elements, we’re just moving them around by controlling CSS properties with JavaScript.
And of course, fantastic home made cookies.
But there’s one more trick that’s fairly well known amongst the JS community. With CSS3 came 3D transformations – you can now move your HTML stuff around in 3 dimensions, in browsers that support it of course. As soon as you set any of the 3D properties of an element in a webkit browser, it’s rendered using the GPU, and this makes a massive difference on mobile Safari.
Our game framerate doubled just by setting the “-webkit-transform:translateZ(0px)” property of our HTML elements.
The animated objects are HTML divs with a sprite sheet (like the picture above) for the background. We change which frame we’re on by simply offsetting the background position (again, adjusting CSS properties).
The background is a canvas, but we only render the non-moving platforms once at the start. HTML5 canvas is only slow when you draw stuff into it, when you’re not updating it, it’s no more CPU intensive than an ordinary image.
It was a great day working with a fun and very talented team, and I believe we’ve proved that we could provide a pretty good experience on iPads in the browser.
If you want to learn more about creative programming with JavaScript and HTML5, you could do worse than attend one of my courses! I’ll also be writing a tutorial for an upcoming .net magazine all about using these 3D CSS transformations.
Related posts:
This entry was posted in JS Library, Tutorials by Seb Lee-Delisle on Posted on September 23, 2011 by Seb Lee-Delisle.Google developer advocate Seth Ladd has quietly but consistently been producing an excellent series of articles on his blog about a JavaScript port of Box2D, the physics engine used in Angry Birds.
Box2D is written and maintained by its original author, Erin Catto. Erin, who works for Blizzard Entertainment, built the library for a physics tutorial at GDC back in March 2006.
Erin Catto is a bit of a legend really, and I’m astonished that he only has 500 twitter followers. Surely we can help with that, right? I wonder if Jer Thorp’s plea to Rovio to chuck him some cash ever worked? I know that they at least publicly acknowledged his contribution but only after he confronted them at GDC.
Why not show your support for Erin and Box2D by donating here? I just did.
Although there are at least 2 JavaScript ports of Box2D, Seth advocates the use of Box2D-Web version as it’s an automatic conversion of the well supported and maintained ActionScript Box2DFlash.
Here are handy links to all of Seth’s articles. I’ll try to keep it updated as he goes along.
- Box2D orientation for JavaScript
- Box2D JavaScript Example Walkthrough
- Box2D FPS and World Step Tests with JavaScript
- Box2D and Web workers for JavaScript developers
- Box2D, Update Rate, and Paint Rate for JavaScript
- Box2D, Web workers, and Page Visibility API
- Box2D, Web workers, Better performance
- Box2D and Polygons for JavaScript
- Box2D with Complex and Concave Objects, for JavaSc…
- Box2D and Joints for JavaScript
- Box2D, Impulse, and JavaScript
- Box2D, Collision, Damage, for JavaScript
Left-click to create an object. Right-click to show next example.
What's thisBox2DJS is a JavaScript port of Box2D Physics Engine. To tell the truth, this is converted from Box2DFlashAS3_1.4.3.1 in an automatic manner. (The reason why not Box2DFlashAS3_2.0.0 based is simply because I overlooked the renewal.)
How to use
- Download a zip file and extract it.
- Copy js/ and lib/ directories from the extracted directory to your app directory.
- Copy script tags in the header part of index.html in the extacted directory to your html file where you want to simulate physics.
Because this libray does not have a lazy-loading system now, you should load all classes before starting your simulation. To make things worse, each library has a bit complecated dependency each other so that loading libraries in wrong order may cause a fatal error. To avoid such a trouble, it is strongly recomended to copy the header part of this file or `index.html' including the downloaded zip file.
- Utilizing Box2D APIs, simulate the newton world as you like.
The Box2DJS APIs are completely same as those of Box2DFlashAS3. Please refer information about it.
Sample code
Create a world
var worldAABB = new b2AABB(); worldAABB.minVertex.Set(-1000, -1000); worldAABB.maxVertex.Set(1000, 1000); var gravity = new b2Vec2(0, 300); var doSleep = true; var world = new b2World(worldAABB, gravity, doSleep);Create a circle body
var circleSd = new b2CircleDef(); circleSd.density = 1.0; circleSd.radius = 20; circleSd.restitution = 1.0; circleSd.friction = 0; var circleBd = new b2BodyDef(); circleBd.AddShape(circleSd); circleBd.position.Set(x,y); var circleBody = world.CreateBody(circleBd);Create a revolute joint
var jointDef = new b2RevoluteJointDef(); jointDef.anchorPoint.Set(250, 200); jointDef.body1 = world.GetGroundBody(); jointDef.body2 = circleBody; world.CreateJoint(jointDef);Step a world
var timeStep = 1.0/60; var iteration = 1; world.Step(timeStep, iteration);Repeat stepping (for instance)
var ctx; var canvasWidth; var canvasHeight; function step(cnt) { world.Step(1.0/60, 1); ctx.clearRect(0, 0, canvasWidth, canvasHeight); drawWorld(world, ctx); // see demos/draw_world.js setTimeout('step(' + (cnt || 0) + ')', 10); } Event.observe(window, 'load', function() { setupWorld(world); // as you like ctx = $('canvas').getContext('2d'); var canvasElm = $('canvas'); canvasWidth = parseInt(canvasElm.width); canvasHeight = parseInt(canvasElm.height); step(); });DependenciesLinks Contact
- prototype.js
- IECanvas (when you use a canvas tag to display the result of your physics simulation)
If you have some requests or find any bugs, please tell me about them.
JigLibJS2 Javascript 3D Physics Engine![]()
JigLibJS2 is an automated port of the JigLibFlash 3D physics Engine. A similar port had already been made by JigLibJS. But I couldn't get the vehicle physics to work with it, which was what I wanted to achieve. So I decided to create a PHP script that automatically ports AS3 code to Javascript. JigLibJS2 is the result of that script (fresh port of JigLibFlash fp11). The code is available at github. I've just worked with the collision and car physics and have not (yet) tested if all the other physics elements work as well.
Demo:
These demo's use the three.js 3D rendering engine. Use arrow keys to drive, spacebar to brake and click to shoot things up in the air:
3D Vehicle Physics demo (canvas renderer) (Should work with Firefox 4, Chrome 10, Safari 5 and IE 9)
3D Vehicle Physics demo (WebGL renderer) (Should work with Firefox 4, Chrome 10 and Safari 5)Links:
JigLibJS2: JigLibJS2 at github
JigLibFlash: AS3 3D physics Engine
JigLib: Original physics Engine
JigLibJS: The original Javascript port
three.js: Javascript 3D Engine
glmatrix: High performance matrix and vector operations for WebGLI'm at:
bart at mediawave dot nl
Demo application base class and 3D physics demos based on ammo.js. Support for several scenegraphs including Three.js and SceneJS.
Authors
Stefan Hedman (schteppe@gmail.com)
Download
Get the source code on GitHub : schteppe/ammo.js-demos
See also
You may also be interested in cannon.js, a lightweight physics engine written in JavaScript.
Fancy making a game? Using Canvas? You do! Excellent!
Matthew Casperson has been incredibly busy writing this whole bunch of tutorials about developing a platformer using canvas. From keyboard input to parallax scrolling, from animations to resource loading – it’s all covered in his tutorials.
Check them out here:
Latest commit to the master branch
bin June 22, 2011 Set working directory to basedir. [hpeikemo] config June 22, 2011 Control rumpetrolld with capistrano. [hpeikemo] em May 15, 2012 Updated bundles. [hpeikemo] public March 09, 2012 Fixed CSS for ALL modern browsers. — Please do not code for webkit on… [Pinapple] scripts October 14, 2010 chmod launchloop [hpeikemo] tests October 18, 2010 Proper behavior in EM::Twitter.verifyRequest [hpeikemo] .gitignore June 22, 2011 exclude data dir from repository. [hpeikemo] Capfile June 22, 2011 capified [hpeikemo] Gemfile June 11, 2011 Added deamon controller, bundler. [hpeikemo] Gemfile.lock May 15, 2012 Updated bundles. [hpeikemo] README October 19, 2010 Minor tweaks, updated README. [hpeikemo] README
A massive multiplayer experiement done in HTML5/Canvas/JS and magical websockets. Rumpetroll is norwegian, but probably not what you think
Firefox, Opera, Chrome or other HTML5 compliant browser required. This site requires a modern browser for graphics and sound effects. A compliant browser can be downloaded from any of the following sites: Firefox OperaChrome Galactic Plunder
This game, written in JavaScript using the HTML5 Canvas and Audio objects, was developed as a proof of concept that a shooter-style game can be coded without the use of any Flash.
The first level of the game is fully implemented, and the second is in development.
More Information