LotusTable
Online multiplayer mahjong, with nine rulesets
Nearest to launch
Try it at lotustable.netA live demo instance. Play a table against bots or with others.
hand.state.yaml
machine: mahjong.hand
initial: dealing
states:
dealing:
on: { wall_dealt: discarding }
discarding:
on:
claim: melding
hand_complete: scoringStatelume
What runs on it
- Real-time multiplayer tables with live seat occupancy
- An event-driven engine over Kafka and Redis
- Statelume machines exercised by real players, not fixtures
Nine rulesets, all complete
Mahjong is not one game. LotusTable implements nine distinct rulesets, each as its own scoring engine: American, Beijing, Bloody River, Hong Kong (old and new), Japanese Riichi, Sichuan, Taiwanese, and the international competition standard (MCR / 国标麻将), which scores the full official 81-fan table.
That breadth is unusual and deliberate. Most platforms implement one or two variants, and the audiences barely overlap — American mahjong is a different game with a different demographic, MCR is what competitive play uses, and Riichi has the most organised online community. Scoring correctly across all nine is a deep, unglamorous body of work that is hard to replicate quickly.
Also the proving ground
LotusTable is a real platform with real players. It is also the field test for Statelume — the place where our state machines meet live concurrency and latency rather than a test harness.
That second job is deliberate. A language for modelling behaviour is only trustworthy if something demanding actually runs on it, and game rules are an honest workload: intricate, stateful, full of edge cases, and unforgiving about correctness.
How it is built
The platform is split so that the hot path stays short. A Spring Boot lobby server handles login, worlds and table listings, and serves the JSON and server-sent-event APIs the table page boots from. The table page itself is a static shell served separately, and game traffic goes from the browser straight to the game gateway — the lobby server is off the hot path entirely.
Underneath sit an event-driven engine, Kafka for the table-catalog projection feed, and Redis. Everything is containerised, built with Jib, and deployed continuously.
What it has been proven to take
The platform has run 2,500 concurrent game sessions with 10,000 connected players and zero failures, on four bare-metal desktop-class machines totalling 40 cores — which at the time were also running the load generator itself, our CI, and everything else we operate.
That is the largest run the hardware allowed, not the point at which anything broke.
What it feeds back
Every improvement LotusTable forces — a missing pseudostate, an awkward transition, a performance ceiling in generated code — goes back into Statelume. The game is the customer that never lets the language get away with an untested claim.