ask-handoff

Duck Hunt Fire — Design Spec (arma + mira)

slug duck-hunt-fire-2026-11-02-gun-mira-design project duck-hunt-fire source cursor updated 11/2/2026, 11:42:30 PM

Duck Hunt Fire — Design Spec (v0: arma + mira)

Status: awaiting user review
Date: 2026-11-02
Scope v0: phone gun APK + Stick host APK with crosshair + static targets. No ducks/score/rounds yet.

Goal

Prove end-to-end: Android phone orientation + fire button moves a crosshair on Fire TV Stick over same Wi‑Fi (no internet). Measure feel/lag before building real Duck Hunt.

Architecture

[gun APK]  --WebSocket LAN-->  [tv APK on Fire Stick]
 SensorManager + big button     embeds HTTP+WS server
 discovers Stick (mDNS/NSD)     Canvas crosshair + static targets
 fallback: type IP / scan QR    serves /gun.apk for install
  • Runtime host: Fire Stick APK (Mac only for build + adb install).
  • Transport: WebSocket on LAN. No Bluetooth in v0.
  • No internet required after APKs are on devices.

Rejected simpler options

  • PWA on phone: DeviceOrientation often needs secure context; LAN HTTP breaks gyro.
  • Mac as permanent server: fine for debug; user wants stable Stick APK.
  • BLE first: pairing/Fire OS cost > benefit for living-room Wi‑Fi.

Apps

tv (Fire Stick)

  • Android TV / Leanback, fullscreen landscape.
  • Embedded local HTTP + WebSocket (Ktor preferred).
  • Renders: background, N static targets (circles), crosshair from latest aim.
  • On fire: hit-test crosshair vs targets; brief flash (hit/miss). No score UI required beyond flash.
  • Connect screen: show local IP, port, mDNS name, QR encoding http://<ip>:<port>/gun.apk and ws://<ip>:<port>/ws.
  • NSD/mDNS advertise service type e.g. _duckhunt._tcp.

gun (Android phone)

  • Minimal APK: full-screen fire button + status (searching / connected / lag).
  • Sensors: rotation vector / game rotation → yaw/pitch; map to normalized aim x,y ∈ [0,1] (calibrate “point at TV center” with a hold-to-calibrate button).
  • Send aim ~30–60 Hz; send fire on tap/down.
  • Discovery: NSD for _duckhunt._tcp; fallback manual IP; optional open install QR is TV-side only.

Protocol (WebSocket JSON)

Phone → TV:

{"t":"aim","x":0.52,"y":0.41,"ts":1234567890}
{"t":"fire","x":0.52,"y":0.41,"ts":1234567891}
{"t":"hello","role":"gun","v":1}

TV → Phone:

{"t":"hello","role":"tv","v":1}
{"t":"pong","ts":...}
{"t":"hit","ok":true}
  • Coordinates: origin top-left of game viewport; x,y normalized 0–1.
  • One gun client in v0 (second connection replaces or is rejected — pick reject extra).

Phone APK install (the “link”)

Primary (no Play Store, same Wi‑Fi):

  1. Install tv APK on Stick via Mac adb install.
  2. Open game on Stick → connect screen shows QR + URL like http://192.168.x.x:8787/gun.apk.
  3. Phone camera / browser opens that URL → downloads APK → “Install unknown apps” for the browser → install gun.
  4. Open gun → auto-discover Stick (or type IP from TV).

Dev fallback: Mac serves the same APK (python -m http.server or Gradle) at http://<mac-ip>:8787/gun.apk while iterating.

Not in v0: public internet link / Play Store / Firebase App Distribution (needs account + internet; optional later).

Stack

  • Single Gradle multi-module repo: :tv, :gun, optional :shared (protocol constants).
  • Kotlin, minSdk suitable for Fire OS 7 (Android 9) on tv; phone minSdk 26+.
  • TV rendering: Android Canvas/SurfaceView (no Unity).
  • Server: Ktor CIO on TV process.

v0 acceptance

  1. Stick shows connect UI with IP + QR for /gun.apk.
  2. Phone installs from that LAN link and connects over WS without internet.
  3. Tilting phone moves crosshair smoothly on TV (~30 Hz+).
  4. Fire button flashes hit/miss on static targets.
  5. Mac not required after both APKs installed.

Out of scope (later)

  • Flying ducks, waves, audio, multiplayer, Bluetooth, public download link.

Project folder

Suggested: ~/duck-hunt-fire (or ~/Projects/duck-hunt-fire if created). Confirm name before scaffold.