> For the complete documentation index, see [llms.txt](https://docs.kouch.dev/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.kouch.dev/docs/feature_plan.md).

# Kouch — Feature Plan

> **Since 2026-09-30 (ADR 0026):** Kouch seats at most 8 players, every one with motion, on two DSU servers (26760–26761; seats 1–8 keep their ports). The 16-player figures in this document are historical.

> Working name: **Kouch** (trademark + store checks pending). Solo dev with help from friends. Steam app built on Steam Input.
>
> This is the owner's product plan as written before implementation started, with brand terms replaced by the neutral wording used everywhere else in the repo. Where the implementation plan in `ARCHITECTURE.md` decided differently (2026-09-17), the change is called out in a **Decision:** note. `ARCHITECTURE.md` wins on conflicts.

## Overview

A Steam app that acts as an emulator frontend and controller hub: up to 16 players (motion for up to 8), custom player ordering, Steam and Discord invites for netplay, cloud save sync, phone controllers, and community content through Steam Workshop.

**Ground rules**

* No excluded-platform emulators, names, or imagery anywhere (store page, app, marketing, code, docs).
* Never ship or transfer ROMs, BIOS, firmware, or keys.
* Workshop content is **data only** unless sandboxed.
* Emulators users install themselves are the default; officially approved emulators come later.

***

## Pricing & Release

| Stage                   | Price | Notes                                          |
| ----------------------- | ----- | ---------------------------------------------- |
| Early Access            | $3.00 | Price increase stated in writing in the EA FAQ |
| Full release (≈3 years) | $5.49 | Save a big feature for the 1.0 launch boost    |

* Optional Ko-fi / supporter link (no perks outside Steam payments).
* Regional pricing adjusted manually (LATAM especially).
* Phone controller app is **free**.
* Public roadmap and devlogs for every milestone.

***

## 1. Controller Input (Core)

**Steam Input as the input source**

* Read all controllers through `ISteamInput` (16 max, which matches the player cap exactly).
* Buttons, sticks, triggers, gyro, and accelerometer per controller handle.
* Raw gamepad-style action set so every input is forwarded.
* Tight polling loop (`RunFrame()` before reads) for low latency.
* Steam Input must stay enabled for the app in Steamworks config.

**Player slots (1–16, motion capped at 8)**

* App is the single source of truth for player order; Steam's reorder screen is not relied on (unreliable for Steam Input API apps).
* Claim-a-slot screen: press a button to join, LED color + rumble confirms slot.
* Persistent assignments keyed to device identity, restored on reconnect.
* Slot reserved temporarily when a controller disconnects.
* Live reordering mid-game without restarting the emulator.
* Optional toggle: "Use Steam's controller order" (only if testing shows it works).
* Motion slots assigned separately from player slots: any 8 of the 16 players can have motion (defaults to P1–8; users can reassign, e.g. give motion to P9 instead of P2).
* Players without a motion slot still get full buttons, sticks, and rumble.
* Claim screen shows a motion icon on slots that have gyro active and a "motion full" notice when all 8 are taken.

**Output / rerouting**

* DSU (Cemuhook) servers for motion slots. **Decision:** four DSU servers (26760–26763, 4 slots each = 16 players) are the *primary* output for buttons, sticks and motion on both OSes; all 16 slots always exist so the emulator's port mapping never shifts. See `ARCHITECTURE.md` "DSU servers".
* Virtual gamepads for buttons/sticks/triggers: `uinput` on Linux/Steam Deck; a bus driver on Windows (ViGEmBus is archived, its successor is commercial). **Decision:** virtual pads exist for rumble and for emulators without a DSU button client. Linux uinput with force feedback is real Phase 2 work; Windows ViGEmBus is an opt-in per-profile backend, never required to run Kouch.
* Avoid Xbox/XInput-style virtual pads for players 5+: XInput only exposes 4 slots.
* Per-emulator player caps respected by profiles; extra players can spectate or rotate in.
* Emulator profiles bind each player to a specific virtual device / DSU slot to avoid double inputs.
* Optional HidHide integration on Windows to hide physical devices. **Decision:** dropped from Phase 1–2; profiles instead disable the emulator's native gamepad backend.
* Emulators launched from the app so Steam Input stays tied to the app's session.

**Hardware for 16 players**

* Plan roughly 3–4 Bluetooth adapters (about 4–5 controllers each) or mix in wired controllers and phones.
* USB hubs with their own power for wired setups.

**Must-test early**

* Input keeps flowing while the emulator window has focus.
* `GetConnectedControllers()` order behavior after reconnects.

***

## 2. Split-pair controller support

**Native-style grip screen**

* **L + R across two halves** → combined pair (one player).
* **SL + SR on one half** → sideways single.
* **Button press held upright** → vertical single.
* On-screen icons showing orientation and player number.
* Long press to remove a half from a slot.

**Per-mode handling**

* Sideways single: rotate stick 90°, remap face buttons, SL/SR as shoulders, rotate gyro axes.
* Pair: merge into one virtual gamepad; motion from right, left, or both (two DSU slots).
* Vertical single: native layout, own gyro.
* App handles pairing itself (users keep the halves separate in Steam).

**Per-game rules**

* Profiles declare allowed modes (sideways OK / pairs required / one per player with motion).
* Grip screen shown automatically at launch if the current setup doesn't fit.

**Polish**

* Player LEDs matching slot number (exposed by Steam Input).
* Rumble on slot claim.
* Gyro recalibration button.
* Mixed setups with pairs, singles, pro-style controllers, and phones in the same session (up to 16).
* Pairs using both gyros count as 2 motion slots.

> Note: the Steamworks SDK marks the pair/single device-type values as unused, so Phase 3 detection will need HID (VID/PID) rather than `GetInputTypeForHandle`.

***

## 2b. Motion remote support (added 2026-09-19)

The classic Bluetooth motion remote — the pointing/motion controller that the fifth-generation-console emulators are built around — is the one popular controller **neither Steam Input nor SDL3 supports**, so Kouch has to own its whole stack. Kouch's name for it is `MotionRemote` ("motion remote"; the pointing attachment is the "stick attachment", the gyro add-on the "motion extension").

**Input (a third `InputSource` in `kouch-input`: `HidSource` over `hidapi`, the Phase 8 raw-HID work pulled forward)**

* Raw HID reports in → `ControllerFrame` out, same as `LanSource`, so seating, LEDs, rumble and DSU output need no changes.
* Buttons, accelerometer, the four player LEDs (= slot colour position), rumble; the stick attachment (stick + two buttons + its own accelerometer); the motion extension (gyro) → a motion slot like any other pad.
* The protocol is public and stable (status/data reporting modes, extension decoding, IR camera init); no console material of any kind is involved.

**Pointer**

* IR pointing needs a sensor bar (or any two IR sources) — hardware the user provides; Kouch shows a "point at the screen" calibration step.
* DSU carries no pointer, so the pointer goes out as an **absolute mouse** through the existing `VirtualPad` trait (Linux uinput; Windows `SendInput`), per-profile: emulators that emulate this controller natively take a mouse for the cursor; others ignore it.

**Pairing**

* "Connect a remote" from the Players screen / Quick Menu: press the remote's sync button, Kouch drives the OS pairing (BlueZ on Deck/Linux, the Windows BT API), the LEDs confirm the seat.
* **Deck/Linux first** (the kernel driver already exposes it well); Windows best-effort — its Bluetooth stack is known to drop this controller between sessions, so the Windows path ships with a "re-pair" shortcut and clear messaging rather than a promise.

**Estimate:** \~3–4 weeks for a solid version (1–2 input, 1 pointer + calibration, 3–5 days pairing UI). Needs one remote, the stick attachment, the motion extension and a sensor bar on the owner's desk. Sequenced after the Steam app id lands (Phase 3, alongside split-pair, since both need the HID source).

***

## 3. HD Rumble & Haptics

* Default: Steam Input rumble (`TriggerVibration`, `TriggerVibrationExtended`, `TriggerSimpleHapticEvent`).
* DualSense adaptive triggers via `SetDualSenseTriggerEffect`.
* **Optional enhanced mode:** direct HID output for true HD rumble.
  * Split-pair / pro-style pads: output report `0x10` (amplitude + frequency pairs) via hidapi.
  * DualSense haptics via audio channels (later, USB-focused).
* Test for conflicts with Steam's own reports; opt-in per controller with a warning if needed.
* Priorities:
  1. UI haptics (slot claims, menu ticks, connection feedback).
  2. "Enhanced rumble" community profiles translating two-motor rumble into HD patterns per game.

***

## 4. Frontend

* Game library scanning the user's own folders.
* Emulators set manually by the user (path detection + manual picker).
* Per-system / per-game launch arguments.
* Full controller navigation.
* ES-DE was considered as a base. **Decision:** not forked; Kouch has its own theme format and a paged tile-grid launcher (`DESIGN.md`).
* Local-only emulator profiles (no downloads required).

**Add games to the Steam library (non-Steam shortcuts)**

* Each game in the user's library can be exported as its own tile in Steam (Big Picture + Deck Game Mode).
* Shortcut target launches **through the app**, not the emulator directly: `steam://run/<appid>//--launch=<game-id>`, so Steam Input, the overlay, player slots, and sync all run under the app's session.
* App receives the argument, applies the game's profile (grip rules, phone layout, motion slots), launches the emulator, and returns to Steam on quit.
* Written by the **external GitHub companion tool**, not the Steam build: requires editing Steam's `shortcuts.vdf`, which only works with Steam closed and has no official API (same approach as Steam ROM Manager).
* Optional artwork from SteamGridDB (user supplies their own API key; no bundled game art).
* Remove / re-sync shortcuts when games are deleted or renamed.
* Shortcut names come from the user's own files; the app never ships game names, art, or presets for specific titles.

**Linux / Steam Deck specifics**

* Native Linux build of the app (not Windows via Proton), since uinput, HID, and gamescope overlay need native access.
* `shortcuts.vdf` at `~/.local/share/Steam/userdata/<id>/config/shortcuts.vdf`; Flatpak Steam uses `~/.var/app/com.valvesoftware.Steam/...`. Handle multiple Steam accounts (one `userdata` folder each).
* Artwork goes in `userdata/<id>/config/grid/`, named by the shortcut's generated app ID.
* Companion tool runs in **Desktop Mode**: shuts Steam down, writes shortcuts, restarts Steam.
* Game Mode option: a **Decky Loader plugin** that adds/removes shortcuts live without restarting Steam (unofficial, can break on Steam updates).
* Shortcut target: `xdg-open` / `steam` with the `steam://run/<appid>//--launch=<game-id>` URL.
* Test how Game Mode handles a shortcut that immediately hands off to another Steam app (possible "game closed" flicker or focus weirdness).
* Flatpak emulators (common on Deck via Discover): launch with `flatpak run <id>`; save paths live under `~/.var/app/<id>/` for cloud sync mapping.
* Avoid or work around containerized Steam Linux Runtime: launching host/Flatpak emulators from inside the container can fail.
* `/dev/uinput` access: SteamOS already allows it; other distros may need the `steam-devices` udev rules.
* Gamescope external overlay for the Quick Menu in Game Mode.

***

## 5. Emulator Distribution

**Default: bring your own emulator**

* App ships with nothing; detects installed emulators or lets users pick a path.

**Custom URL scheme**

* `kouch://add-emulator?name=...&path=...&profile=...` (branded scheme name).
* Registered via registry on Windows, `.desktop` handler on Linux/Deck.
* Passes to the running instance or relaunches through `steam://run/<appid>`.
* Local file fallback for Deck Game Mode.
* Security: confirmation dialog, no launch args from URLs, path validation (no traversal / UNC), show file path + SHA-256, length caps.

**External GitHub updater (separate from Steam app)**

> Superseded 2026-09-18 by `docs/adr/0006-in-app-emulator-updater.md`: the updater lives in Kouch, driven by user-imported community profiles, nothing bundled or on by default. The safeguards below carry over.

* Community-maintained, separate org, not promoted inside the Steam app.
* Downloads from official release pages only (no rehosting).
* User-imported sources, nothing enabled by default.
* Signed manifests, pinned keys, SHA-256 verification, HTTPS only, no cross-domain redirects.
* Staged updates with rollback, never replaces a running emulator, changelog before updating.
* Remote blocklist for malicious or DMCA'd sources.
* Maintainer 2FA and branch protection.

**Later: RetroArch-style official emulators (non-excluded platforms only)**

* Free DLC for emulators whose devs approve Steam distribution.
* Workshop items uploaded by official devs, verified by SteamID allowlist + signed binaries.
* Written approval from maintainers required.
* License checks (GPL source links; avoid non-commercial/no-redistribution licenses unless explicitly allowed).
* No BIOS/firmware bundled; setup points to user's own files.
* First targets: PPSSPP, PCSX2. Then DOSBox, ScummVM, MAME, Flycast, RPCS3, xemu, Xenia.
* Pitch devs after Early Access has an install base.

***

## 6. Netplay & Online

* **Lobbies** via `ISteamMatchmaking` (game, emulator, up to 16 slots + motion slot availability as lobby data).
* **Invites + Rich Presence** via `ISteamFriends` ("Playing X, 3/4 slots" + Join).
* **Networking** via `ISteamNetworkingSockets` + Steam Datagram Relay (no own servers, no port forwarding).
* **Local UDP proxy:** emulator connects to `127.0.0.1`, traffic tunneled over Steam relay.
* File hash + emulator version check before launching. **No ROM transfers.**
* Per-emulator netplay adapters (pluggable, community-extendable).
* Investigate Remote Play Together as a bonus mode.

**Discord integration (Discord Social SDK)**

* Rich Presence: "Playing \[game] on \[system], 3/16 players" with party size.
* Game invites powered by Rich Presence party info + a join secret.
* All three join paths: invite from inside the app, invite from the Discord client (DMs/servers), and join requests from a player's activity card (host accepts in-app or in Discord).
* Join secret maps to the Steam lobby; traffic still goes over Steam Datagram Relay. Discord is only the invite layer.
* Joiners still need the app on Steam; launch through Steam via `RegisterLaunchSteamApplication` so invites open the Steam app correctly.
* Ephemeral, single-session join secrets (never raw Steam lobby IDs or IPs).
* Account linking optional (only needed for in-app Discord friends list/DMs later).
* Privacy toggles: hide game title in presence, show only "Playing \[app]", or presence off entirely.
* Custom invite image + 1024×1024 Rich Presence assets (no console or game art).

**Universal invite links / codes**

* Short invite codes (e.g. `K7F-92Q`) and `kouch://join?code=...` links shareable anywhere (WhatsApp, text, Telegram).
* Optional web landing page that opens the app or points to the Steam store page.
* Codes expire, are single-lobby, and can be revoked by the host.
* Host approval option for anyone joining by code.
* Same URL handler hardening as the emulator scheme (confirmation, validation, no args).

***

## 7. Cloud Save Sync

* `ISteamRemoteStorage` with user-mapped save folders per system/emulator.
* Sync after the game closes (never while running).
* Timestamps, conflict prompts, local backups.
* Save states synced with a version-compatibility warning; in-game saves prioritized.
* Saves only — never ROMs, BIOS, or keys.

***

## 8. Phone Controllers (Native App)

**Platforms**

* Native iOS + Android app (no browser version). Developer accounts on both already set up.
* Flutter (or KMP + SwiftUI) for a shared codebase.
* Beta testing through TestFlight and Play Console internal testing.

**Connection**

* Auto-discovery via mDNS/Bonjour; QR code pairing as backup.
* Pairing key exchanged once; encrypted connection (Noise / DTLS).
* PC approves each new phone; local network only by default.
* Raw UDP for low latency; latency indicator; 5GHz recommended.

**Controller features**

* Phones join as any player slot (fills 9–16 in party setups); phone gyro uses a motion slot only if one is free.
* Gyro, touch, buttons, sticks, tilt, swipe gestures.
* Haptics: Core Haptics (iOS), `VibrationEffect` (Android).
* Screen kept awake; orientation locked per layout.

**Community layouts (Workshop)**

* JSON layouts: element types, positions, sizes, opacity, background image, gyro/tilt options.
* Elements map to virtual controller inputs.
* Auto-selected per game (emulator + game hash/title ID), user overrides + re-upload.
* Workshop voting surfaces best layouts.
* **Data only — no scripts.** Fixed set of built-in element types.
* PC app pulls layouts from Workshop and pushes them to the phone (no Steam login on mobile).

**Store review**

* Demo mode with sample layout for reviewers.
* No emulator names or excluded-platform imagery in screenshots.

**Companion screen mode (later)**

* Stream a chosen window to the phone with touch mapped back as clicks.
* Window capture (Windows Graphics Capture / PipeWire) + hardware H.264/HEVC.
* Generic "second screen" feature; local network only; low-res mode for Deck.
* Marketed generically, no console references.

***

## 9. Steam Workshop & Plugins

* **Themes** (data: JSON tokens, images, sounds) — first. **Decision:** themes override design tokens only, never CSS selectors or scripts (`DESIGN.md` §10).
* **Emulator profiles** (launch args, configs, netplay adapters).
* **Phone controller layouts.**
* **Enhanced rumble profiles.**
* **Code plugins** last: sandboxed Lua or WASM, limited API (controller state, remapping, UI), no filesystem/network access.
* Versioned plugin API from day one.
* Moderation for anything containing keys, firmware, ROMs, or downloaders.

***

## 10. Security Summary

* URL handler hardening (confirmations, validation, no args).
* Signed + hashed downloads, pinned keys, rollback, blocklists.
* Encrypted phone pairing, per-device approval.
* Workshop content data-only; plugins sandboxed.
* Account security for all maintainers (2FA, branch protection).

***

## 11. Quick Menu Overlay

**Opening it**

* Controller combo (default: hold Select + Start \~1s, configurable per player or host-only). **Decision:** plus a bindable "Open quick menu" Steam Input action.
* Keyboard hotkey on desktop. Avoid Shift+Tab (Steam overlay) and the Guide/Home button (Steam grabs it).
* Phone controllers get a dedicated menu button.

**While open**

* Input to the emulator is frozen at neutral so menu navigation doesn't leak into the game.
* Emulator paused (per-profile pause hotkey, or process suspend as fallback). No pausing during netplay; overlay shows "netplay active" instead.

**Features**

* **Player list:** all 16 slots with controller type icon, player LED color, motion icon (8 max), grip mode for split pairs, connection type (USB/Bluetooth/phone/netplay).
* **Battery** where available (phones for sure; controllers depending on what Steam Input / HID exposes).
* **Reorder players** live (drag or "press to swap"), same logic as the claim screen.
* **Assign motion slots** and see which 8 are active.
* **Change split-pair grip** without leaving the game.
* **Connect a phone:** QR code + pairing approval right in the overlay.
* **Invite friends:** Steam and Discord invites, show or copy invite code.
* **Netplay status:** players, ping per player, kick/approve joins (host only).
* **Game actions (per emulator profile):** save state, load state, reset, screenshot.
* **Quit:** graceful close first, force-kill fallback after a timeout, then back to the frontend. Cloud sync runs after exit.
* **Quick controller tools:** rumble test, gyro recalibration, disconnect a controller.

**Tech**

* Windows: transparent topmost overlay window. Works over borderless/windowed; exclusive fullscreen breaks it, so emulator profiles default to borderless.
* Steam Deck Game Mode / gamescope: use gamescope's external overlay support (`GAMESCOPE_EXTERNAL_OVERLAY`); test early.
* Linux desktop: compositor overlay window (X11/Wayland differences need testing).
* Fully controller-navigable, big text for TV distance, same theme system as the frontend (Workshop themes apply).
* Permissions: host-only actions (quit, kick, netplay settings) vs. any-player actions (own slot, own phone, own grip).

***

## Roadmap

| Phase                       | Focus                                                                                                                                                                                             |
| --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **1 — Early Access launch** | Steam Input → 16 controllers (8 with motion), four DSU servers, emulator paths, claim-a-slot screen                                                                                               |
| **2**                       | Frontend basics: library scanning, launching, controller navigation, Quick Menu overlay (player list, reorder, quit); rumble via DSU extension + Linux uinput pads; opt-in Windows virtual pads   |
| **3**                       | Split-pair grip screen & modes; **motion remote** over raw HID (buttons/accel/stick attachment/motion extension, IR pointer as a virtual mouse, in-app pairing — Deck first, Windows best-effort) |
| **4**                       | Phone controller app + community layouts, phone pairing from the overlay                                                                                                                          |
| **5**                       | Netplay with Steam lobbies, invites, relay proxy, Discord invites + Rich Presence, invite codes/links                                                                                             |
| **6**                       | Cloud save sync                                                                                                                                                                                   |
| **7**                       | Workshop themes & profiles                                                                                                                                                                        |
| **8**                       | HD rumble enhanced mode                                                                                                                                                                           |
| **9**                       | Official emulator DLC / verified Workshop emulators                                                                                                                                               |
| **10 — 1.0**                | Sandboxed plugins, companion screen mode, big launch feature                                                                                                                                      |

***

## Early Tests To Run

The executable versions of these live in `EARLY_TESTS.md` (run via `cargo run -p kouch-lab -- <test>`).

* [ ] Input keeps flowing when emulator window has focus — **blocked** until Kouch has its own Steam app id (see `EARLY_TESTS.md` row 0)
* [ ] `GetConnectedControllers()` order + reconnect behavior
* [ ] Whether Steam's Reorder Controllers affects the app at all
* [ ] Split-pair combined vs separate handle motion reporting
* [ ] Opening split-pair halves over HID while Steam Input is active (rumble conflicts)
* [ ] Double input when emulator sees both physical and virtual pads
* [ ] Remote Play Together capturing app-launched emulators
* [ ] 16 controllers connected at once: Bluetooth stability, polling latency, CPU usage
* [ ] Windows virtual pad driver with 16 devices
* [ ] Motion slot reassignment while a game is running
* [ ] Discord invite → Steam launch flow when the joiner's app is closed
* [ ] Discord join request accept/decline while the emulator has focus
* [ ] Overlay over borderless vs. exclusive fullscreen emulators (Windows)
* [ ] Overlay in Steam Deck Game Mode (gamescope)
* [ ] Pause + neutral input while overlay is open, per emulator
* [ ] Graceful quit vs. force-kill without corrupting saves
* [ ] Steam shortcut → `steam://run` launch keeps Steam Input under the app's session (not the shortcut)
* [ ] Deck Game Mode: shortcut hand-off to the app (flicker, focus, return to library)
* [ ] Launching Flatpak emulators from the app under the Steam Linux Runtime
* [ ] Decky plugin adding shortcuts live without a Steam restart

***

## Team

* **Core (solo):** input pipeline, sync, plugin API, security.
* **Friends:** themes, emulator launch configs, controller testing (Deck, split-pair pads, 8–16 controllers), store art.
* GitHub repo + issues as task board from day one.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.kouch.dev/docs/feature_plan.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
