ETERNALCHESS.
A real-time multiplayer chess game powered by WebSockets. Server validates moves, broadcasts state to all connected players instantly. Persistent. Live. No page reloads.
How to Play
This is a shared, persistent chess game. There is one board, and everyone in the world plays on it together. The game continues even after you close your browser.
Click a piece to select it, then click a destination square to move. You can also type moves in standard notation (e.g. e4, Nf3, O-O) in the input box below the board.
When 1-2 people are online, anyone can make a move at any time.
When 3 or more people are online, a queue system activates. Each person makes one move, then goes to the back of the line. The page will notify you when it's your turn.
Your queue position is shown above the board.
The WebSocket Pipeline
Every chess move flows through a persistent WebSocket connection. The server validates, updates state, and broadcasts to all connected clients in real time.
// Client → Server (move)
socket.send(JSON.stringify({
"move": "e4"
}));
// Server → All Clients (broadcast)
{
"type": "move",
"move": "e4",
"fen": "rnbqkbnr/pppppppp/8/...",
"turn": "b"
} Implementation
from fastapi import FastAPI, WebSocket
import chess, json
app = FastAPI()
board = chess.Board()
clients: list[WebSocket] = []
@app.websocket("/ws")
async def ws(socket: WebSocket):
await socket.accept()
clients.append(socket)
# Send current state
await socket.send_json({
"type": "init",
"fen": board.fen()
})
async for data in socket.iter_json():
move = board.parse_san(data["move"])
board.push(move)
for c in clients:
await c.send_json({
"type": "move",
"fen": board.fen()
}) const socket = new WebSocket(
'ws://{YOUR_SERVER}:8000/ws'
);
socket.onopen = () => {
console.log('Connected');
};
socket.onmessage = (event) => {
const data = JSON.parse(event.data);
if (data.type === 'init') {
board.load(data.fen);
renderBoard();
}
if (data.type === 'move') {
board.load(data.fen);
renderBoard(); // all viewers
}
};
// Send a move
socket.send(JSON.stringify({
"move": "e4"
}));