Orbit Snatch — Game Design

Date: 2026-05-28
Status: Approved (Superpowers demo)
Integration: svelte-component, single-player arcade

Problem

Funday needs a simple, novel, instantly playable arcade game that demonstrates platform best practices without multiplayer/Nakama complexity.

Approaches Considered

ApproachHookProsCons
A. Orbit Snatch (recommended)One-button direction flip on a gem orbitUnique mechanic, 30s to learn, combo dopamineCircular collision needs careful tuning
B. Chrono Rewind DashRewind 3s every 5sVery novelHard to explain, complex UX
C. Flappy clone variantTap to riseFast to buildNot unique; jumpduck exists

Recommendation: Orbit Snatch — one input, orbital motion, combo streaks, escalating speed. Distinct from peng (paddle), jumpduck (side-scroll), memory (turn-based).

Core Loop

  1. Player orbits a central star at fixed radius.
  2. Tap / Space / click flips orbit direction (CW ↔ CCW).
  3. Gems spawn on the ring; collect by passing through them.
  4. Hazards (spike arcs) rotate on the ring; touch = game over.
  5. Combo: consecutive gem collects without flipping multiply score (×1 → ×2 → ×3… cap ×5).
  6. Escalation: angular speed increases every 5 gems collected.
  7. Session: ~30–90 seconds; high score persists to leaderboard.

Architecture

games/orbit-snatch/index/
├── funday-plugin.json
├── assets/thumbnail.svg
├── vitest.config.ts
├── package.json
└── src/
    ├── Game.svelte          # Platform bridge (hostUpdate)
    ├── components/
    │   └── OrbitArena.svelte # Render + rAF loop
    └── lib/
        ├── orbitEngine.ts    # Pure logic (TDD)
        ├── orbitEngine.test.ts
        └── audio.ts          # Procedural Web Audio
  • No Nakama match handlermaxPlayers: 1, client-authoritative score like peng.
  • hostUpdate reports { state, score, combo } to play-shell HUD.
  • DaisyUI theme tokens (oklch(var(--p))) for platform theme sync.

Data Flow

Input (tap/space) → flipDirection(engine)
rAF tick → stepEngine(state, dt) → collisions → spawn entities
OrbitArena → hostUpdate({ state, score, combo })
Game over → hostUpdate({ state: 'finished', score, outcome: 'lose' })

Error Handling

  • Audio init wrapped in try/catch (autoplay policy).
  • Game loop cancels on component destroy.
  • Engine is pure — no DOM side effects in tests.

Testing Strategy

  • Unit: orbitEngine.test.ts — direction flip, gem collect, hazard hit, combo math, speed ramp.
  • Manifest: npm run validate:manifests from frontend.
  • Build boundary: check-game-boundaries.mjs passes with relative ./ imports from src/Game.svelte.

Success Criteria

  • Playable in /play/orbit-snatch via native mount
  • One-button controls work on desktop + touch
  • Combo + speed escalation feel addictive in first 60s
  • All engine unit tests pass
  • Manifest validates

Orbit Snatch — Implementation Plan

Date: 2026-05-28
Design: 2026-05-28-orbit-snatch-design.md

Task Breakdown

Task 1: Pure engine + TDD (no UI)

Files: src/lib/orbitEngine.ts, src/lib/orbitEngine.test.ts, vitest.config.ts, package.json

  1. Write failing tests for:
    • flipDirection toggles sign
    • Gem collection when angles align within threshold
    • Hazard collision ends run
    • Combo increments on no-flip streak; resets on flip
    • Speed increases every GEMS_PER_SPEED_TIER gems
  2. Implement engine until green.

Task 2: Platform shell

Files: funday-plugin.json, assets/thumbnail.svg, src/Game.svelte

  1. Manifest: svelte-component, maxPlayers: 1, entry src/Game.svelte
  2. Game.svelte wires hostUpdate, mounts OrbitArena

Task 3: Arena + juice

Files: src/components/OrbitArena.svelte, src/lib/audio.ts

  1. SVG ring arena with player dot, gems, hazard arcs
  2. rAF loop calling stepEngine
  3. Procedural collect/hit/flip sounds (no 'noise' oscillator — use buffer)
  4. Screen pulse on collect; shake on death

Task 4: Verification

  1. cd games/orbit-snatch/index && npm test
  2. cd frontend && npm run validate:manifests
  3. Spot-check svelte-check on Game.svelte if needed

Checkpoints

#GateCommand
1Engine tests passnpm test in game dir
2Manifest validvalidate:manifests
3No boundary violationsincluded in frontend build check

Out of Scope (YAGNI)

  • Nakama match handler
  • Multiplayer lobby
  • 3D / Threlte (2D SVG sufficient for arcade)
  • IndexedDB persistence (leaderboard via platform on game over)

