A one-tap gravity-flip dodge runner where grazing danger is the point
Rift Runner is a solo-designed and solo-built hypercasual endless runner: one input, tap to flip your rail, dodge obstacles scrolling toward you. It's built as a zero-dependency Progressive Web App — hand-rolled canvas rendering, no game engine, procedurally synthesized audio with no sound files at all, and no backend or account system, with every system running entirely on-device. It's an actively developed, ongoing project, currently at its September 2026 feature batch, covering four modes (Endless, Daily, Weekly, Zen), five obstacle types, eight unlockable skins, eleven achievements, and three difficulty tiers.
The design hook is the near-miss system: the safest way to play — flip early, stay maximally clear of everything — is deliberately not the highest-scoring way to play. Cutting close on purpose builds a streak into a higher-risk, higher-reward "Overdrive" state. That tension between surviving and courting danger is the whole design, not a bolted-on scoring gimmick.
Role: Solo Designer & Developer
Tools:
Vanilla JavaScript, HTML5 Canvas (2D context)
WebAudio API (procedural sound synthesis — no audio files)
Service Workers / PWA tooling (offline play, installability)
Playwright (automated and adversarial testing)
Responsibilities:
• Full game design — core loop, scoring, obstacle design, mode design, progression and customization systems
• Full implementation — rendering, collision, procedural audio, state persistence, offline/installable packaging
• Fairness and balancing architecture (see below)
• Building and running a 14-script automated test suite, including adversarial stress tests
Reward Risk, Don't Just Penalize Failure
The near-miss system tracks the closest distance the player ever got to each obstacle; clearing one within 34px counts as a close call worth bonus points and builds a streak. Five close calls in a row triggers Overdrive — track speed jumps 22%, the palette locks into a hot red/orange look, near-miss bonuses double — and it ends the instant the streak breaks or the player dies. The game is built so the cautious playstyle caps out below the risk-seeking one, which is what separates this from a generic tap-to-dodge clone: there's an actual skill ceiling, and it's "how long can you sustain Overdrive," not just "how long can you survive."
Legible By Feel, Not Just By Sight
Every event type — a near-miss, a milestone, death, Zen mode's non-lethal "soft hit" — has its own distinct haptic pattern and sound, so the game is readable without staring at the center of the screen. Obstacle types are also distinguishable by shape and pattern rather than color alone, after catching a real colorblind-accessibility gap where two obstacle types shared a silhouette and were only told apart by hue (white vs. red) — now one carries diagonal stripes, another a dot pattern, so the distinction survives regardless of color vision.
Problem: an unwinnable moment, not a hard-but-fair one
Early in development, obstacles could occasionally spawn on opposite rails in a combination that closed the entire track — a dead end the player had no way to avoid, not a difficult-but-survivable one.
Solution: a structural fairness guarantee
Rather than patching the specific case, the fix became a hard invariant: every obstacle, at spawn time, checks its position against existing opposite-rail obstacles and clamps its own height so a minimum safe gap (78px) always exists between them — calculated against each obstacle type's worst-case animated peak height, not its height at the moment it spawns, so the guarantee holds through the obstacle's entire animation cycle. When a later obstacle type (Mover) was added that can change rails mid-flight — breaking the assumption that only the spawn rail matters — it got its own version of the same check, re-run at the exact moment it attempts its one switch. If switching would create an unsafe combination, it simply doesn't switch that time, rather than ever forcing an unfair state onto the player.
Result: verified, not assumed
An adversarial stress test forced Mover's normally rare switching behavior to fire far more often than real play ever would — 1,095 sightings and 345 actual rail switches in 40 seconds, against roughly 220 score-gated triggers in ordinary play — and logged zero fairness violations. A separate 45-second bot run reaching a score of 726 across 5 lives also came back clean.
A second problem: the bug bots couldn't find
A related issue — the minimum gap between two obstacles in the same spawn pattern could be tight enough that a human physically couldn't react in time — passed every automated test, because a scripted bot doesn't need reaction time. It only surfaced once a real person played it. The fix floors the minimum gap between pattern steps against the player's actual flip-animation duration (160ms), scaling with difficulty (230–320ms) so tightening the difficulty can never cross back into genuinely unreactable territory.
Automated testing has a blind spot shaped like a human
The most important bug in this project — a gap tight enough to be unreactable — was invisible to every bot-driven test and only showed up in human playtesting. It reinforced that "passes all tests" and "feels fair to play" aren't the same claim, and a test suite built entirely around what's easy to automate will miss anything that depends on human timing.
Fairness has to be provable, not just intended
Building a system that rewards players for getting close to danger only works if they can trust that danger is never actually unfair. That trust couldn't rest on "I played it a lot and it felt okay" — it needed a structural invariant plus adversarial testing that tried to break it on purpose, which is what let the near-miss system be aggressive without being unfair.