MegaGem Live.
An interactive auction-strategy game — one human against two AI bidders — played from a first-person seat inside a neon 3D auction room. Nobody holds full information: read the hidden gem signals, win the auctions that matter, and outscore the AI before the round ends.
MegaGem Live began as a flat 2D auction dashboard — a port of Jane Street's board game MegaGems — and grew into a real-time game with AI opponents, an AI coach, and a full first-person 3D room, built from scratch with Python (Flask / Socket.IO) and vanilla JavaScript.
The core tension is hidden information. At the start of a round, half the gems are pulled from play and secretly split between the three players, so everyone holds a piece of the answer. A gem type's final value depends on how many of that type were removed — and the only way to learn more is to win auctions, which forces you to reveal one of your own secrets to the table.
How it plays
- 3 players — you plus 2 AI bidders. Everyone starts with 35 coins.
- 30 gems across 5 types (Gold, Sapphire, Jade, Onyx, Opal), 6 of each. At round start 15 are removed and dealt as private information — 5 per player.
- A deck of action cards drives each auction — Win 1 Gem, Win 2 Gems, Invest, Loan. The highest bid wins and pays; ties break to the lower player ID.
- Winning costs information — you reveal one of your private removed gems to everyone, which shifts the whole table's estimate of what each type is ultimately worth.
So the game is a constant balancing act: spend coins to win value and gather signals, but overpay early — or leak the wrong secret at the wrong moment — and you hand the round to the AI.
Three ways in
- Start Game — the full real-time auction: live bidding, reveals, and end-round scoring.
- Play with Tutor — the same game with an AI coach on its own wall screen, offering tips and answering strategy questions mid-round.
- Learn How to Play — a step-by-step tutorial plus a Q&A chat with an AI tutor.
Inside the 3D room
The 3D upgrade was deliberately decoupled from the game so the graphics could be bolted on without touching a line of game logic. Four layers, each with one job — and because the backend broadcasts the exact same events it did for the original 2D dashboard, the whole room is effectively a presentation skin (a Flat 2D mode in settings switches it off entirely).
Flask + Socket.IO
Runs all game logic, resolves bids, and broadcasts events. It has no idea the frontend is 3D.
Three.js · WebGL
Renders the physical room — desk, lighting, and the spinning gems that mirror the live gem queue.
CSS3DRenderer
Folds ordinary HTML — the bid form, leaderboard, and auction log — inside the 3D space as interactive panels, not flat textures.
Socket.IO client
The connective tissue: it listens to the Brain, then tells the Stage to animate and the Illusionist to update.
Tech stack
- Backend — Python, Flask, Flask-SocketIO, eventlet.
- Real-time — Socket.IO (WebSockets) for live bid / reveal / scoring events.
- AI — Google Gemini (
google-genai) powers the AI bidders, the in-game coach, and the tutor, with a heuristic fallback when no API key is present. - 3D — Three.js (WebGL) for the room, plus CSS3DRenderer to fold real HTML panels into the scene.
- Frontend — vanilla JS, Socket.IO client, hand-built SVG gem art, and a custom canvas layer for sparkles, shockwaves, and confetti.
- Deploy — Docker and Render for the app; GitHub Pages for the static landing page.
Engineering challenges
- The human seat kept locking players out. It was tied to a socket connection, so a
refresh spawned a new socket and the game rejected the returning player. Fixed by binding the seat
to a persistent client ID in
localStorage— it now survives refreshes and transfers to a new client if the owner has no live connection. - The gem queue could show an impossible state. A refill bug briefly displayed 3 gems when it should never exceed 2. Fixed by refilling one gem at a time, and verified across 200+ simulated games.
- Gemini availability is a moving target. Model IDs 404 by key, region, and API version, so the app defends with a fallback chain of model names and a heuristic bidder — the AI never hard-crashes the round.
- Folding real HTML into a 3D scene. Keeping CSS3DRenderer panels crisp, clickable, and correctly scaled meant auto-scaling every screen to the window while it stayed a live form element rather than a flat texture.
- Camera feel. View switches interpolate (lerp) between targets — the big screen, your tablet, the neutral room, the auction log — instead of snapping, simulating the motion of turning your head.
Reflection
The most valuable lesson wasn't any single technology — it was the payoff of clean separation of concerns. Because the backend broadcasts game events without caring how they're displayed, I could reimagine the frontend from flat HTML into a WebGL auction room without rewriting the game. That Brain / Stage / Illusionist / Messenger split is the thing I'd carry into every future project.
The bugs taught the rest: in a real-time app, identity has to be durable and the source of truth stable — tying a seat to a socket felt fine until the first refresh proved otherwise. And integrating Gemini was a lesson in designing for failure: build the fallback path first, so the game stays fun whether its opponents come from a language model or a simple heuristic.