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.apkandws://<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
fireon 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,ynormalized 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):
- Install
tvAPK on Stick via Macadb install. - Open game on Stick → connect screen shows QR + URL like
http://192.168.x.x:8787/gun.apk. - Phone camera / browser opens that URL → downloads APK → “Install unknown apps” for the browser → install
gun. - 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
- Stick shows connect UI with IP + QR for
/gun.apk. - Phone installs from that LAN link and connects over WS without internet.
- Tilting phone moves crosshair smoothly on TV (~30 Hz+).
- Fire button flashes hit/miss on static targets.
- 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.