‹ Back to Blog

Quantris Is a Fighting Game

quantrismultiplayerdev-lognetworking

Every multiplayer game has to deal with the same fundamental problem: the speed of light isn’t fast enough. Two players on opposite sides of the country have at least 40 milliseconds of round-trip latency between them, and in practice it’s usually more. For a game running at 60 frames per second, that’s two or three frames where neither player knows what the other one is doing.

Different genres have found different ways to deal with this. RTS games use deterministic lockstep, where both players send their inputs and nobody advances until everyone has confirmed. The Age of Empires team famously shipped 1500-unit battles over 28.8k modems with this approach, documented in Mark Terrano’s 1500 Archers on a 28.8 postmortem. It works, but it means your input lag is always at least as bad as the worst connection in the game. First-person shooters use client-side prediction with server reconciliation, letting you move immediately while the server sorts out the truth (Valve’s Source multiplayer networking docs are the canonical reference here). Fighting games use rollback netcode, predicting your opponent’s input, running the game at full speed, and rewinding to fix things if the prediction was wrong. Tony Cannon’s GGPO popularized this approach, and Infil’s Fighting Game Netcode primer is the clearest explanation of why it feels so much better than the alternatives.

Puzzle games mostly don’t have to think about it at all. In games like TETR.IO or Tetris 99, each player’s board is fully independent. You send garbage lines to your opponent when you clear, they queue it up and apply it later. The boards never share state, so there’s nothing to synchronize frame by frame. Hard Drop’s wiki has a good writeup on garbage and attack mechanics across the various competitive Tetris implementations.

Quantum Chess didn’t have to think about it much either. The original Steam version (and the current web rebuild’s first pass at online multiplayer) used a realtime database for state sync. One player makes a move, the game state updates in the database, the other player sees it. Turn-based games can get away with this because there’s a natural boundary between actions. You don’t need frame-perfect synchronization when players are taking turns. The Quantum Chess rebuild now has proper WebSocket connections for lower latency, but even there, the turn structure means the networking is relatively forgiving.

Quantris is a different problem entirely. It’s real-time, both players are acting simultaneously, and the quantum mechanics create shared state between the two boards. I spent months trying to make the multiplayer feel good, and the answer may be in a genre I wasn’t looking at. Not puzzle games. Fighting games.

What multiplayer looks like

Quantris has two multiplayer modes right now: co-op and head-to-head.

In co-op, two players share a game. Both towers are running simultaneously, you draw from the same hold queue, and your scores are combined. You’re working together, and the quantum mechanics are the shared challenge. Cross-tower entanglement means your superposition blocks can become entangled with your partner’s blocks, so a measurement on your board can collapse blocks on theirs.

In head-to-head, the towers are independent in terms of scoring and piece queues, but entanglement still crosses the gap. When you measure an entangled group and it collapses blocks on your opponent’s board, you steal the scoring credit. It’s an offensive weapon. You’re trying to build up entangled groups that span both boards, then collapse them at the worst possible moment for your opponent.

Both modes depend on the same underlying mechanic: cross-tower entanglement.

How cross-tower entanglement works

Entanglement in Quantris is group-based. When you apply an entanglement block, it fires a line of sight entangling beam (CNOT). The target blocks and the control blocks all get linked into an entanglement group. These groups can span across both player boards.

When anyone measures any block in an entangled group, the entire group collapses. Every block in the group resolves to either EXISTS (solidifies, becomes a normal block) or NOT_EXISTS (removed entirely, and the tower compacts to fill the gap). The outcomes are determined by the quantum state of the group, computed by Quantum Forge running as a WASM module.

The key game rule: a line can only clear if every block in that row has been measured and exists, or the entire line is decimated; i.e. if all blocks in a line are measured to not exist, that counts as a line clear (you actually get more points this way). Superposition blocks prevent clears. So measurement is both destructive (blocks can vanish) and constructive (blocks become clearable). And when a group spans both boards, that measurement fires on both boards simultaneously.

This is where networking gets hard.

The first attempt: Supabase Realtime

The initial online multiplayer used Supabase Realtime channels. It was the fastest path to “two people playing at the same time over the internet.” Supabase gave us pub/sub out of the box, and for early testing it worked fine.

But Supabase Realtime is built for application state sync, not 60fps game state. The latency was inconsistent, message ordering wasn’t guaranteed in the way we needed, and we were fighting the abstraction instead of building on it. For a game where a single frame of desync can cascade into completely different board states, we needed tighter control over the transport.

