2026-05-06
Bichess is a Fog of War chess site I’m building. In Fog of War, each player sees only the squares their own pieces could legally move to. The implementation choice that matters is where in the stack that visibility is enforced.
chess.com offers Fog of War. The implementation sends opponent moves to the client and hides them with a UI overlay. Anyone inspecting WebSocket payloads in browser dev tools can recover hidden state in real time. The fog is CSS; the truth is on the wire.
Bichess enforces visibility at the protocol level. The server holds canonical state and computes a per-seat view via getPlayerView(state, color). The WebSocket sends each connected client only the PlayerView for their seat. No hidden field, no client-side filtering. Captured kings, opponent locations, last-move squares outside the visibility set. None of it travels until that client’s pieces can legally see it. Server payload tests assert the absence of hidden truth on every outbound message.
What this changes downstream:
- Postgame reveal is a server transition (the canonical board becomes broadcastable), not a CSS toggle.
- Replay can be served from either perspective without leaking, by replaying the recorded
PlayerViewstream rather than the canonical event log. - Any engine that connects to the protocol consumes the same bounded information a player gets.
When the contract is “the client must not see X,” the trust boundary is wherever the data physically stops travelling. UI-layer enforcement is a comment, not a constraint.