Air Hockey was one of the first real-time games we added to Doozles, and for a while it was also the one we got the most complaints about. Players described it in different ways, such as "laggy", "the puck goes through me" or "it stutters", but they were all describing the same few problems. This post explains what was going wrong and what we changed. If you have ever wondered why online action games are so much harder to build than turn-based ones, this is a good example.
How the first version worked
The original design was the textbook one for keeping a game fair: the server is the boss.
- Your phone sends the position of your finger to our server.
- The server runs the physics: it moves the puck, checks collisions with both strikers and the walls, and detects goals.
- Sixty times a second, it sends the full game state back to both players.
- Each phone draws what the server says.
This is fair, because nobody can cheat by editing their own puck. It is also great for turn-based games like Ludo or Connect Four, where a delay of a tenth of a second is invisible. For a fast physics game it has one big weakness: every hit has to travel to the server and back before you see it.
Problem 1: the round trip
Imagine the puck coming at your striker. You flick your finger and your striker moves instantly, because we draw your own striker locally. But the puck only reacts once the server has received your new position, run the collision and sent back the result. If that round trip takes 150 milliseconds, the puck visibly slides into your striker, keeps going for a few frames and then suddenly flies off. Your brain reads that as "lag", or "the puck went through me".
Distance makes it worse. A server on another continent can easily add 200 milliseconds or more.
Problem 2: bunched-up updates
We sent sixty updates a second, but the internet does not deliver packets at perfectly even intervals. Some arrive early, some late, some in pairs. Our drawing code animated the puck from the previous update to the latest one and then stopped. Whenever an update was late, the puck froze for a moment, then jumped. That is the stutter people saw.
Problem 3: too much data
Sixty full-state messages a second to every player is a lot, especially on mobile data. When the connection could not keep up, messages queued behind each other, so players saw the game further and further in the past until the queue drained.
Problem 4: inconsistent hits
The server calculated how fast your striker was moving from how far it jumped between two network messages. Because messages arrive unevenly, the same swipe could be measured as very slow or extremely fast. Sometimes a fast swipe moved the striker straight past the puck between two physics steps, and no collision happened at all.
What we changed
One physics engine, shared everywhere
We moved the table physics into a single module that runs identically on the server and in your browser. It uses a fixed time step and moves strikers smoothly across each step, which we call a swept collision, so a fast flick can no longer skip over the puck. We also removed a couple of old hacks that forced the puck in a particular direction after certain hits, which made some rebounds feel unnatural.
The player closest to the puck runs it
The biggest change is who simulates the puck. Now, whichever player has the puck on their half of the table runs the physics for it on their own device. Your hits react on the very next frame, with no round trip. When the puck crosses the centre line, control passes to the other player.
The server still keeps score and checks every update: the puck cannot move impossibly fast or teleport, and goals are decided on the server. We kept fairness while removing the delay from the moment that matters most, your hit.
Fewer, smarter updates
The server now sends a compact update thirty times a second instead of a full state sixty times a second. Updates are marked as volatile, so if your connection hiccups, stale messages are dropped instead of piling up.
A small buffer for smoothness
Your opponent's striker, and the puck while it is on their side, are drawn about a tenth of a second in the past using a small buffer of recent updates. That sounds strange, but it means we always have two real positions to animate between, so movement stays smooth even when packets arrive unevenly. You never notice the delay, because it only applies to things you are not touching.
Cheaper drawing
We now draw the static table once and reuse it, and we draw the canvas at your screen's real pixel density, so it looks sharp on phones without extra work every frame.
Two new ways to play with no network at all
Because the physics now runs in the browser, we could add two modes that do not need a server:
- Local split screen: put a phone or tablet flat between two people and play face to face, each using their own half of the screen.
- vs Bot: a computer opponent with easy, medium and hard levels.
Both have zero lag by definition, which makes them a great way to learn the angles before challenging a friend online.
What we learned
Turn-based games forgive a lot. Real-time games do not, and "make the server authoritative" is only half the answer. The other half is deciding which moments must feel instant, and designing the networking around them. If you find a rebound that still feels wrong, tell us at support@doozles.xyz. We read every message.