Real-time grid battle game where players compete to capture territory on a shared board. Every click claims a cell, and the change is instantly visible to all connected players.
The project is split into two independent services:
0xGrid/
├── frontend/ Next.js app (React 19, Tailwind CSS v4)
└── backend/ Bun WebSocket server (TypeScript)
The frontend and backend communicate exclusively over WebSockets. There is no REST API.
- User clicks a cell in the browser.
- The frontend sends a
CAPTURE_CELLmessage to the backend over WebSocket. - The backend validates the request, checks for power-ups (like
OVERCLOCK⚡ orGLITCH_REVEAL👁️), updates the in-memory grid state, and broadcasts aCELL_UPDATEDmessage to all connected clients. - Every connected browser receives the update and re-renders the affected cell, applying any glitch or cascade animations instantly.
| Layer | Technology | Purpose |
|---|---|---|
| Frontend | Next.js 16, React 19 | UI framework |
| Styling | Tailwind CSS v4 | Design system |
| Backend | Bun | WebSocket server runtime |
| Language | TypeScript | Type safety across both services |
| Real-time | Bun native WebSocket | Client-server communication |
| State (Phase 1) | In-memory Map | Grid state store |
| State (Phase 2) | Redis or Prisma | Persistent, shared grid state |
- Bun v1.0 or later installed on your system.
git clone https://github.com/silky-x0/0xGrid.git
cd 0xGridcd backend
bun install
bun run devThe WebSocket server starts on ws://localhost:8080.
To verify it is running, open a browser console and run:
const ws = new WebSocket("ws://localhost:8080");
ws.onopen = () => console.log("Connected");
ws.onmessage = (e) => console.log("Message:", JSON.parse(e.data));Before starting, if you want to point to a remote server, create .env in the frontend directory:
NEXT_PUBLIC_WSS_URL=wss://your-backend.urlcd frontend
bun install
bun run devThe app starts on http://localhost:3000.
All messages are JSON strings. The client and server communicate using the following message types.
| Type | Payload | Description |
|---|---|---|
HELLO |
{ userId?: string } |
Sends saved ID on connect to resume session |
CAPTURE_CELL |
{ cellId: string } |
Claims a cell on the grid |
| Type | Payload | Description |
|---|---|---|
HELLO |
{ id: string, color: string } |
Identifies the client and their color |
GRID_STATE |
Cell[] |
Full grid snapshot sent on connection |
CELL_UPDATED |
Cell |
Broadcast when any cell is captured |
ERROR |
{ message: string } |
Error message from the server |
type Cell = {
id: string; // Format: "{row}-{col}", e.g. "5-10"
ownerId: string | null; // UUID of the player, or null if neutral
color: string; // Hex color assigned to that player
timestamp: number; // Unix timestamp in milliseconds
powerUp?: "OVERCLOCK" | "GLITCH_REVEAL"; // Optional power-up modifier
};- Backend WebSocket server with Bun
- In-memory grid state with
Map - Per-client persistent identity mapping via Session Storage
- Broadcast on cell capture
- Frontend grid component with responsive panning/zooming
- Implement database integration (Prisma/PostgreSQL) for persistence
- Handle server restarts without losing grid state
- Establish strict cooldown system to prevent spam
- Global Leaderboard tracking top capturers
- Configurable User nicknames
- Minimap for large geographic grids
- Smooth CSS Animations, Glitches, and Power-ups
License: MIT