I recently built Mosaic Grid, a browser-based puzzle game focused on placement and rotation mechanics. In this post, I want to walk through the technical decisions, challenges, and architecture behind it.
https://mosaic-grid.vercel.app/
🎮 Concept
The game is played on a 6x6 grid, where players must:
- Drag & drop pieces onto the board
- Rotate them in 90° increments
- Avoid overlaps
- Match an expected solution
At its core, it’s a mix of spatial reasoning and constraint solving.
⚙️ Tech Stack
- Next.js (App Router)
- React
- TypeScript
- CSS Modules / Tailwind (depending on your setup)
- Custom state management (no heavy external libs)
đź§ Core Architecture
1. Grid Representation
The board is represented as a 2D array:
type Cell = {
occupied: boolean;
pieceId?: number;
};
const grid: Cell[][] = Array(6).fill(null).map(() =>
Array(6).fill({ occupied: false })
);
2. Piece Modeling
Each piece is defined as a matrix:
type Piece = {
id: number;
shape: number[][];
rotation: number; // 0, 90, 180, 270
};
Example (2x2 block):
[
[1, 1],
[1, 1]
]
3. Rotation Logic
Instead of storing multiple orientations, I compute rotations dynamically:
function rotateMatrix(matrix: number[][]): number[][] {
return matrix[0].map((_, i) =>
matrix.map(row => row[i]).reverse()
);
}
Each click applies a 90° rotation.
4. Drag & Drop System
I implemented a custom drag & drop system instead of relying on libraries.
Why?
- More control over snapping behavior
- Easier grid alignment
- Better performance for small UI
Key ideas:
- Track mouse position
- Translate piece preview in real-time
- Snap to nearest valid grid cell on drop
5. Placement Validation
Before placing a piece:
- Check boundaries
- Check collisions
- Apply special rules (e.g. 2x2 pieces must align on even coordinates)
function canPlace(piece, x, y, grid) {
for (let i = 0; i < piece.shape.length; i++) {
for (let j = 0; j < piece.shape[i].length; j++) {
if (piece.shape[i][j]) {
if (!grid[y + i] || !grid[y + i][x + j]) return false;
if (grid[y + i][x + j].occupied) return false;
}
}
}
return true;
}
6. Game State
State is split into:
- grid
- pieces (with position + rotation)
- progress (correct vs incorrect placements)
I avoided global state libraries and used React state + lifting state up, which was sufficient for this scale.
🎯 UX Details
Some small but important features:
- Double-click to rotate placed pieces
- Click to rotate pieces in the palette
- Visual feedback:
- 🟢 Correct placement
- đź”´ Incorrect placement
- Automatic “snap” to valid positions
đź§© Challenges
1. Rotation + Collision Combined
Handling rotation while ensuring valid placement required recalculating bounding boxes dynamically.
2. Snapping System
Making the drag feel natural without jitter or misalignment took several iterations.
3. Edge Cases
- Partial out-of-bounds placement
- Rotated shapes exceeding grid limits
- Special constraints for certain pieces
🚀 What’s Next
Possible improvements:
- Procedural level generation
- Mobile touch support improvements
- Animations (Framer Motion)
- Score / timer system
- Multiplayer or daily challenges
đź’¬ Feedback
https://mosaic-grid.vercel.app/
If you’re into puzzle games or frontend architecture, I’d love your feedback!
What would you improve in this kind of system?
How would you handle state or performance differently?

Top comments (0)