Drive an OBSBOT Tiny 3 with a game controller. Browser UI styled after OBSBOT Center, a live gamepad viewer with GamepadViewer-compatible skins, on-camera presets with thumbnails, and an OBS dock. Windows, Python, no OBSBOT Center required.
Built and verified against a real Tiny 3 and an 8BitDo SN30 Pro+ / DualSense. Everything it claims the camera can do has been measured — see obsbot-tiny3-protocol for the receipts.
pip install -r requirements.txt
python -m obsbot_controllerThat opens http://127.0.0.1:8765/ — the app is the browser UI. The camera starts in manual
control (AI tracking off) so the controller has full authority.
| Surface | URL | What it's for |
|---|---|---|
| Dashboard | / |
camera control, presets, image settings, controller view |
| Viewer | /viewer |
just the controller — OBS browser source (?transparent=1&keys=1) |
| OBS dock | /obs-dock |
compact controls styled like native OBS (separate repo) |
| HTTP API | /state, /control |
drive the camera from anything else |
Symmetric on purpose — every function pairs with its opposite.
| Input | Action |
|---|---|
| Left stick / click | pan-tilt / centre pan-tilt |
| Right stick / click | zoom / reset zoom |
| L2 · R2 | zoom out · zoom in (analog) |
| L · R | precision · turbo (hold) |
| D-pad ↑ ↓ | gimbal speed + / − |
| D-pad ← → | preset 1 · preset 2 |
| South · East · West · North | reset · tracking on-off · preset 3 · sleep-wake |
Rebind anything from Settings; Calibrate sticks fixes an unknown pad's axes and deadzone. The button map lights up live as you press, and uses the names printed on your controller (Cross/Circle/Square/Triangle on a DualSense, B/A/Y/X on an 8BitDo).
The viewer reproduces gamepadviewer.com's DOM and runtime behaviour, so GamepadViewer custom
CSS skins work verbatim — drop a folder with a skin.json into skins/web/. Bundled:
DualSense White, Xbox Elite Series 2 White, 8BitDo SN30 Pro+ (CC0), 8BitDo SN30 Pro. The skin
auto-matches the connected pad, with a manual override. Optional labels point at each control.
The Tiny 3 accepts many vendor commands it then ignores, so this app only exposes what was measured to work:
- Works — pan/tilt/zoom, recenter, presets, sleep/wake, AI tracking on/off, and AI mode (Single · Group · Voice · Desk · Hand · Whiteboard)
- Not exposed, because the camera ignores it — Auto Zoom framing (Full Body, Upper Body, Close-up, Headless, Lower Body), group-mode command, tracking speed, zone tracking. Those are OBSBOT Center-only. The UI says so rather than showing dead buttons.
- The tracking state shown is read back from the camera, not what the app asked for.
Two known camera quirks the app works around: zoom writes are inconsistent (re-asserted at ~1 Hz while the app owns framing), and while the camera sleeps its image properties read back garbage.
config.json — camera name match, speeds, expo curve, controller lock (by device GUID),
invert flags, recenter_resets_zoom, and startup/exit AI behaviour. Most of it is editable live
in the UI. Presets live in presets.json, thumbnails in preset-thumbs/.
A Camera control switch in the header (or Esc) stops the app writing to the camera entirely — useful when a stick drifts or another app needs the device.
DESIGN.md · PROCESS.md · BLUETOOTH.md · CONTROLLERS.md · skins · CLAUDE.md
MIT. Not affiliated with or endorsed by Remo Tech Co., Ltd. (OBSBOT). Bundled skins keep their own licenses — see each skin folder.