
Meticto: Multiplayer Meta Tic-Tac-Toe
Building a real-time online Meta Tic-Tac-Toe game with React and Socket.IO. A journey from a learning exercise to a shareable multiplayer experience.
Tech Stack
Project Overview: Meticto
Meticto is my take on Meta Tic-Tac-Toe (or Ultimate Tic-Tac-Toe), brought online as a real-time multiplayer game. It's the familiar 3x3 grid, but each cell contains another 3x3 Tic-Tac-Toe board. The twist? Where you play in a small cell dictates which large board your opponent must play in next. This simple rule adds layers of strategy, turning a seemingly basic game into something quite engaging.
The core goal was to build a fully functional, playable online version using React for the frontend and Node.js with Socket.IO for the backend communication and server-authoritative game state.
Motivation: Learning Through Building (and a Break!)
The idea for Meticto sprouted while I was deep in the dev trenches of a significantly more complex board game adaptation (a project I'm currently working on!). I realized that the turn-based game engine I was designing could be generalized and expanded, but I wanted a focused project to truly understand the nuts and bolts of managing turns, state, and real-time communication myself.
While libraries like boardgame.io
are fantastic, I felt the need to build the core WebSocket logic using Socket.IO
from scratch this time. Around then, I stumbled upon a YouTube video explaining Meta Tic-Tac-Toe. What initially looked like simple Tic-Tac-Toe on steroids quickly revealed its strategic depth – almost chess-like (okay, maybe that's a slight stretch, but the foresight required is surprising!). It seemed like the perfect candidate: complex enough to be interesting, simple enough to be achievable as a learning exercise.
The Journey: From Local Fun to Online Multiplayer
I started by building a basic local client just to implement the rules and play against myself. It was genuinely fun – both to code the logic and to play the resulting game. That intrinsic enjoyment is often the best fuel for a side project.
When I showed it to a few friends and they echoed the sentiment, enjoying the gameplay loop, the idea solidified: let's make this an online multiplayer experience. This meant diving headfirst into Socket.IO, designing the client-server message structure, and building the server-side GameManager
to handle multiple rooms and enforce the rules authoritatively.
The development followed several phases:
- Server Foundation: Refactoring initial logic into a robust
GameManager
, handling room creation, joining, and basic state synchronization. - Core Gameplay: Implementing the turn validation, move application, win/draw checks, spectator logic, and the mutual rematch feature.
- Timers: Adding optional server-side turn timers with forfeit logic.
- UI/UX Polish: Iteratively improving the user experience with visual turn indicators (that neon border animation took a few tries!), audio cues for turns and game end states, toast notifications for errors, and making the interface responsive.
Technical Details & Challenges
Building a real-time, stateful application like this always presents interesting hurdles:
- Meta Game Logic & State: The core rule linking moves to the next active board is the main complexity. Ensuring the server correctly validated moves against the
activeBoardIndex
, handled the "play anywhere" rule when a target board was full, and accurately determined win/draw states on both small boards and the meta-board required careful state management and validation logic within theGameManager
. Broadcasting the full, updated state after each move ensured client consistency. - Real-time Rooms (Players/Spectators): Managing different user roles within the same Socket.IO room needed clear separation. The server logic distinguishes join attempts based on room status ('Waiting', 'Playing', 'Finished') and maintains separate lists for players and spectators. Broadcasting updates correctly (e.g., game state to everyone, role info specifically) and handling cleanup when players disconnect (ending the game, notifying others) were crucial steps.
- Turn Indicator Animation: Getting the neon border animation to trigger reliably when the turn changed required iterating through a few approaches (CSS classes,
key
prop changes) before landing on the Web Animations API for direct, imperative control within auseEffect
hook, which proved most robust against React's render timing.
Key Features Implemented
- Full Meta Tic-Tac-Toe rules enforcement.
- Real-time online multiplayer for 2 players.
- Room creation with optional turn timers (15s, 30s, 60s).
- Lobby displaying available games (Waiting, Playing, Finished).
- Spectator mode for watching games.
- Functional turn timers with visual countdown and forfeit on timeout.
- Mutual rematch system with role swapping.
- Visual turn indicator (neon border animation).
- Audio cues for turn start, win, loss, and draw.
- Global mute option for sounds (persisted in localStorage).
- Toast notifications for errors.
- Basic responsive design for mobile/tablet/desktop.
Conclusion
Meticto successfully met its initial goal: it was a fantastic learning experience for building a turn-based, real-time multiplayer game system with Socket.IO from the ground up. It was also genuinely fun to build and play. Getting the core logic, room management, and real-time synchronization working correctly was incredibly satisfying. The recent UI/UX polish phase added a layer of interaction feedback that makes it feel much more complete.
I don't have grand ambitions for Meticto becoming the next big online game, but it serves its purpose as a completed project and a testament to the learning process.
Future Plans & Known Issues
If I notice significant traffic or find renewed motivation, potential future steps could involve adding persistence (likely via a database) to enable features like saved game history or user statistics – I even have some mockups exploring these ideas. Improving scalability beyond the current single-instance, in-memory model would also be necessary for higher traffic.
Known Issues / Areas for Refinement:
- Render takes a while to spin up the server, especially on the first load. This is a known issue with Render.com and not specific to my app. I might explore alternatives like Deno Deploy or Vercel in the future.
- The game state is currently stored in memory on the server. This means that if the server restarts, all active games are lost. A database or persistent storage solution would be necessary for a production-ready version.
- The UI could use more polish, especially on mobile devices. While it works, some elements could be better optimized for smaller screens. To give you an idea, my thoughts are revolving around how colonist.io handles mobile responsiveness. I might explore this further in the future.
For now, Meticto stands as a fun, functional strategy game and a valuable piece in my development journey.