Inspiration
Board games are how families actually talk to each other. Picture this: Grandma is in one city and the grandkids are in another, and "let's play Monopoly on a video call" -- a staple means of fostering connection -- means someone holds a phone over a board for two hours. We wanted the board itself to be the shared thing: real dice, real cards, real pieces sliding across a real table, while the people playing could be anywhere. And, following the theme of fairy tales, the board became a storybook: Hansel and Gretel's Black Forest, Rapunzel's Tower instead of jail, a Magic Mirror that narrates every turn.
What it does
Magic Monopoly is a physical Monopoly board where the tokens move by themselves. A player at the table rolls real dice into a tray; an overhead camera reads the pips and the game engine drives that player's token along its lane with a magnet hidden under the board. Land on Crystal Ball or Chest of Spells, draw a physical card, lay it in the card zone, and the same camera switches to reading the card's marker and applies its effect. The Mirror, voiced by Meta Muse Spark and read aloud by ElevenLabs, narrates each turn, asks "would you like to buy Royal Ballroom for 120 gold?", and listens for the answer through Meta Muse Voice Transcribe. Nobody at the table touches the laptop.
Remote players join by scanning a QR code. Their phone shows the same board, their gold and their property cards, live, and lets them roll and buy on their turn. Anything they type is spoken at the table by the Mirror, and when the table rolls, Muse Spark looks at a camera frame and tells the phones what it sees: who is leaning over the board, whose hand just threw the dice. Everyone, at the table or far away, is in one shared game.
Every action is saved. Close the laptop mid-game and reopen it, and the links still work, the state is intact, and the tokens drive back to their squares. Chronicles folds the history of every game into stats, and player accounts carry wins, finishes and favourite properties across games. A 3D storybook view follows the moving token around a miniature of the board.
How we built it
- Hardware. An Arduino Uno with a CNC shield drives NEMA-17 steppers; each lane is a GT2 belt loop carrying a neodymium magnet under a thin acrylic plate, and the tokens have steel bases. Custom firmware takes a tiny serial protocol. A small belt service exposes the board over a Cloudflare tunnel, so the game server moves tokens with one HTTP call, reads back the real square before each move, and takes the shorter way round.
- Vision. OpenCV on a Logitech C920 looking down at the action zone. Dice are found as bright blobs, each die is warped flat and its dark round pips counted; a state machine waits for the dice to settle and requires three agreeing frames before a roll counts. Cards carry ArUco markers (one id per card, printed from the same JSON the engine uses), so identification is exact and rotation-proof. One camera process does both: it asks the game what it is waiting for and switches between the dice tray and the card zone by itself, and the board page shows what the camera sees at every moment.
- Game engine. A pure-Python rules engine: state plus action gives new state plus events, no I/O, fully unit-tested. A room per game applies actions under a lock and broadcasts state over WebSockets to every page. Camera, belt, voice and narrator are adapters around it, each with a stub, so the whole game runs without any hardware.
- Persistence and sharing. SQLite logs every action with a state snapshot; games resume on restart with the same links. A Cloudflare tunnel gives a public URL so phones work on any network, with QR codes on the create page.
- Meta Muse. Muse Spark writes the Mirror's lines from engine events (templates as a fallback, so the game never waits on the network), reads camera frames to describe the table to remote players, and resolves unclear spoken answers. Muse Voice Transcribe turns a short mic clip into yes or no during the decision window.
Challenges we ran into
- Belt corners. A rigid carriage can't wrap around a pulley, so the carriage hangs off the outside of the belt loop. Getting a token around a corner without jamming was the single riskiest thing in the build.
- Dead reckoning. The motors don't know where the token really is. We zero at Once Upon a Time, read the belt's own count before each move, and added a debug sync that adopts the physical position so a skipped tooth doesn't ruin a game.
- Light tables. Our dice reader used Otsu thresholding, which split the wood grain instead of finding the dice when the dice were under 2% of the pixels. Thresholding between the background median and the brightest pixels fixed it on real frames.
- A reasoning model that said nothing. Muse Spark is a reasoning model, and our first narrator calls came back empty because the token budget was spent thinking. Minimal reasoning effort and a bigger budget turned it into a one-line storyteller.
- Four workstreams at once. Hardware, vision, the game app and the design were built in parallel and merged the same evening, including two different ideas of what the camera should send the engine. A small translation layer and 334 tests kept it honest.
What we learned
Keep the camera stateless and let the engine decide what a read means; a stray roll then can't corrupt anything. Make the physical world the input and the virtual state the truth, never the other way around. And write the pure engine first: everything else, from phones to voice to resume, became an adapter around it.
What's next for Magic Monopoly
Homing switches at the start square, Muse Spark writing the Mirror's lines and an end-of-game storybook with Muse Image, and three real lanes around a bigger board.
Two notes before you paste it. It describes Muse Voice as working, which is true in code but unverified against the live API until you run the key check, so confirm that before submission or soften the sentence. And the Muse Spark narration is listed under "what's next", since it isn't built yet; if we get it in tonight, move it up.
Built With
- a4988
- acrylic
- arduino
- aruco
- c++
- camera
- cloudflare
- css
- fastapi
- gt2-belt
- html
- javascript
- meta-muse
- muse-voice-transcribe
- nema-17
- neodymium-magnets
- numpy
- opencv
- pytest
- python
- qr-code
- sqlite
- uvicorn
- web-speech-api
- websockets
Log in or sign up for Devpost to join the conversation.