Snake Game
Modern Browser-Based Arcade Game
This project is a modern, browser-based Snake game built with React and TypeScript, designed to deliver classic arcade gameplay with a polished UI on both desktop and mobile. The system focuses on clean separation of game logic and presentation, responsive layout, and stable real-time state updates in a fixed-interval game loop.
The core focus of the project is on predictable game state management, cross-device input handling, and maintaining smooth performance without unnecessary re-renders.
Technologies
React 19TypeScript 6Vite 8Plain CSSCSS GridlocalStorage
Features
- Classic Snake gameplay with self-collision detection
- Wrap-around walls (exit one edge, re-enter from the opposite)
- 10 adjustable speed levels
- High score tracking across sessions
- Random emoji food items
- Desktop controls (Arrow keys / WASD) with pause support (Space / Esc)
- Mobile-friendly UI with responsive board scaling
- On-screen D-pad and swipe gestures on the game board
- Safe-area support for notched devices
- Dark theme with glassmorphism panels and smooth animations
My Role in Project
Front-End Developer (React / TypeScript)
Responsible for implementing game logic, UI/UX, responsive layout, input handling (keyboard, touch, swipe), state management, and local persistence.
Challenges & Focus
Running a stable game loop with React state without race conditions or stale direction valuesSupporting multiple input methods (keyboardD-padswipe) with consistent behaviorMaking the board fully responsive on small screens without breaking grid alignmentPreventing invalid direction changes (e.g.instant 180° turns)Persisting user preferences (speed levelhigh score) reliably in the browser
Technical Decisions
Custom Hook for Game Logic (useSnakeGame)
All core game state and rules live in a dedicated hook, keeping the UI component focused on rendering and user interaction.
useRef for Direction
The current direction is stored in a ref to avoid stale closures inside the interval-based game tick and to prevent invalid reverse moves.
Separation of Logic, UI, and Utilities
Game rules, rendering, and helper functions (food generation, wrapping, speed calculation) are split into separate modules for clarity and maintainability.
CSS Grid for the Board
The game board is rendered with CSS Grid instead of Canvas, enabling simpler styling, emoji food rendering, and responsive scaling.
Plain CSS instead of a UI Framework
Custom CSS was chosen for full control over animations, glassmorphism effects, and mobile-specific layout without extra dependencies.
localStorage for Lightweight Persistence
High score and speed level are stored locally without a backend, keeping the app fully static and deployable anywhere.
Optimizations & Solutions
- Functional state updates in the game tick to work safely with React's batching
- Memoized callbacks (useCallback) for direction changes, reset, and pause
- Dynamic cell size calculation based on viewport width for mobile fit
- Opposite-direction blocking to prevent accidental self-collision
- Food spawn logic that avoids overlapping with the snake body
Trade-offs & Engineering Considerations
- CSS Grid rendering was preferred over Canvas for simplicity and styling flexibility, at the cost of less scalability for very large grids or advanced visual effects.
- The game loop uses setInterval instead of requestAnimationFrame, which is simpler but less frame-synced with the display.
- No backend or multiplayer was intentionally omitted to keep the project lightweight and fully static.
- Touch and keyboard inputs share one direction handler, with some mobile UX trade-offs (e.g., no diagonal movement).
Outcome
This project demonstrates practical front-end engineering beyond simple UI: managing real-time state in React, designing cross-device interactions, and structuring a small codebase for clarity and extensibility. It shows attention to UX details (responsive board, swipe, persistence) while keeping the architecture simple and maintainable.