KeyFury is a real-time multiplayer typing game where players compete in typing challenges. Join rooms, chat with competitors, and test your typing speed against others with live statistics and post-game analytics. Also features a clean, distraction-free single-player mode.
- Multiplayer Mode: Create private rooms or join existing ones using a unique Room ID.
- Single Player Mode: Practice and improve your typing speed with configurable time limits (15, 30, 45, 60 seconds).
- Real-time Competition: See every player's WPM, accuracy, and progress bar update live during a match.
- Synchronized Gameplay: A server-managed countdown ensures all players in a room start the test simultaneously for a fair race.
- Live In-Game Chat: Communicate with other players in the room lobby before the game starts.
- Detailed Post-Game Analytics: After each match, view a detailed leaderboard and personal performance stats. Compare your WPM, accuracy, and progress over time against other players with interactive graphs powered by Recharts.
- Secure Authentication: User accounts are managed with Firebase Authentication, supporting both email/password and Google Sign-In.
- Modern & Responsive UI: A sleek, distraction-free interface built with React and Tailwind CSS.
The project is a monorepo with a separate client and server directory.
- Framework: React 18 (with Vite)
- Styling: Tailwind CSS
- Routing: React Router DOM
- Real-time Communication: Socket.IO Client
- Authentication: Firebase Authentication
- Data Fetching: Axios
- Data Visualization: Recharts
- State Management: React Context API, Custom Hooks (
useEngine,useSyncedTimer) - UI/UX: Lucide React (icons), React Toastify (notifications)
- Runtime: Node.js
- Framework: Express.js
- Database / In-Memory Store: Upstash Redis
- Real-time Communication: Socket.IO
- Environment Management:
dotenv
KeyFury is built on a robust, event-driven architecture designed for low-latency, real-time interactions.
-
Real-time Engine with Socket.IO: The core of the multiplayer experience is powered by Socket.IO. The server listens for events like
createRoom,joinRoom,startTest, andchatMessage, and efficiently broadcasts state updates to the relevant clients. -
State Management with Upstash Redis: The project leverages Upstash for a serverless Redis database, ensuring low-latency data access and simplifying management. It serves as the single source of truth for all ephemeral game data:
- Room & Player Data: Stored in Redis Hashes for quick read/write access. This includes live stats and the final
performanceHistoryfor each player. - Chat History: Stored in Redis Lists, capped to the last 50 messages to save memory.
- Session Management: Each socket connection is mapped to a user and room, with a TTL for automatic cleanup.
- Efficiency: Redis Pipelining is used to batch multiple commands into a single request, reducing network round-trip time and ensuring atomicity for complex operations.
- Room & Player Data: Stored in Redis Hashes for quick read/write access. This includes live stats and the final
-
Performance Optimization:
- Throttling: To prevent overwhelming the server with frequent updates, player stats (
WPM,progress) are sent from the client using a customuseThrottlehook. This limits the rate ofupdateStatsemits to one every 300ms. - Background Services: The server runs periodic cleanup tasks to validate active rooms and remove stale data, ensuring the application remains stable and performant over time.
- Throttling: To prevent overwhelming the server with frequent updates, player stats (
-
Hybrid Data Flow: The application uses a hybrid data flow model. Real-time game events and live stats are handled via WebSockets. After the test, the comprehensive results, including
performanceHistory, are fetched via a standard RESTful API endpoint (/api/results/:roomId). This approach leverages the strengths of both protocols: WebSockets for low-latency live updates and REST for retrieving a final, complete dataset. -
Game Synchronization: The
useSyncedTimercustom hook on the frontend works in tandem with the server to keep all players' timers perfectly aligned. The server broadcasts sync events every 2 seconds, and the client-side hook intelligently corrects for network latency, providing a smooth and fair countdown experience.
Follow these instructions to set up and run the project locally.
- Node.js (v18.x or later)
- npm or yarn
- A Redis instance. The project is configured for Upstash Redis, but a local instance or another cloud provider will also work.
git clone https://github.com/Akarsh1412/KeyFury
cd KeyFury
-
Navigate to the server directory
cd server -
Install dependencies
npm install -
Create a
.envfile. You can get yourREDIS_URLandREDIS_TOKENfrom your Upstash database console.server/.envPORT=5000 CLIENT_URL=http://localhost:5173 REDIS_URL=YOUR_UPSTASH_REDIS_URL REDIS_TOKEN=YOUR_UPSTASH_REDIS_TOKEN -
Finally, start the backend server:
npm run dev
-
Navigate to the client directory from the root
cd client -
Install dependencies
npm install -
Set up a new project on the Firebase Console. Enable Email/Password and Google authentication methods. Then, get your project's configuration keys and add them to a new
.envfile.client/.envVITE_BACKEND_URL="http://localhost:5000" VITE_FIREBASE_API_KEY=YOUR_FIREBASE_API_KEY VITE_FIREBASE_AUTH_DOMAIN=YOUR_FIREBASE_AUTH_DOMAIN VITE_FIREBASE_PROJECT_ID=YOUR_FIREBASE_PROJECT_ID VITE_FIREBASE_STORGE_BUCKET=YOUR_FIREBASE_STORAGE_BUCKET VITE_FIREBASE_MESSAGING_SENDER_ID=YOUR_FIREBASE_MESSAGING_SENDER_ID VITE_FIREBASE_APP_ID=YOUR_FIREBASE_APP_ID VITE_FIREBASE_MEASUREMENT_ID=YOUR_FIREBASE_MEASUREMENT_ID -
Finally, start the frontend development server:
npm run dev
You can now access the application at http://localhost:5173.
createRoom: Initializes a new room with the creator as the leader.joinRoom: Allows a user to join an existing room.leaveRoom: Removes a player from a room and handles leader reassignment if necessary.chatMessage: Broadcasts a message to all players in a room.startTest: Initiated by the room leader; starts the countdown for all players.updateStats: Sent from a client to the server to update their live game statistics.liveStats: Broadcast from the server to all clients with updated stats for all players.testEnded: Sent when the game is over (time up or all players finished).
GET /api/results/:roomId: Fetches the final results and performance history for all players in a completed game.
Contributions are welcome! If you have suggestions or want to report a bug, please feel free to open an issue on the GitHub repository.
- Fork the Project
- Create your Feature Branch (
git checkout -b feature/AmazingFeature) - Commit your Changes (
git commit -m 'Add some AmazingFeature') - Push to the Branch (
git push origin feature/AmazingFeature) - Open a Pull Request
This project is licensed under the MIT License. See the LICENSE file for more details.