Sudoku with Friends
A multiplayer Sudoku game where you and your friends can solve puzzles together in real-time. No sign-up required, just create a room and share the link. Built with a custom Sudoku engine from scratch.


Key Features
Real-time Multiplayer
Live synchronization
No Registration
Instant play
PWA
Mobile-first design
Multiple Levels
From easy to Inhuman
Tech Stack
Frontend
Backend & Database
How It Works
Real-time Multiplayer
Firebase Realtime Database keeps everyone's moves synced instantly. When someone fills in a number, everyone else sees it within milliseconds. The trickiest part was handling conflicts, what happens when two people try to fill the same cell at the same time? I added validation and conflict resolution so it always stays consistent.
Custom Sudoku Engine
The most interesting part of this project was building the Sudoku engine from scratch: 820 lines of puzzle generation and solving logic. It uses a backtracking algorithm to generate valid puzzles and can create six difficulty levels by removing different amounts of numbers.
The difficulty ranges from Easy (62 starting numbers) down to Inhuman (only 17 numbers—the theoretical minimum for a valid Sudoku). Each puzzle is guaranteed to have exactly one solution.
State Management
React Context handles the game state, with undo/redo support and a notes mode for pencil marks. The validation runs in real-time, so you immediately see when there's a conflict.
Technical Highlights
Sudoku Generation
The puzzle generator uses backtracking to create valid grids:
// Recursive backtracking to fill the grid
function solveGrid(grid: number[][]): boolean {
for (let row = 0; row < 9; row++) {
for (let col = 0; col < 9; col++) {
if (grid[row][col] === 0) {
// Try numbers 1-9 in random order
const numbers = shuffleArray([1, 2, 3, 4, 5, 6, 7, 8, 9]);
for (const num of numbers) {
if (isValid(grid, row, col, num)) {
grid[row][col] = num;
if (solveGrid(grid)) return true;
grid[row][col] = 0; // Backtrack
}
}
return false;
}
}
}
return true;
}Real-time Sync
Firebase hooks keep the game state in sync:
// Listen to game state changes
useEffect(() => {
const gameRef = ref(database, `games/${roomId}`);
onValue(gameRef, (snapshot) => {
setGameState(snapshot.val());
});
return () => off(gameRef);
}, [roomId]);
// Update a cell and sync to all players
const updateCell = async (row: number, col: number, value: number) => {
await update(ref(database), {
[`games/${roomId}/grid/${row}/${col}`]: value,
[`games/${roomId}/lastMove`]: { row, col, value, timestamp: serverTimestamp() }
});
};Challenges
The hardest part was keeping the game state consistent when multiple people are editing at once. Firebase helps with atomic updates, but I still needed validation to handle edge cases. I ended up using optimistic updates with rollback, it feels instant but corrects itself if there's a conflict.
Making it work well on mobile took some iteration. The 9×9 grid needs to be big enough for fat fingers but still fit on small screens. I added a custom numpad and made sure all the touch targets were at least 44px.
Stats
Performance & Analytics
Performance Metrics
User Engagement
What I Learned
Building the Sudoku engine from scratch was a great exercise in algorithm design. Backtracking algorithms are elegant but can be tricky to debug, unit tests saved me hours of headache.
Firebase is great for getting real-time features up quickly, but you need to think carefully about data structure and security rules. I learned to use optimistic updates to make it feel instant while still validating on the backend.
Starting with single-player mode first was the right call. Getting the core game logic solid before adding multiplayer complexity made debugging much easier.