Browser control panel
The plugin serves the Live screen's camera controls at
http://localhost:9980, in a
browser window you can park beside OBS. Nobody has to touch the phone.
Opening it#
Open http://localhost:9980 on the computer running OBS. The panel
is on by default; the switch is Tools → LensLink Settings → Browser
control panel.
Loopback only. The panel binds to 127.0.0.1,
so nothing on the network can reach it — deliberate, since it can start
cameras and has no authentication.
Panel port (default 9980) is a plugin-wide
setting if something else already has that port.
What is on it#
Same controls as the app's Live panel, in the same order:
- Status pill with dot, word and latency. A second pill appears while lip-sync calibration runs, and offers Recalibrate once locked.
- Source tabs, when more than one camera source is live — one page controls every phone.
- Zoom, exposure (AE bias, or manual ISO and shutter), focus, white balance.
- Flashlight, Lens, Flip.
- Format changes mid-stream: resolution, frame rate, codec, microphone.
- Subject distance, while the green screen runs with depth assist.
- Start camera while idle, red Stop camera while live, each with an Auto-start toggle.
It mirrors changes made on the phone, pausing while you drag so it never fights your hand. A LensLink Screen source has no camera controls, and the panel says so rather than showing dead sliders.
The HTTP API#
The panel is one client of a small HTTP API on the same port; a stream deck or
a script can be another. Every endpoint takes an optional
?src=<id>; without it the plugin picks the first suitable
source.
Reading state#
| Request | Returns |
|---|---|
GET /api/sources | Every source: id, name, connected, standby, screen. |
GET /api/status | status text, plus screen, standby, connected, autoStart and the lip-sync sync stage. |
GET /api/state | The phone's last camera-state snapshot: lenses, zoom, exposure, focus, white balance, supported formats. The same cache the source properties read, which is how the three surfaces stay in step. |
Sending commands#
POST /api/control takes one JSON command per request. Unknown
commands are ignored by design, so a newer panel and an older app degrade
rather than break.
POST /api/control
{ "cmd": "zoom", "value": 2.0 }
{ "cmd": "exposure_bias", "value": -0.5 }
{ "cmd": "exposure", "mode": "manual", "iso": 400, "shutterSeconds": 0.004 }
{ "cmd": "exposure", "mode": "auto" }
{ "cmd": "focus", "mode": "locked", "lensPosition": 0.42 }
{ "cmd": "focus", "mode": "auto" }
{ "cmd": "white_balance", "mode": "locked", "temperature": 5600 }
{ "cmd": "white_balance", "mode": "auto" }
{ "cmd": "flashlight", "on": true }
{ "cmd": "flip" }
{ "cmd": "start_stream" }
{ "cmd": "stop_stream" }
{ "cmd": "set_format", "resolution": "1080p", "fps": 60, "codec": "hevc" }
{ "cmd": "mic", "id": "builtin:2" }
{ "cmd": "green_screen", "on": true, "maxDistance": 2.5 }
| Request | Effect |
|---|---|
POST /api/autostart | {"on":true|false} — the same property the auto-start checkbox edits. An empty body is rejected, not read as false. |
POST /api/recalibrate | No body. Starts a fresh lip-sync measurement. |
Examples#
# Start the camera on the only connected phone
curl -X POST http://localhost:9980/api/control -d '{"cmd":"start_stream"}'
# Zoom the source with id 2 to 3x
curl -X POST 'http://localhost:9980/api/control?src=2' -d '{"cmd":"zoom","value":3.0}'
# What is connected right now?
curl -s http://localhost:9980/api/sources
Turning it off#
Clear Browser control panel in Tools → LensLink Settings. The port closes immediately; everything else is unaffected.