Orbit Snatch — Fun Improvement Plan

REQUIRED SUB-SKILL: Implement task-by-task. After all tasks → fun-verify.

Goal: Make each run teach the next better move, make earned chains feel visible, and make intentional flips reliably feel more valuable than passivity.
Baseline: Fun Stack avg 3.0/5 (dev/docs/content/games/orbit-snatch/audits/orbit-snatch-fun-audit.md) Target: avg >= 3.5/5, zero unresolved red killers
Date: 2026-05-28
Audit: dev/docs/content/games/orbit-snatch/audits/orbit-snatch-fun-audit.md

Audit Inputs

Intent: Hard Fun + Sensory Fun, 30-90s arcade burst, solo + MP. Promise: one-button mastery panic where the player flips at the perfect moment, snatches gems, dodges spikes, and chases one more best.

Anti-goals: No grind, no unfair deaths, no confusion, no griefing or toxic MP incentives.

Baseline Fun Stack: L1-L8 = 4, 5, 3, 2, 2, 3, 2, 3. Average 3.0/5.

Core loop gaps: Feedback weak, Learning broken, Reward weak.

Top findings in scope:

PriorityFindingSeverityLoop Link
12.5Failure does not teach; death screen lacks cause and next-action lessonRedLearning broken, Feedback weak
8.0Combo/peak payoff is underexposed in observed playYellowReward weak, Feedback weak
6.7Skill/agency is opaque; no-input run beat active flippingRedAgency weak, Learning broken

Implementation order follows the audit’s practical sequence: failure lesson, combo payoff, then agency tuning.


Finding 1: Failure Does Not Teach 🔴

Loop link / killer: Learning broken; Feedback weak; player feels stupid; player may quit after failing.
Approved approach: Pending user approval. Recommended: A.

Hypothesis table

ApproachChangePsychologyEffortRisk
A (recommended)Track last hazard collision, highlight it on death, and show a one-line next-action lesson.Converts “I died somehow” into “I know what hit me and what to try.” Directly fixes the broken Learning link with small UI and engine data.MediumRequires shared state/type change across solo and MP; must keep payload compact.
BAdd a short replay ghost of the final second.Strongest teaching signal and memorable failure moment.HighMore state history, animation complexity, and multiplayer sync risk than needed for first fix.
CAdd only generic failure copy: “Dodge red spikes; flip earlier.”Very small diff and almost no engine risk.LowMay still feel vague because it does not point at the actual hazard or timing mistake.

Tasks

Task 1.1: Add Death Cause Data

Files:

  • Modify: games/orbit-snatch/index/shared/types.ts
  • Modify: games/orbit-snatch/index/shared/orbitEngine.ts
  • Modify: games/orbit-snatch/index/src/lib/orbitEngine.test.ts

Feel Outcome: Competence improves because the game can name what ended the run instead of presenting an unexplained “Orbit Broken.”
Verify: Unit test proves hazard collision stores the eliminated user, hazard id or angle, and player angle.
Implementation skill: game engine TDD.

  • Add a compact lastElimination or deathRecap field to ArenaState.
  • Populate it in checkHazards when a hazard eliminates a player.
  • Assert solo hazard death exposes the recap in orbitEngine.test.ts.

Task 1.2: Surface Death Recap

Files:

  • Modify: games/orbit-snatch/index/src/components/OrbitArena.svelte

Feel Outcome: Learning improves because the failure overlay gives cause plus next-action guidance.
Verify: Browser playtest death overlay shows a concrete lesson such as “Spike clipped you near the top-left. Flip earlier or coast past the gap.”
Implementation skill: critique + svelte-code-writer.

  • Derive a short direction label from the death recap angle.
  • Add a one-line cause below Orbit Broken.
  • Add a one-line next-action lesson that stays concise on small screens.

Task 1.3: Highlight The Killing Hazard

Files:

  • Modify: games/orbit-snatch/index/src/components/OrbitArena.svelte

Feel Outcome: Feedback improves because the player can visually connect the death text to the arena threat.
Verify: On death, the responsible red arc visibly pulses or remains emphasized for the overlay state.
Implementation skill: animate + svelte-code-writer.

  • Detect the recap hazard in drawFrame.
  • Draw a brief death highlight ring or brighter arc when phase === 'finished'.
  • Respect prefersReducedMotion by using static emphasis when reduced motion is enabled.

Task 1.4: Keep MP Elimination Consistent

Files:

  • Modify: games/orbit-snatch/index/shared/types.ts
  • Modify: games/orbit-snatch/index/server/match_handler.ts
  • Modify: games/orbit-snatch/index/src/components/OrbitArena.svelte

