-
-
Every morning the whole machine re-runs, and everyone watches the same marble.
-
One machine, built by a whole subreddit, one part at a time.
-
One part per person, per day. Choose it, rotate it, place it.
-
You can only build where yesterday's marble actually reached.
-
Parts the marble stops touching dissolve overnight. No vote can save them.
-
1,879 px deep. A record the community set together.
-
Every part is signed, and credited with exactly how far it carried the marble.
-
It lives as one Reddit post that the whole subreddit comes back to.
Inspiration
I collected marbles as a kid and kept a box of them. The kind of small private hoard you take out, line up, and admire by yourself. Nobody else ever touched that box.
Clatterfall is the inverse of that box. There is one marble, and it belongs to everyone. A whole subreddit builds the machine it runs down, one part per person per day, and every morning we all watch the same marble take the same run. I wanted to take the thing I loved alone as a kid and turn it into something a community does at the same time.
The mechanic came from the r/place feeling: a thing nobody could build alone, that only grows. But r/place is a canvas, and a canvas just sits there. I wanted the collective artifact to do something. So it became a machine, and the machine runs.
What it does
r/Clatterfall is a single, persistent, ever-growing chain-reaction marble machine that an entire subreddit builds together.
- You get one part per day. One. You drop it into the machine's live "frontier", the handful of cells just past where the marble currently reaches.
- Every morning the machine runs. A scheduled job re-simulates the entire committed machine, top to bottom, and launches one marble down it.
- Everyone comes back to watch the same run. Did today's parts set a new record, or did a bad part cap us short? The marble threads the whole community contraption while the depth ruler climbs toward the record pin.
- Every part is signed. Tap any part to see who placed it and exactly how far it carried the marble ("u/alice's ramp carried it +118 px"), and vote to keep it or cut it. Each run's result card names the three redditors whose parts carried the marble furthest that day.
- The machine keeps what earns its place. Parts the marble stops touching dissolve on their own. Nobody curates that: the physics does.
The hook is the 24 hour gap between placing your part and finding out whether it helped. That is the whole game.
How I built it
Stack: Devvit Web, Phaser 4, TypeScript, matter-js, Hono, Redis.
The central design problem was this: if physics runs on each player's device, everyone sees a different marble. That breaks a shared collective artifact instantly.
So the physics is server-authoritative. Once a day, the server runs matter-js headlessly over the whole machine and records the marble's path as a compact keyframe trail. Every client then replays those exact keyframes with Phaser tweens. Cross-device drift is not mitigated, it is structurally impossible. Every redditor watches the identical run, down to the pixel.
Phaser draws everything inside the game. All the art is generated at runtime from Graphics primitives (the wood ramps, the brass ruler, the marble, the confetti). There is not a single sprite sheet, texture, or piece of AI art in the project, and the game never loads an image at runtime. Every PNG in the repo sits outside the game: the app icon, the subreddit icon and banner, and the Devpost art are each written by a script in tools/ from the game's own palette, and the rest are screenshots of the game itself. On top of that: a camera-follow rig chasing the marble, tween choreography for the replay, impact flash rings and a motion trail, camera shake on a record break, and a three-scene lifecycle.
The bit I like most is the record line. The depth to beat is drawn straight across the shaft, and as the marble closes on it the replay clock dilates into slow motion while the camera leans in, so the moment the whole community has been waiting 24 hours for actually plays like a moment instead of scrolling past at constant speed. When he crosses, the line snaps and falls away. The simulation is immutable and already happened on the server, so none of this changes what the run says, only how it feels.
Redis turned out to be the interesting constraint. Devvit's client has no set operations, so I built the "one part per cell" and "one part per user per day" guarantees out of hashes with hSetNX, which returns 1 only on first write. That gives true atomic first-commit-wins claims. Placement claims the cell before it claims your daily part, so losing a race to another player never burns your one part for the day.
The frontier is what keeps the machine coherent. You can only build in the corridor the marble actually falls through after the machine lets go of it, widened by one column each side. Anywhere else, the marble would never touch your part, so it would score nothing and dissolve. Without the constraint, hundreds of daily parts become a chaotic pile. With it, the machine stays a deliberate contraption that weaves down the shaft.
Challenges I ran into
Determinism. Solved by the server-once simulation above. This was the first architectural decision and it removed an entire class of bugs before they existed.
Coordination. My first instinct was to let people build anywhere. Simulating that, the machine either became a chaotic pile (everyone builds everywhere) or a one cell wide drill (everyone builds at the tip). The fall-corridor frontier, plus bounded 8 cell walls that reflect the marble back inward, was the fix.
I got the frontier wrong once, and simulating a whole season is what caught it. It started as a widening cone below the marble's last contact, which sounds right and is wrong: the marble falls in a narrow corridor, so most cells in that cone were places it would never visit. People were being invited to spend their one part a day somewhere it could not possibly matter. Simulated over 20 days, random players never improved the record once. Making the frontier the marble's actual fall path fixed it, and it made the rule mean what the game says it means.
Fragility. A serial marble run is far more fragile than a canvas. One bad part could strand everyone. I made it structurally impossible: a soft catch floor always sits below the deepest part, so the marble always completes. A bad part can only cap the distance, never brick the machine. And parts the marble stops touching decay out on their own, so the machine self heals.
The judging window problem. The scored run only happens once a day, which is the whole retention hook, but it means someone trying the game for five minutes can never see their part run. I added a non-scoring Test run that re-simulates the current machine on demand, including the part you just placed. You get the answer immediately, without letting anyone rehearse away the daily cliffhanger.
Phaser scenes are singletons. Class field initializers do not re-run when you re-enter a scene. This crashed the core loop (watch a run, go back to build) and froze every run after the first. It only showed up when I tested the actual cycle rather than each scene in isolation.
Accomplishments I'm proud of
The daily run. One vermilion marble threading a machine that a whole community built, camera chasing it, parts flashing as it hits them, the ruler climbing toward the record pin. It is a thing no DOM or CSS app can fake.
The contribution math. Every part gets credited with exactly how far it carried the marble, using a high-water-mark accumulator. The per-part credits sum to the total depth exactly, even when a bouncer throws the marble upward. That is what lets me tell you "your ramp carried it +118 px" and have it be literally true.
Zero moderation surface. A "user contribution" here is a part type, a rotation, and a grid cell. There is no free text, no image upload, no way to inject anything harmful. It is UGC that cannot be abused, by construction. Mods can still pull a single griefing part from the board (the server re-checks moderator status on every removal), but there is no harmful content for them to have to remove.
Every piece of art in the game is drawn from primitives. No AI art, no asset packs, no textures, and the game never loads an image at runtime. Even the art outside the game (the app icon, the subreddit icon and banner, the Devpost card) is written by scripts in tools/, from the game's own palette.
What I learned
- Devvit's Redis has no set operations.
hSetNXon a hash is the atomic primitive you actually want, and it is enough to build correct first-commit-wins claims. - Phaser scenes are singletons. Reset your mutable fields in
init()or your second visit will not behave like your first. - Server-authoritative simulation is a cheat code for shared experiences. Simulate once, ship keyframes, let clients animate. Nobody can desync.
- Test the cycle, not the screen. All my screenshots passed while the game was crashing on the return trip, because I was loading each scene fresh instead of moving between them.
- A validation pass will find lies in your own writeup. Mine claimed the preview was "live client physics". It is a server re-simulation. I cut the claim.
What's next for Clatterfall
- Seasons. The goal basin deepens each season, and the finished machine gets saved as a monument the sub can scroll back through.
- More parts. Spinners and seesaws were cut to keep only the marble dynamic. Now that the sim is stable, they can come back.
- Real communities. The machine is subreddit scoped, so any sub can run its own. Watching two different subreddits evolve wildly different machines from the same four parts is the thing I most want to see.


Log in or sign up for Devpost to join the conversation.