// TECH //

How Clash of Stars keeps hundreds of ships in sync

Two browsers, one match, and not a single ship position on the wire. Every number below was read out of the build that is live on BlitzBrawl right now.

Nobody is in charge

Most online games run an authoritative server: one machine owns the truth and every player asks it what happened. Clash of Stars has no such machine. The only server in the gameplay loop is a relay that takes a packet from one player and pushes it to the others, and never opens the payload. To that relay, a fleet order and a keep-alive look the same.

So the match exists in two, three or four places at once, one copy per browser, and no master copy corrects them. They agree because each computes the same thing from the same input, and because the engine works to prove they still do.

Two browsers, one clock

The simulation runs at a fixed 20 ticks a second, so one tick is exactly 50 ms of game time. Ticks are not tied to your frame rate: tick k is due at a fixed offset from the match's start instant, and each frame the engine simulates every tick that has come due. If your device stalls, catch-up is capped at 250 ms per frame and the surplus is dropped rather than repaid, so a slow phone loses game time instead of falling behind forever.

Two devices whose clocks disagree run at different tick numbers at the same instant, which makes every incoming order look late. During the 3-2-1 countdown each client pings its peers every 120 ms, corrects for round-trip time, and shifts its start instant onto the median of the group while the tick counter is pinned at zero, so nothing jumps. After that it nudges its tick interval by 2 percent, or 8 percent when badly out of line.

What actually travels

Ship positions never go on the wire. Orders do. A send is one binary packet: an opcode byte, your slot, a sequence number, the tick it should run on, the target planet, the percentage, and the source planets at a byte each. Sending from one planet is 12 bytes. Selecting all 28 planets of the largest map and firing from every one is 39 bytes, and that single packet can put more than a thousand ships in the air.

Seven more opcodes cover the rest: two ways to re-aim a swarm in flight, one that carries surrender or a slot changing hands, and four for ping, pong, acknowledgement and the state hash. Commands are retransmitted every 250 ms until every live peer acknowledges them, and receivers discard repeats by sender and sequence number, so a duplicate is free and a lost packet costs a retry.

Your own orders wait a quarter of a second

The tick a command runs on is stamped by the sender, 5 ticks into the future, 250 ms, and rides inside the packet. Receivers never recalculate it: an order runs on that exact tick everywhere, or it does not run on time anywhere. The delay is fixed rather than tuned to your ping, so a command lands on the same tick for everyone. Solo play uses zero delay through the identical code path: single player is multiplayer without the transport.

When an order lands late, the match rewinds

Every 4 ticks the engine stores a full copy of the game state and keeps the last 64, which is 256 ticks, roughly 12.8 seconds of rewindable history. An order arriving with its execution tick already in the past is never dropped. The engine files it into the command log, restores the newest copy at or before that tick, and re-simulates forward, replaying every logged command in one global order: execution tick, then slot, then sequence number.

Replays run muted with game events suppressed, so a capture you already watched does not announce itself twice. The most important test in that game's suite asserts that a rewind and replay land on a state bit-identical to the run that never rewound.

Hundreds of ships, each computed on every machine

There is no fleet object here. Every ship is its own entity with its own position, and the swarm shape falls out of three rules run once per tick: pursue the target planet's centre, shove apart overlapping pairs, slide around any planet that is not the destination. Because the shape emerges from the simulation rather than the renderer, every client draws the identical swarm without spending a byte on it.

A four-player match typically peaks between two and five hundred ships in the air at once, and holding most of a large map for a minute lets one send launch over 1,600 at once. The simulation sets no ceiling; the renderer stops drawing past 4,096 ships in a frame, and the game's own performance audit uses about 1,170 as its worst case.

That reproduces only under hard rules: no random numbers the engine does not seed and carry in the state, no trigonometry anywhere in it, every list walked in ascending id order.

A 32-bit number that catches a lie

Every 40 ticks, 2 seconds, each client folds its whole simulation state into one 32-bit hash and broadcasts it: planet owners and garrisons, every ship's id, owner, target and the exact bit pattern of its coordinates, the player flags, the random-number state. Folding raw float bits rather than rounded values means a divergence in the last decimal place is caught rather than smoothed over. The check costs 10 bytes.

Mismatches are treated with suspicion, because most are not real. A rewind rewrites hashes a peer already broadcast, so corrected values are re-sent in three rounds, receivers keep the newest value per tick, and a comparison waits 8 ticks before counting. A mismatch is believed only once it survives two evaluations 24 ticks apart. A genuinely identical match must never report a divergence, and that too is a test.

When a client really has drifted

A confirmed divergence triggers convergence rather than an argument. Every affected client except the lowest-numbered live player asks for a resync; that player answers with a serialized snapshot plus the command tail to catch up. The requester adopts it, resumes its sequence numbering above the high-water mark, and fast-forwards. If the odd one out is the lowest slot, everybody adopts its state anyway: convergence beats correctness, because the match is identical again either way.

A snapshot nobody asked for is ignored, and every payload is shape-checked first. The same queue carries the mundane cases: a player silent for eight seconds is handed to the bot AI by one ordered command, so every machine does it on the same tick, and a returning player pulls a snapshot and claims the slot back.

None of this needs a server that understands the game. It needs a shared clock, a strict order for commands, a cheap way to rewind, and one small number that catches a machine drifting out of step.

▶ Play Clash of Stars
Read next: From click to live match in seconds How Zone Command turns a map into a war economy