This project is a 3D networked tank game with physics that you can play in the browser, inspired by Wii Tanks.
I started off by setting up the rendering, which was fairly simple. I used three.js for this, as I have a lot of experience with it, and is a lot simpler that using WebGL.
Now that the scene was ready, it was time to do the networking. For this, I used socket.io. I could have used the builtin websocket libraries, but socket.io provides many helpful abstractions, like messaging, and also allows the connection to fallback to HTTP long-polling if websockets aren’t available. We use this connection between client and server to update the position of the local player and also other people in the game.
In order for the game to be fair, the server has to have authority over the players – meaning that the server is in full control of their position and rotation etc. If this wasn’t the case, players would be able to teleport by issuing network messages in the console – however, with server authority, the player would only be fooling themselves, as the server will maintain its version of the player that is synced to other clients.
Whenever the client makes an input, they send their new velocity to the server, which updates the model of the player on the server if the server deems the update to be trustworthy (e.g. the player is moving at a reasonable speed). I made the server broadcast player positions and velocities 10 times every second, and every time clients receive this update they snap all of the player locations to the server model and then extrapolate the player positions based off of the last input every frame. In order to make sure the extrapolated player moves by the same amount each frame, we multiply the velocity by the time between frames, also known as delta time. This means that in a set amount of time, the player will always travel the same distance, no matter how many frames are used.
Right now, the game was a little bland – players were simply cubes! To fix this, I made a tank model in blender, and imported it into three.js.

From there, it was time to implement walls and collisions. As it stands, the players rest on top of a floor, except they aren’t really resting on it – they just ignore gravity. I could have used distance tests for collisions, but then I had the idea of using a physics engine to do it for me. By doing it this way, I could also have complex objects, like moveable walls, and could make the bullets physical objects that can interact with the environment. This is the first real difference from Wii Tanks – being hit by a bullet does not end the game, you simply go flying instead.
For the physics library, I decided to go with ammo.js – a javascript port of the bullet physics engine .I found that ammo.js can be difficult at times however, as certain features are built with C++ in mind rather than JS (e.g. operator overloading, references). Because of this, I nearly switched to something like cannon.js or oimo.js half way through, but chose to stick with ammo I found it to be efficient and I already had some experience with it. Besides, by that point I had already written a large portion of the interface between it and three.js.
However, using a physics engine required some serious reworking of the networking code. The collection of articles at https://gafferongames.com/ was exceptionally useful however, and I learnt a lot about how networked physics games work properly. I recommend checking out the “Fix your timestep” and “State synchroinzation” articles. The system I ended up with in the end is that both the client and the server run a physics simulation, and the server sends the client information about where the objects should be every 200ms. This system still isn’t perfect (quite visibly so), possibly partly due to the server it runs on, so I might come back to this in the future to see if I can increase its performance.
At this point, I began to encounter an issue where the players were sometimes hit by the bullets they had just shot. To rectify this, I looked to disable the collisions for the tank and the new bullet – a feature that Bullet (talking about the engine this time!) happens to have. However, ammo.js did not – it was compiled from the version just before this feature was added. In the end, I had to recompile ammo.js from source using the next Bullet version, exposing the method in the IDL file. I had to modify/introduce a few other methods (breaking changes yippee!), but eventually did manage to get it to compile – feel free to contact me if you want this version.
As a network game, unfortunately the game is forced to run over TCP. This is ok, but not ideal – for network games, usually a different protocol like UDP is used. This is because if a packet is not sent correctly, no attempt to resend it is made. At first, this seems like a disadvantage, but the packet resending TCP provides can not only apparently slow down the packet speed but also can cause updates to arrive in an incorrect order. While we could fix this with an extra field on the network message for ordering, doing this would mean updates would have to wait for the last missing one to arrive first, further worsening the performance.

That is about everything! Most of the rest of the project was relatively simple to implement – the stuff I added without going into much detail about includes a few maps, emotes and tank classes. If you want to see it for yourself, the link is below
Hi John,
I enjoyed playing your game, glad you are taking an interest in programming. I used to work for your Dad many cycles ago.
Going to check out the rest of your projects 😎
Regards
Andrew