Feel Outcome: Social/Hard Fun stays fair because multiplayer players receive the same teachable elimination signal without new toxic language.
Verify: MP state payload still broadcasts and local eliminated player sees the recap.
Implementation skill: funday-play-shell + game engine TDD.

  • Reuse the shared recap from authoritative state broadcasts.
  • Avoid adding chatty or blame-oriented copy for opponent eliminations.
  • Run solo tests first, then route-level MP smoke after implementation.

Finding 2: Combo/Peak Payoff Is Underexposed 🟡

Loop link / killer: Reward weak; Feedback weak; player wins or scores but feels little peak.
Approved approach: Pending user approval. Recommended: A.

Hypothesis table

ApproachChangePsychologyEffortRisk
A (recommended)Add combo tier copy, stronger collect burst for combo 3+, and end-run best-chain summary.Makes mastery visible exactly where the current loop already has combo math. Smallest diff with high sensory payoff.Low-MediumNeeds a new stat field or local tracking for best chain.
BAdd rare power-up gems with screen-clearing payoff.Creates bigger peaks and strategic variety.Medium-HighNew mechanics may distract from fixing current reward clarity.
CAdd only louder sounds and bigger particles.Quick sensory lift.LowRisks decoration without teaching why combos matter.

Tasks

Task 2.1: Track Best Chain

Files:

  • Modify: games/orbit-snatch/index/shared/types.ts
  • Modify: games/orbit-snatch/index/shared/orbitEngine.ts
  • Modify: games/orbit-snatch/index/src/lib/orbitEngine.test.ts

Feel Outcome: Meaning and Memory improve because the run can summarize the player’s best mastery moment, not just score.
Verify: Unit test proves consecutive collects update bestCombo or equivalent, and flip resets current combo without losing best chain.
Implementation skill: game engine TDD.

  • Add a persisted per-player best combo field.
  • Update it when collectGems increments combo.
  • Add tests for best combo preservation after a flip.

Task 2.2: Add Combo Tier Feedback

Files:

  • Modify: games/orbit-snatch/index/src/components/OrbitArena.svelte
  • Modify: games/orbit-snatch/index/src/lib/audio.ts
  • Modify: games/orbit-snatch/index/src/lib/particles.ts

Feel Outcome: Sensory Fun improves because combo 3+ becomes a noticeable “I did it” moment.
Verify: Playtest shows combo tier 3+ with visible text/particle/audio escalation, without drowning out normal collects.
Implementation skill: delight + animate + svelte-code-writer.

  • Add a short tier label for combo 3+ near the HUD.
  • Scale particle count or speed based on combo tier.
  • Make playCollect pitch or secondary tone clearly escalate at combo 3+ and 5+.

Task 2.3: Show Run Summary Payoff

Files:

  • Modify: games/orbit-snatch/index/src/components/OrbitArena.svelte

Feel Outcome: Reward and Memory improve because the final overlay reminds the player of the best chain they created.
Verify: Death overlay includes “Best Chain xN” when N > 1, with score and best still readable.
Implementation skill: critique + svelte-code-writer.

  • Add best-chain text to the finished overlay.
  • Keep the overlay hierarchy: outcome, score/best, best chain, lesson, Again.
  • Confirm mobile-sized arena still fits without covering the button.

Task 2.4: Preserve Reward Signal In Host Updates

Files:

  • Modify: games/orbit-snatch/index/src/components/OrbitArena.svelte

Feel Outcome: Platform HUD/dock can reflect combo state if needed, keeping reward feedback consistent outside the canvas.
Verify: hostUpdate includes the current combo and best chain after collect and finish.
Implementation skill: funday-play-shell.

  • Extend report() with best-chain data if available.
  • Confirm existing score, combo, mode, and timeLeft fields remain unchanged.
  • Do not add a new platform API unless current hostUpdate can carry the data.

Finding 3: Skill/Agency Is Opaque 🔴

Loop link / killer: Agency weak; Learning broken; no-input strategy outperformed active flipping in observed runs.
Approved approach: Pending user approval. Recommended: A.

Hypothesis table

ApproachChangePsychologyEffortRisk
A (recommended)Add first-run flip timing coaching, near-hazard telegraph, and a small engine test that intentional pathing can beat passivity in a seeded scenario.Makes the one button feel learnable and proves active play can win a representative setup.MediumRequires careful tuning so coaching does not over-explain or make the game too easy.
BChange combo design so flipping never resets combo; reward every active flip.Makes input feel less punishing immediately.MediumUndercuts the original risk/reward premise: “consecutive gems without flipping multiply score.”
CDelay hazards heavily in the first 10 seconds.Reduces early frustration and gives room to learn.LowCould preserve the inverted strategy if doing nothing still scores too well.

