Inspiration
Reddit's own list of what keeps redditors coming back — r/honk, r/colorpuzzlegame, r/bunnytrials — all share the same shape: a short, satisfying core loop, community-made content, and a reason to check back tomorrow. We wanted to build one game that earned all three of those things at once instead of picking a lane, because the hackathon's own prize tracks (a retentive "hook," Phaser, retention mechanics, user-generated content) aren't actually four separate problems — they're four views of the same well-built loop.
Marble-run physics toys (Incredible Machine, Pachinko boards, the various "place a ramp and watch it roll" mobile puzzles) are inherently communal and satisfying to watch — someone else's solution to your problem is interesting in a way that someone else's Wordle score just isn't. That's the emotional core CASCADE is built around: you don't just solve the puzzle, you watch a small crowd solve it with you, in real 3D, at the same time.
What it does
CASCADE drops a marble down a tall vertical shaft. You have exactly two parts — a tiltable Plank Bridge and a bouncy Spring Cap — to deflect it sideways into a goal that's deliberately not directly below the start. Tap a part, tap the shaft to place it, tap GO, and watch real Rapier3D physics decide whether you solved it.
Every solve becomes a ghost: not a recorded video, but the actual placement data, replayed as a second live physics simulation the next time anyone opens the post. Open today's level and you're not solving it alone — you're watching a handful of translucent marbles (the community's best runs, gold-tinted for the current leader) fall through the same shaft alongside yours.
Players can also build a level: place up to three obstacle pegs, choose where the goal sits, decide how generous the solver's budget should be — and then they have to prove it's solvable themselves before they're allowed to submit it. Submitted levels enter a community playtest queue, where other players try them and rate them 1–5. Once a day, a scheduled job automatically posts the highest-rated pending level to the subreddit — so the game's content pipeline runs itself.
How we built it
CASCADE runs on Devvit Web, with a deliberately hybrid rendering stack: a Three.js scene running a live Rapier3D physics world for the actual 3D marble-run (real WebGL, real lighting and fog, real physics — not an isometric illusion), with a fully transparent Phaser 3 canvas layered on top owning every bit of interaction: the HUD, tap-to-place input (raycast from Phaser's pointer events into the Three.js camera), the level editor, results screens, and all scene transitions. Nothing in the game works without Phaser driving it, even though the world underneath is genuinely three-dimensional.
The core insight that shaped the architecture: the client and server share one simulation module. buildWorld() and simulateLevel() live in src/shared/sim/ and are imported by both sides. The client steps that world once per animation frame so the player watches it happen in real time; the server fast-forwards the identical world in a tight headless loop with no rendering, purely to check the outcome. Same physics code, same scoring formula, two different clocks — which means the server can always independently re-verify a claimed solve instead of trusting whatever the client sends.
The backend is a Hono app running as a Devvit serverless function, using Redis (via @devvit/redis) for leaderboards (sorted sets), streaks, and the community level queue, plus the Reddit API for creating posts and awarding milestone-streak flair. A Devvit scheduler cron task runs once a day, pulls the top-rated pending community level (or falls back to a hand-built seed level if the queue is empty), and posts it — the exact same "pick a level and post it" function is shared by the daily cron job, the moderator's manual menu item, and app install, so there's one code path for "how does a post get its level" instead of three.
Challenges we ran into
- Reconciling "real 3D physics" with "fair, cheat-resistant leaderboards." Rapier3D isn't guaranteed bit-identical across every device, so instead of chasing frame-perfect cross-machine determinism, we made ghosts replay-by-placement (a few hundred bytes of input data, not recorded frames) and made the server the sole source of truth for scoring — every solve, and every level submission's solvability proof, gets independently re-simulated server-side before it's trusted.
- Two rendering engines sharing one input surface. Phaser and Three.js both want to own pointer events. The fix was to let Phaser own input exclusively and have it raycast into the Three.js camera/scene manually (
world.screenToPlacementPlane) rather than trying to split events between two canvases. - Keeping the UGC loop honest without heavyweight moderation tooling. The anti-troll gate isn't a content filter — it's structural: a creator can't submit a level without first solving it themselves, and that proof is re-verified server-side. Combined with the fact that submitted levels are pure numeric data (peg positions, a goal position, a budget) rather than free text or code, the abuse surface stays small by construction.
- A real bug we caught ourselves: an early version of
/api/level/todayalways returned the same hardcoded level regardless of which post was open. Fixed by binding each post to its level at creation time (post:{postId}:levelIdin Redis) instead of assuming "today" means the same thing for every post.
Accomplishments that we're proud of
- A genuinely 3D physics puzzle running inside a Reddit webview, with a hybrid Phaser/Three.js architecture that keeps it eligible for the Phaser-specific award while not compromising on visual identity.
- A ghost-racing system that's real physics, not a trick — every ghost you see is an actual second (third, eighth) Rapier3D simulation stepping in the same render loop as your own marble.
- A complete, closed UGC loop: build → prove solvable → submit → community rates → best one auto-features daily. Nothing in that pipeline needs a human moderator to keep it running day to day.
- Every server endpoint independently re-verifies the physics rather than trusting the client — checked against the actual installed Devvit Redis/Reddit type definitions rather than assumed API shapes, so nothing here is guesswork.
What we learned
That the four hackathon tracks aren't in tension if you build the feeding relationships between them first: UGC becomes the retention engine's content source, the daily post becomes the venue for the hook, and the hook (racing ghosts) becomes the reason anyone bothers rating other people's levels in the first place. Designing the data flow between systems mattered more than polishing any single system in isolation.
What's next for CASCADE
- Cosmetic unlocks (marble skins, part finishes) tied to streaks and style-score milestones.
- Letting editors reshape the shaft itself, not just goal position / pegs / budget.
- A weekly "Builder of the Week" recap post, auto-generated from the rating data already being collected.
- Tighter mobile polish and further code-splitting to shrink the initial
game.htmlpayload.
Built with
Devvit Web · Three.js · Rapier3D (@dimforge/rapier3d-compat) · Phaser 3 · Hono · TypeScript · Vite · Redis
Track alignment (for reference while filling out Devpost's category checkboxes)
| Track | How CASCADE qualifies |
|---|---|
| Best App with a Hook | Daily physics puzzle + real-time ghost racing gives a reason to return every day and something to beat each time. |
| Best Use of Phaser | Phaser drives the entire interaction layer — HUD, input, editor, scene machine — on top of a genuinely 3D Three.js/Rapier3D world it couldn't render alone. |
| Best Use of Retention Mechanics | UTC-day streaks with milestone flair (3/7/14/30/60/100 days), a scheduled daily post, and a "beat today's best" prompt shown before you even place a part. |
| Best Use of User Contributions | Full level editor with a solvability-proof gate, a community playtest/rating queue, and automatic daily promotion of the top-rated submission — no human curation required. |
Links
App listing (developer.reddit.com):
https://developers.reddit.com/apps/cascade-marblesSource (this repo):
https://github.com/rohanjain1648/CASCADE
Built With
- devvit
- hono
- phaser.js
- three.js
- typescript
Log in or sign up for Devpost to join the conversation.