PixDrive Studio accepts commands from external systems over two network interfaces:

  • REST, a plain HTTP API. Use it from Stream Deck, Bitfocus Companion, Home Assistant, Node-RED, shell scripts, or anything that can send an HTTP request.
  • OSC, an Open Sound Control receiver over UDP. Use it from lighting consoles, TouchOSC, QLab, Ableton, and other show-control tools.

Both interfaces control cues, playback, and the master brightness. They use the same engine path as the app’s own UI, so an external trigger behaves like a click in the app.

For AI/agent automation, PixDrive also ships an MCP server for LLM-driven control. The REST/OSC APIs on this page target show-control hardware and home-automation systems.

Cues

Every preset is a cue. There is nothing to configure. A cue is addressable two ways:

  • by name: the preset’s name (stable, recommended), or
  • by number: its 1-based position in the preset list.

A cue is one of two kinds:

  • Look: a snapshot of one or more effects, created on the Control page.
  • Show: a full sequencer timeline, created on the Sequencer page. Triggering a Show restarts it from the beginning and starts playback.

Enabling external control

External control is off by default and requires the full (licensed) version.

  1. Open Settings → External Control.
  2. Toggle HTTP/REST and/or OSC on.
  3. Optionally change the bind address, port, and (REST only) a bearer token.

Changes to enabled/port/bind take effect after you restart the app; the token applies without a restart.

Default endpoints

InterfaceDefault bindDefault portBase path
REST127.0.0.19848/api
OSC (UDP)127.0.0.19000

Both servers bind to 127.0.0.1 by default, so only the local machine can reach them. To trigger from another device on the network, set the bind address to 0.0.0.0 or a specific interface IP. See Security.

REST API

Base URL: http://<host>:9848

All responses are JSON. Most mutating endpoints accept both GET and POST so you can trigger them from tools that only send GET requests (e.g. a browser or a simple button). The exception is /api/brightness?value=<f>, which is POST-only (a GET to /api/brightness always reads the current value).

Endpoints

MethodPathDescription
GET/api/cuesList all cues (number, label, kind).
GET/POST/api/cue/{id}/goTrigger a cue by number or name.
GET/POST/api/playback/playStart sequence playback.
GET/POST/api/playback/pausePause playback.
GET/POST/api/playback/stopStop and reset to the start.
GET/POST/api/playback/position?ms=<n>Seek to a position (milliseconds).
GET/api/brightnessRead the master brightness (0.0–1.0).
POST/api/brightness?value=<f>Set the master brightness (0.0–1.0).
GET/api/statusCurrent playback + brightness state.

List cues

curl http://127.0.0.1:9848/api/cues
{
  "cues": [
    { "number": "1", "label": "Colors",  "kind": "look" },
    { "number": "2", "label": "Rainbow 2D", "kind": "look" },
    { "number": "3", "label": "Mexico",     "kind": "look" },
    { "number": "4", "label": "Show",       "kind": "show" }
  ]
}

To build a controller, read this list and trigger each cue by its number or label.

Trigger a cue

By number:

curl -X POST http://127.0.0.1:9848/api/cue/4/go

By name (URL-encode spaces):

curl -X POST "http://127.0.0.1:9848/api/cue/Show/go"
curl -X POST "http://127.0.0.1:9848/api/cue/Rainbow%202D/go"
{ "ok": true, "cue": "Show" }

Unknown cue → 404:

{ "ok": false, "error": "Cue '99' not found" }

Playback

curl -X POST http://127.0.0.1:9848/api/playback/play
curl -X POST http://127.0.0.1:9848/api/playback/pause
curl -X POST http://127.0.0.1:9848/api/playback/stop
curl -X POST "http://127.0.0.1:9848/api/playback/position?ms=5000"

Each returns { "ok": true } on success.

Master brightness

The global dimmer scales the entire output (all effects and the sequencer), 0.01.0. PixDrive clamps values to that range.

# read
curl http://127.0.0.1:9848/api/brightness
# → { "value": 1.0 }

# set to 50 %
curl -X POST "http://127.0.0.1:9848/api/brightness?value=0.5"
# → { "ok": true, "value": 0.5 }

Setting brightness over the API also moves the toolbar slider in the app.

Status

curl http://127.0.0.1:9848/api/status
{
  "has_sequence": true,
  "state": "Running",
  "position_ms": 3430,
  "duration_ms": 36000,
  "active_segment_ids": ["10d55fa9-…"],
  "loop_active": false,
  "master_brightness": 1.0
}

state is one of Stopped, Running, Paused.

Security

  • Local by default. Both servers bind to 127.0.0.1; only the local machine can reach them until you change the bind address.
  • Optional bearer token (REST). Set a token in Settings → External Control. Once set, every request must include it: curl -H "Authorization: Bearer <your-token>" \ -X POST http://127.0.0.1:9848/api/cue/1/go Requests without a valid token get 401.
  • Demo mode. The trial/demo version rejects all requests with 403; the servers run on the full version only.
  • OSC has no authentication (the protocol defines none). Restrict it via the bind address and run it on a trusted network/LAN only.

If you expose REST on the LAN (bind = 0.0.0.0), set a bearer token.

OSC API

Send OSC messages over UDP to <host>:9000. OSC is fire-and-forget: no reply, no listing endpoint. If you need the cue list, use the REST GET /api/cues.

AddressArgumentDescription
/pixdrive/cue/<id>/goTrigger a cue by number/name.
/pixdrive/playback/playStart playback.
/pixdrive/playback/pausePause playback.
/pixdrive/playback/stopStop and reset.
/pixdrive/playback/positionint/float (ms)Seek to a position.
/pixdrive/brightnessfloat (0.0–1.0)Set master brightness.

The cue <id> is part of the OSC address, e.g. /pixdrive/cue/4/go or /pixdrive/cue/Show/go.

Quick test from a terminal (Python, no dependencies):

import socket, struct

def osc(addr, *args):
    # address, null-terminated + padded to 4 bytes
    a = addr.encode() + b"\x00"
    a += b"\x00" * ((4 - len(a) % 4) % 4)
    if not args:
        return a + b",\x00\x00\x00"
    # single float argument
    return a + b",f\x00\x00" + struct.pack(">f", float(args[0]))

s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.sendto(osc("/pixdrive/cue/Show/go"), ("127.0.0.1", 9000))
s.sendto(osc("/pixdrive/brightness", 0.5), ("127.0.0.1", 9000))

Integration examples

Bitfocus Companion / Stream Deck: add a Generic HTTP action:

POST http://<pixdrive-host>:9848/api/cue/1/go

Add the Authorization: Bearer <token> header if you set a token. Because the endpoints also accept GET, an “Open URL” button works too.

Home Assistant: a REST command:

rest_command:
  pixdrive_cue:
    url: "http://<pixdrive-host>:9848/api/cue/{{ cue }}/go"
    method: POST
    headers:
      Authorization: "Bearer !secret pixdrive_token"
# call it
service: rest_command.pixdrive_cue
data:
  cue: "Show"

TouchOSC / lighting console: map a button to /pixdrive/cue/1/go and a fader (0.0–1.0) to /pixdrive/brightness.

Notes & limits

  • PixDrive derives cues from presets and updates the cue list whenever presets change; there is no hand-edited cue list.
  • Trigger a Show by name; positions shift when you add, remove, or reorder presets.
  • Playback and seeking act on the loaded sequence. Triggering a Show cue loads that show and starts it from 0.
  • REST and OSC are independent; enable either or both.