Tasks

Task 3.1: Prove Active Pathing Scenario

Files:

  • Modify: games/orbit-snatch/index/src/lib/orbitEngine.test.ts
  • Modify: games/orbit-snatch/index/shared/orbitEngine.ts only if the test exposes a tuning gap.

Feel Outcome: Agency improves because implementation is anchored to a falsifiable rule: timely flips should beat passivity in a representative opening.
Verify: A deterministic test compares no-flip vs timed-flip score or survival in a seeded setup.
Implementation skill: game engine TDD.

  • Create a small deterministic scenario with one gem and one hazard.
  • Simulate no-input and a timed flip.
  • Assert the timed flip survives longer or scores better.

Task 3.2: Add First-Run Timing Hint

Files:

  • Modify: games/orbit-snatch/index/src/components/OrbitArena.svelte

Feel Outcome: Clarity and Competence improve because the player gets a timing model before the first punishing failure.
Verify: First practice run shows a concise hint such as “Flip to dodge red; coast through gems for chains,” then fades or hides after first score/death.
Implementation skill: critique + svelte-code-writer.

  • Track whether the player has scored or died in the current run.
  • Show the hint only during early play.
  • Keep menu copy unchanged unless the in-run hint makes it redundant.

Task 3.3: Telegraph Near-Hazard Danger

Files:

  • Modify: games/orbit-snatch/index/src/components/OrbitArena.svelte

Feel Outcome: Responsiveness and Agency improve because the player sees when their current path is becoming dangerous before impact.
Verify: When the local player is approaching a hazard within a tight angle window, the hazard or player cue warns without guaranteeing survival.
Implementation skill: animate + svelte-code-writer.

  • Compute a local near-hazard warning from existing arena.hazards and me.angle.
  • Add a subtle warning cue to drawFrame or HUD.
  • Disable flashing motion under reduced-motion preference.

Task 3.4: Tune Early Passive Scoring If Needed

Files:

  • Modify: games/orbit-snatch/index/shared/orbitEngine.ts
  • Modify: games/orbit-snatch/index/src/lib/orbitEngine.test.ts

Feel Outcome: Hard Fun improves because the player believes their input matters; doing nothing should not reliably outperform active learning.
Verify: After the test and playtest, no-input novice run no longer clearly beats a basic timed-flip attempt.
Implementation skill: game engine TDD.

  • Only tune spawn timing, collect threshold, hazard timing, or speed if Task 3.1/playtest proves passivity still dominates.
  • Keep changes minimal and measurable.
  • Re-run the full game test suite after any tuning.

Backlog (Out Of Scope)

FindingPriorityDefer reason
Full multiplayer Social Fun auditUnknownAudit did not include a full 2+ session MP run; needs separate evidence.
Telemetry for death reason, time-to-first-score, flip count, combo distribution, retry countUsefulAdds measurement depth but is not required for the first feel fix.
Hydration/perceived loading polishYellowBlank shell resolved after hydration and is not one of the top feel blockers.
Full a11y/performance/responsiveness auditSeparate quality gateRun after feel blockers are addressed so it audits the final interaction shape.
Replay ghostHigh-value future teachingDeferred because Approach A fixes the broken Learning link with less state and animation complexity.
New power-up gemsPotential peak featureDeferred to avoid adding mechanics before current combo reward is legible.

fun-verify checklist (pre-filled)

CheckBaselineTargetMethod
Fun Stack avg3.0/5>= 3.5/53-persona playtest: novice no-input, rapid flips, competent rhythm
Competence2/5>= 3/5Failure overlay explains cause and next action; active player can state what to try next
Agency2/5>= 3/5Timed-flip attempt beats or clearly improves over no-input in seeded/playtest evidence
FeedbackWeakPassFlip, hazard threat, combo tier, and death cause are visible or audible
RewardWeakPassCombo tier 3+ creates a visible peak; end overlay shows best chain
Red killers3 observed/risk0 unresolvedKiller sweep after playtest
Peak momentNot visible in active runsStrongerPlayer can identify a best combo or clutch dodge moment

Verification record (fill after fun-verify)

Date:
Result: PASS | FAIL
Stack avg: 3.0 →
Evidence:

Historical intent versus release outcome

This document serves as a historical record of the original 2026-05-28 intent. As of the 2.0.1 release, the focus is on exact geometry, local sector clarity, a 600 ms capability-gated arming delay, and a 300 ms frozen culprit frame. Certain features like broader onboarding or new game design mechanics remain unresolved hypotheses.

Ask Docs

AI assistant to help answer questions about the documentation. Answers are read-only and cite docs/source.

Hi! How can I help you with the documentation today? Answers are read-only and cite docs/source.

Ctrl+Enter to send