We migrated to a custom WebSocket relay server. Raw WebSocket connections, JSON messages, room codes, pub/sub. This gave us the control we needed to build proper frame synchronization on top.

Lockstep: correct but sluggish

The first real sync model was deterministic lockstep. Both players run both game engines locally. Every frame, you send your inputs. Neither engine advances until both players have confirmed their inputs for that frame. This guarantees perfect synchronization: both boards are always in exactly the same state on both clients.

The problem is feel. Your piece doesn’t move until your opponent’s client has confirmed that frame, which means every input you make eats a full network round trip plus a buffer to absorb jitter. Single player input lag is one frame, basically imperceptible. Lockstep input lag is several times that, and it scales directly with how bad your opponent’s connection is. For a game where you’re trying to precisely place pieces under time pressure, even a modest connection feels like playing underwater.

Competitive Tetris players talk about ARR=0 (instant auto-repeat) as a baseline expectation. Our lockstep couldn’t even support that, because the standalone input tracker we built for the delay compensation couldn’t generate ARR=0 moves without engine collision feedback.

I looked at how the games we were being compared to handle this.

How puzzle games normally do it

TETR.IO, Tetris 99, Puyo Puyo Champions, and basically every other competitive falling-block game use what I’d call the independent-boards model. Each player’s board runs locally at full speed with zero input delay. The boards are completely decoupled. When you clear lines, you send an attack event (garbage lines) to your opponent. The opponent queues that garbage and it gets applied the next time they lock a piece without clearing.

This works because garbage is additive (new rows pushed onto the bottom), queueable (applied at a natural safe boundary), and doesn’t create shared state. Each board is fully independent. The opponent’s board display is just a periodic visual update, maybe 50 to 150 milliseconds behind. Nobody cares because you’re watching your own board 95% of the time.

This model completely falls apart for Quantris.

Why Quantris breaks the model

Cross-tower entanglement creates shared mutable state. Both players are interacting with blocks that exist on both boards simultaneously. And measurement collapse is the opposite of garbage in every way that matters.

It’s subtractive, not additive. Collapse removes blocks from the opponent’s board. The opponent might be actively building on those blocks right now.

It’s not queueable. Garbage gets applied at the next piece lock, a natural safe moment. Measurement collapse affects blocks mid-play. You can’t wait for a safe moment because the opponent is continuously interacting with the entangled blocks.

It creates shared mutable state. Entangled groups live on both boards. Both players interact with the same logical blocks. The boards are fundamentally not independent.

Here’s the scenario that kept me up at night:

  1. Both boards have cross-tower entangled superposition blocks in rows 8 through 10.
  2. Frame 34: Player 1 deploys a measurement. The entangled group collapses to NOT_EXISTS. Blocks removed from both boards, tower compacts. Player 1 sees this instantly.
  3. Frame 36: Player 2 hasn’t received the collapse info yet. They hard drop a piece onto those entangled blocks. The piece locks. Entanglement-push auto-entangles the new piece with blocks below. Player 2 starts manipulating their next piece.
  4. Frame 42: The collapse info arrives at Player 2. The blocks they built on don’t exist. The entanglement that happened at frame 36 was against ghost blocks. Their current piece position might be invalid after tower compaction.

This is irreconcilable without either lockstep or rollback. No queue can absorb it. The blocks Player 2 built on aren’t there.

Quantris is a fighting game

When I started looking at this problem through a different lens, the answer was obvious. Quantris doesn’t have the same networking constraints as a puzzle game. It has the same constraints as a fighting game.

In a fighting game, one player’s action on frame N directly and immediately changes the state the other player is interacting with. A punch lands and the opponent enters hitstun. Both players need to agree on the game state at every frame, and there’s no natural boundary where you can queue up state changes.

The fighting game community solved this problem years ago with rollback netcode, most famously through GGPO. The idea is straightforward: don’t wait for your opponent’s input. Predict it (usually by assuming they’ll do the same thing they did last frame), advance your local game at full speed, and if the prediction was wrong when the real input arrives, roll back to the last confirmed state, replay with the correct inputs, and fast-forward to the present.

The insight for Quantris is that cross-tower measurement is the equivalent of a hit in a fighting game. It’s a deliberate player action that immediately affects the other player’s board state. And like a hit, you can’t queue it. You need to resolve it at the frame it happened.

Rollback for Quantris

The rollback architecture works like this:

Both boards run independently at full speed. Zero input delay. Your piece responds the frame you press the button, just like single player. The opponent’s board display is updated via periodic snapshots, running slightly behind.

