Architecture Overview

An interactive look at the components of a scalable, privacy-focused typing game. This section provides a high-level map of the system architecture. Click on any component below to explore its role and the technology behind it.

1. Frontend Client

HTML, CSS, Vanilla JS

↑ WebSocket ↓

2. Backend Server

Node.js, Socket.IO

3. Real-Time Data Store

Redis

4. Persistent Data Store

PostgreSQL

Frontend Client Details

The client-side application is built with standard web technologies. Its primary jobs are rendering the game, handling user input, and communicating with the backend via WebSockets. To protect privacy, it generates a unique UUID on first visit and stores it in `localStorage`. This UUID, along with the user's average WPM (their League Score), is used for matchmaking without requiring a login. All game history is stored locally.

Backend Server Details

A Node.js server using the Socket.IO library manages all real-time communication. It is designed to be stateless, meaning it doesn't store any game session data itself. This allows for horizontal scaling behind a load balancer to handle up to 10,000 concurrent users. Its main tasks are matchmaking, creating game rooms, and broadcasting player progress updates to others in the same room.

Real-Time Data Store Details

Redis serves as the central, high-speed memory for all active game data. This includes player progress, the status of game rooms, and the matchmaking queues. By storing this ephemeral state in Redis, any backend server instance can handle any player's request, making the entire system resilient and scalable. If a server instance fails, the user can reconnect to another and the game state is not lost.

Persistent Data Store Details

A PostgreSQL database stores static, long-term data that doesn't change frequently. This includes the library of typing texts used for the races, the definitions of the different leagues (e.g., Bronze: 0-40 WPM), and other configuration data. The backend servers query this database when creating a new game room to fetch a random text.


The Player's Journey

From landing on the page to finishing a race, the player's experience is managed by a seamless flow of events. This section breaks down the client-server interaction sequence. Click on any step to see a detailed explanation.

Step 1: Connection & Identification

The user loads the page. The client-side script checks `localStorage` for a UUID. If one doesn't exist, it's generated and saved. The client then establishes a WebSocket connection to the server, sending its UUID and locally stored average WPM.

Step 2: Matchmaking & Room Assignment

The backend receives the connection request and uses the player's WPM to place them into a skill-appropriate queue in Redis (e.g., `matchmaking:silver`). A matchmaking service continuously monitors these queues. Once a queue has enough players, a game room is created, and the assigned players are notified to join it.

Step 3: Real-Time Progress Reporting

The race begins. As the user types, the client calculates their progress percentage and current WPM. Every few characters, it sends a lightweight `progress_update` event to the server. The server then broadcasts this update to all other players in the same game room, allowing their car to move on everyone's screen.

Step 4: Race Finish & League Update

When a player completes the text, the client sends a final event. The server verifies the finish and declares a winner, ending the game for that room. If the player won, the server's `game_end` event includes instructions for the client to store a slightly boosted WPM value locally, which will be used for matchmaking in their next game.

The Backend Engine

The server-side logic is designed for high performance and scalability. This section visualizes the live matchmaking process, a core function of the backend. Players are sorted into queues based on skill to ensure fair races.

Live Matchmaking Queues

This chart simulates players waiting for a game. Click the button to add a new player, who will be randomly placed into a league queue based on their skill.


League Progression Simulator

The game uses a unique, stateless method for league advancement to protect user privacy. A player's matchmaking rating is stored locally and adjusted after each race. Interact with the simulator below to see how winning or losing affects a player's standing for their next game.

Player's Local State

Stored in `localStorage`

Matchmaking WPM Value

40

Next Game Placement

Determined by WPM

Matchmaking Queue

Bronze