Why Browser Games Freeze in Background Tabs
A few years ago I looked after the backend for a browser card game with a few thousand players online at peak. Our disconnect rate looked too high, and the client team swore the network code was fine.

A few years ago I looked after the backend for a browser card game with a few thousand players online at peak. Our disconnect rate looked too high, and the client team swore the network code was fine. When I finally matched server logs against the client's visibility events, about 17 percent of the disconnects came from players who had simply switched to another tab for a few minutes. Nothing was wrong with their connection. The browser had decided our game was not important while it was hidden, and our heartbeat stopped beating.
Browsers throttle background tabs on purpose, to save battery and CPU, and they have become steadily stricter about it. For a normal web page that is invisible. For a real time game it means timers drift, the game loop stops, and the server may conclude the player has left. You cannot switch this behaviour off, but you can design around it, and most of the work is on the side that people forget: the server.
What the browser actually does to a hidden tab
The rules differ by browser and change over time, so treat these as the behaviour I have measured in recent Chrome and Firefox, not a specification. Safari on iOS is stricter still and may freeze a hidden page entirely.
| Mechanism | Visible tab | Hidden tab | Hidden for over 5 minutes (Chrome) |
|---|---|---|---|
| requestAnimationFrame | Every display frame | Paused completely | Paused completely |
| setTimeout and setInterval | As scheduled | At most about once per second | Chained timers may wake only once per minute |
| WebSocket messages | Delivered | Delivered | Delivered |
| Timers in a dedicated Web Worker | As scheduled | Lightly throttled, if at all | Not subject to the per minute limit in my tests |
The per minute row is Chrome's "intensive throttling", introduced in Chrome 88. It applies when a page has been hidden for five minutes, has been silent for at least thirty seconds, and the timer is part of a chain of nested timeouts. A game that plays audio, even quietly, usually avoids it, which is one reason some browser games keep a near silent loop running. I would not rely on that trick, since browsers are free to change the conditions.
Why games break in three different ways
The heartbeat goes quiet
Our card game sent a heartbeat from the client every five seconds using setInterval, and the server dropped any session silent for thirty. In a hidden tab, the interval slowed to once per second at worst, which was fine. After five minutes hidden, intensive throttling pushed it to once per minute, and the server dropped the player well before the next beat. That matched the 17 percent almost exactly.
The loop stops, the messages do not
Because requestAnimationFrame pauses, any game whose logic runs inside the render loop stops processing state. WebSocket messages, however, keep arriving and queue up. When the player returns, a naive client tries to apply two minutes of updates in a single frame. You see this as a sudden fast forward, or a freeze followed by a jump, and in physics heavy games as objects flying through walls.
Time measured in ticks, not clock time
Games that count frames to measure time, "60 ticks equals one second", lose all sense of time while hidden, because no ticks happen. A cooldown that should have finished during a three minute absence is still running when the player comes back.
The fix is mostly on the server
The common advice is to "keep the tab alive" on the client with workers, audio or clever timer tricks. Some of those work today. I prefer to assume the client will be throttled and make the server comfortable with that, because a design that tolerates sleep also tolerates flaky mobile networks, laptops that close and every future browser change.
For the card game, we changed three things. The server, not the client, now sends a ping every ten seconds, and the client answers from inside its WebSocket onmessage handler. Network events are not throttled the way timers are, so the reply goes out promptly even in a hidden tab. We raised the idle timeout to ninety seconds and added a "reconnect into the same seat" path for anyone who misses it. And the server marks players as "away" when the client reports a visibilitychange to hidden, so the other players at the table see an honest status instead of a frozen opponent. Disconnects caused by hidden tabs dropped to under 2 percent of the total within a fortnight.
Design for the tab being asleep. Then a tab that stays awake is a bonus, not a requirement.
Client changes that still matter
The client cannot avoid throttling, but it can come back gracefully. Listen for visibilitychange and record the time the page went hidden. When it becomes visible again, compare against performance.now() and decide: if the gap is short, apply queued updates in order across a few frames; if it is long, throw the queue away and ask the server for a fresh snapshot of the game state. Snapshots are cheaper than replaying minutes of deltas, and they avoid the fast forward effect completely.
Measure all game time with clock readings rather than counted frames, so cooldowns and timers stay honest across a pause. And if you have logic that genuinely must keep running while hidden, such as a turn timer the player can see when they return, a dedicated Web Worker is the most reliable place for it in my experience, with the caveat that I have mostly tested desktop browsers.
For the wider picture of keeping sessions up on bad connections, see how developers keep game sessions alive through flaky connections. The messaging side is covered in how WebSockets keep game chat flowing.
Something to check this week
Open your game, start a session, switch to another tab, and set a timer for six minutes. Come back and look at two things: whether the server still considers you connected, and what the first second after return looks like. Then search your client code for heartbeats sent from setInterval. If you find one, move the responsibility to the server and answer pings from the message handler. More notes on keeping game servers calm live in our back-end section.
More in Games
Games
How WebRTC Data Channels Power Real Time Online Games
Most guides to real time play on the web stop at the web socket, and for a chat box or a turn based board that is usually...
Games
Why Memory Leaks Slow Down Long Online Game Sessions
A browser game can feel perfect in a two minute demo and fall apart after an hour.
Games
How The Web Audio API Drives Sound In Online Games
Sound is the part of a browser game that players feel before they can explain it.