Inspiration

We wanted a fast, social way to play Mahjong online with friends, without needing everyone in the same place.

What it does

A real-time multiplayer Mahjong web app where players can draw, discard, and react (chi/pong/kong) with updates synced instantly across all clients.

How we built it

Frontend in React, backend in Flask + Socket.IO. Game actions are emitted over WebSockets, the server validates the move, updates the game state, and broadcasts state updates to each player (including private hands + public table info).

Challenges we ran into

The hardest part was game logic + state correctness: Mahjong has many branching “reaction” states, priority rules, and turn ownership changes. Keeping backend state authoritative while the frontend stayed responsive was tricky—especially avoiding race conditions and accidental extra draws when turn state changed through reactions. WebSocket event timing and per-player state syncing (public vs private) added extra complexity.

Accomplishments that we're proud of

We got the core real-time loop working: draw/discard, reaction prompts to only eligible players, and consistent state sync across all screens. The structure is solid enough to extend safely.

What we learned

State machines matter. Clear separation between “public table state” and “private player state” is essential, and WebSocket apps need careful guards to prevent duplicate actions and out-of-order updates.

What's next for mahjong web app

Finish the planned power-up card system, polish the UI for revealed/point hands, add better validation + logging, and tighten the turn/reaction state machine so edge cases are handled cleanly.

Built With

Share this project:

Updates