Inspiration My stepson Rian got into a tech high school. Great school. He doesn't know a single person there.

Here's the thing about starting over: whatever you were good at doesn't transfer. Rian makes friends the way a lot of kids do — on a field, playing something. The kids at this school, by and large, are not going to be out there with him. Every social move he knows is about to stop working.

So we went looking for one that would work instead. Something sitting at the intersection of what he's walking into and what he already loves: tech and games. Something we could build together every day, where he'd learn what these tools actually do by pointing them at something real — with a deadline, and strangers who'd play it.

And something he could hand a person on day one.

That's what a daily game is. Not an app you have to talk somebody into installing — a post. You send a link, it plays right there in the feed. Everyone flies the identical canyon on the identical day, so "what'd you get?" is a real question with a comparable answer. That's a conversation with a stranger, and it renews itself every twenty-four hours.

Reddit is where you go when you don't know anyone yet. That's the whole point of the place. Building it here wasn't a platform decision — it was the idea.

Then we added the part that makes it hurt: your first run is permanent.

Rian is u/ButterscotchKey3563. As I write this he's #2 on the First Run board, 1.42 seconds behind me, on a run he can never take back. He is not going to leave it there. And in September he walks into a building full of people who don't know him yet, holding a link.

What it does Every UTC day generates one canyon, seeded from the date. Every pilot flies the identical track with the identical modifiers — Gravity Well, Squeeze Storm, Blackout. Fairness is structural, not promised.

First Run · One Shot. Your first attempt is recorded immutably. It can't be beaten, re-rolled, or deleted. No warm-up. No take-backs. It's your name on the wall, and it's the only score that can never be improved.

Then you're free. Practice all you want, chase the Best Time board, and race the ghosts of the pilots ahead of you — actual telemetry from their runs, replayed on-track beside you.

Two forks split the canyon into four routes. The Route Report shows what the field chose and which line is actually fastest today, so a fork is a decision instead of decoration. Boost gates. Hull damage from wall scrapes. Repair pickups. And a landing worth −2.50s if you stick it, +3.00s if you blow it.

A nemesis is assigned from the pilot immediately ahead of you. Export your run as a ghost code and anyone can race your exact line. Yesterday's podium sits above today's race. Tomorrow's modifier is decided by community vote before the sector rolls at 00:00 UTC.

Come back tomorrow. The canyon changed.

How we built it Devvit Web + Phaser 3 + Devvit Redis. TypeScript end to end, with a shared contract between client and server. No external services — everything runs on Reddit's platform.

Zero-drag drift physics with hard-clamped velocity: the ship carries momentum, and you steer by committing early. Tracks generate deterministically from the date, so every pilot provably gets the same canyon — we couldn't fudge it if we wanted to.

Runs record telemetry at 10Hz. The server compresses it and serves it back as ghosts you race alongside — not a leaderboard row, but an actual line through the canyon that somebody actually flew.

Everything renders in a fixed 380px viewport, mobile first. Instant retry is under a second, and nothing was allowed to ship that made it slower.

133 tests: seeded determinism, a 15-day rollover suite, telemetry round-trips, and the full scoring model.

Challenges we ran into Our own anti-cheat rejected every legitimate run — and 122 passing tests never caught it.

One architectural mistake caused all of it: the server re-derived gameplay events from coarse 10Hz telemetry using different math than the client. Six validators fired on honest play.

The scrape recorder overwrote a telemetry sample's position with a wall-boundary coordinate. The next sample snapped back to the truth, and the synthetic jump computed to 798 px/s against a physical max of 405. Any run that touched a wall was rejected. In a tight canyon, that's every run. The gate validator reconstructed hits using a wider hitbox than the client used, over-counted, then demanded an exact match. The hull check subtracted total scrapes from max hull and forgot that repair pickups exist. And the worst one compared a run's physical time floor against its bonus-adjusted time. A 14.7s run with a −2.50s stick landing became 12.2s — under the 13.1s floor. Being good at the game was the bug.

The suite was green the entire time. It only ever proved that forged runs get rejected. Nothing tested that legitimate runs get accepted. That one-directional gap hid a game that couldn't record a score behind a wall of checkmarks for a full day.

Every one of those bugs was found by a human playing the game and saying "why does this say session expired." Not one was found by a test.

The fix was a principle, not a patch: anti-cheat must fail open. The stakes here are bragging rights. A rejected real player is catastrophic; an admitted cheater is trivial. We now trust the client's tokens and reject only the structurally impossible.

Accomplishments that we're proud of. The First Run is genuinely immutable. Every instinct said to soften it — add a mulligan, let people re-roll a bad start. We didn't. The permanence is the point.

The ghosts aren't decoration. When you race the pilot above you, you're racing their real recorded line, and you can see exactly where they took the fork you didn't.

The Route Report turns a fork into an argument: the field went R-L 100% today, and R-L is the fastest line. That's a genuine strategic read, generated entirely from what real people actually did.

And instant retry is under a second. Nothing shipped that made it slower.

What we learned. Green tests are not verification. Every real bug in this build was found by a human playing the game. Test the acceptance path, not just the rejection path — proving you reject bad input tells you nothing about whether you accept good input.

Never re-derive gameplay on the server from lossy telemetry. Trust the client's tokens and reject only what's structurally impossible. One architectural mistake produced five separate bugs that each looked unrelated.

And a validator that punishes excellence isn't strict. It's broken.

But the lesson I actually wanted to hand Rian turned out to be the one I didn't plan. We had 122 passing tests, four green checks, and a game that silently threw away every honest run. The tooling was confident and completely wrong. The only thing that caught it was a person picking up the game and saying "why does this say session expired?"

Build the thing. Then go play it yourself.

What's next for Canyon Run: Daily Sectors. Par-relative medal tiers. Every sector already authors a par time; medals derive from it — Platinum at par−15%, Gold at par−5%. Fixed second thresholds break the moment a canyon runs long. Par scales to any track, including procedurally generated ones.

Medal-weighted voting. Gold medalists' votes on tomorrow's sector count 3×. Win today, choose tomorrow's battlefield. Today's performance shapes tomorrow's track — the tightest loop we can build.

Distribution. The first real test isn't a leaderboard — it's a hallway. A daily game is the cheapest opening line that exists: no install, no account, no pitch. Just a link that plays in the feed, and a score you can compare because everyone flew the same canyon. Most of the people we most want playing this aren't on Reddit yet. They're about to be, because someone handed them a sector.

Procedural sectors beyond the authored rotation. Cosmetic ship skins with identical physics. Never pay-to-win in ranked.

Built With

Share this project:

Updates

Submission history