Every frame, the engine saves a snapshot to a ring buffer (last 20 frames). A Quantris board is compact: the 10x20 grid, piece state, quantum state, entanglement groups, RNG position, all of it fits in a couple kilobytes. Saving 20 snapshots costs about 40KB. Re-simulating 15 frames takes less than a millisecond. The performance cost is trivial.

When a cross-tower measurement event arrives from your opponent, tagged with the frame it occurred:

  1. Load the snapshot from that frame
  2. Apply the measurement collapse (blocks removed or solidified)
  3. Re-simulate all frames from then to now, replaying your stored local inputs
  4. Replace the current board state with the corrected result

The piece you hard-dropped on frame 36 onto blocks that don’t exist? After rollback and re-simulation, it falls through the gap where those blocks were and lands lower on the board. The entanglement-push that happened against ghost blocks never happened. The re-simulation handles all of this deterministically.

And here’s the part that makes this work so well for a quantum game: the visual cover is built in. A measurement collapse is already a dramatic event. Blocks shimmer, decimation animations play, superposition states resolve. The visual transition from the predicted state to the corrected state gets folded into the collapse animation. Players see “quantum measurement happened” instead of “the game corrected a prediction error.” The quantum theme provides perfect camouflage for the rollback.

Mispredictions are also rare. Most frames, no measurement happens. The prediction (nothing changes) is correct well over 99% of the time. Rollback only fires when someone actually deploys an observe block, which is a deliberate, infrequent action.

The quantum hardware question

Everything above runs on Quantum Forge’s deterministic WASM simulator. The quantum mechanics are real (proper state vectors, density matrices, entanglement, measurement), but the execution is classical. Measurement outcomes are deterministic given the same RNG seed, which is what makes replays and rollback possible.

What happens if the game runs on actual quantum hardware?

Real quantum computers introduce genuine randomness. Measurement outcomes are truly nondeterministic. Reproducing post-measurement quantum state statistics requires techniques like post selection. You also get decoherence (qubits naturally lose their quantum state over time) and gate errors (operations aren’t perfect). The game already simulates decoherence as a mechanic in flux mode, but on real hardware it wouldn’t be a design choice. It would be physics.

The interesting thing for multiplayer is that entanglement on real hardware is physically real. If two qubits are entangled and one player measures their qubit, the other qubit’s state information is instantly determined to be consistent, regardless of distance. This is actual quantum mechanics, not something we’re simulating. The physics of the hardware handles the state synchronization.

But there’s a catch. The collapse happens at the quantum level, but both players still need to know it happened at the game level. Player 2 needs to know that player 1 measured, to recreate that on their end. And real hardware introduces a new problem: what if a block’s qubit decoheres between the time it was placed and the time it’s measured? The block might collapse to a different state than either player expected. What if an opponent’s measurement collapses blocks on your board that you’ve already built on top of, and the collapse outcome is different from what the simulator would have predicted?

This might require the engine to handle retroactive tower modifications. Not just “blocks were removed, compact down” but “these blocks changed state, and here’s a piece that was placed assuming the old state, and the tower needs to be made consistent.” It’s a different flavor of the same rollback problem, but now the source of truth is hardware rather than a deterministic simulator. The engine might need to accept that its tower state can be externally invalidated at any time, not just when an observe block is deployed, but whenever the hardware decides a qubit has decohered.

That’s a research problem I don’t have an answer for yet. But the rollback architecture gives us a possible path. The ability to snapshot state, accept external corrections, and re-simulate forward is exactly what you’d need to stay consistent with nondeterministic hardware. The fighting game framing wasn’t just a good solution for networking. It might be the right abstraction for quantum hardware integration too.

Where things stand

Rollback netcode is implemented and is the default for online multiplayer. State serialization, snapshot ring buffers, re-simulation, visual transitions, all of it is in and working. The quantum collapse animation provides good cover for the corrections, and mispredictions are infrequent enough that most players won’t notice them at all.

The lockstep implementation is still there as a fallback, but rollback is what ships. Zero input delay for normal gameplay, and the quantum measurement corrections feel like part of the game rather than lag compensation.

It took a while to get here, and the answer came from an unexpected direction. Quantris doesn’t play like a fighting game. But under the hood, it has more in common with Skullgirls than with Tetris 99. Cross-tower entanglement made the standard puzzle game networking model impossible, and fighting game architecture made it work.

Comments

Loading comments...