LensLink

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:

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#

RequestReturns
GET /api/sourcesEvery source: id, name, connected, standby, screen.
GET /api/statusstatus text, plus screen, standby, connected, autoStart and the lip-sync sync stage.
GET /api/stateThe 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 }
RequestEffect
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/recalibrateNo 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.