Skip to content
PixDrive
All articles

REST output: sending HTTP requests

PixDrive Studio sends HTTP requests to WLED, Philips Hue or any other HTTP API – from a REST device or straight from your Lua effects.

Updated

Paste the guide into an AI chat, then describe what the script should do.

Besides Art-Net, sACN and TPM2.Net, PixDrive Studio can send HTTP requests. Use them to control anything with an HTTP API: a WLED controller, a Philips Hue bridge, a Shelly, a media server or your own webhook.

There are two ways, and you can combine them:

  1. A REST device gets pixels like any other device. A small Lua script turns their colors into a request. Use this when an HTTP device should simply light up along with the show.
  2. Effect scripts send requests themselves, with rest_get("WLED", "/path") or http_get(url). Use this when you need full control: requests on the beat, at specific points in a show, or with any data you like.

This page covers requests that PixDrive sends. To control PixDrive itself over HTTP, see External control API (REST & OSC).

Adding a REST device

  1. Click Add Device in the device list.
  2. Choose REST (HTTP) as the protocol.
  3. Enter a name, for example WLED, and the base URL in the address field, for example http://192.168.178.52. There is no port field for REST; put a non-standard port in the URL.
  4. Click Add.

Select the device in the list and the Properties panel on the right shows all REST settings. At the top it shows how to address the device from an effect script, for example rest_get("WLED", "/path").

Settings

  • Base URL: the device’s address. A path from the script is appended. https:// works too.
  • Method: GET, POST, PUT, PATCH or DELETE. Used when the script doesn’t set its own method.
  • Timeout (ms): how long PixDrive waits for a response (100 to 60,000 ms).
  • Mode: Stream (on color change) sends whenever the colors change. One-shot (on trigger) sends only when you click Send now or an MCP client triggers it.
  • Max. rate (Hz): upper limit in stream mode, 0.1 to 60 requests per second. 10 Hz works well for WLED; a Hue bridge handles only about 10 requests per second in total.
  • Headers: sent with every request, for example Authorization: Bearer ….

Every request carries User-Agent: PixDrive-Studio/<version> unless you set your own User-Agent.

The request script

Whenever the REST device has something to send, it calls build_request(pixels, fixtures) in its script. The function gets the current colors of this device’s fixtures and returns what to send.

pixels[i] is a table with the channels of the LED type, each 0 to 255 (gamma-corrected, the same values Art-Net would send):

LED type Fields
RGB r, g, b
RGBW r, g, b, w
RGBCCT r, g, b, ww, cw
CCT bri, ct

fixtures[i] describes the fixtures in device order: id, name, start (index of the first pixel in pixels) and count.

The script returns nil or {} to send nothing, one request table, or a list of them (64 at most). All fields are optional:

  • method: overrides the device’s method
  • path: appended to the base URL, or url for a different full address
  • headers: a table such as { ["X-Key"] = "abc" }, adds to or replaces the device’s headers
  • body: a string is sent as is, a table as JSON (the header Content-Type: application/json is then added automatically)

json_encode(value) turns a table into a JSON string. Tables with the keys 1 to n become arrays, an empty table becomes {}.

This script sets a WLED controller to the color of the first pixel through its HTTP API:

function build_request(pixels, fixtures)
  local p = pixels[1]
  if p == nil then return nil end
  return {
    method = "GET",
    path = string.format("/win&A=255&R=%d&G=%d&B=%d", p.r, p.g, p.b),
  }
end

The script runs in the same sandbox as effect scripts: no file access, no system commands, and a time limit of 250 ms per call. A script that doesn’t load can’t be applied; the error message shows the line.

Templates

Insert template above the editor loads a ready-made script:

  • Generic JSON sends the first pixel’s color as { "r": …, "g": …, "b": … }.
  • WLED (JSON API) colors one WLED segment per fixture through /json/state.
  • Philips Hue sets one lamp per fixture through API v1. The base URL is then http://<bridge-ip>/api/<username>, and each fixture is named after its lamp’s ID, for example 3.

Changes in the editor take effect after Apply script.

Preview and testing

Preview request shows the method, URL, headers and body of the requests for the current colors without sending anything. Below that are counters for sent and failed requests and the last error. Send now sends the current colors immediately, in either mode.

A REST device sends from its own background thread, so a slow or unreachable target doesn’t hold up the preview or the other outputs. In stream mode there is never more than one request in flight; colors that arrive in the meantime are merged instead of queued.

Requests from effect scripts

These functions are available in every effect script:

Function Purpose
rest_get(device, path [, headers]) GET to the REST device’s base URL plus path
rest_post(device, path, body [, headers]) POST, body as a string or table (JSON)
rest_put(device, path, body [, headers]) PUT
rest_request(device, { method, path, headers, body }) any method, otherwise the device’s
http_get(url [, headers]) GET to a full URL
http_post(url, body [, headers]), http_put(…) POST and PUT to a full URL
http_request{ method, url, headers, body } any method
json_encode(value) table as a JSON string

The rest_* functions address a REST device by its name and use its base URL and headers. If the IP address changes, you update it in the device only, not in every script. A REST device without fixtures acts as an address book and sends nothing on its own. The name is case-sensitive. With an unknown name, for example after renaming a device, the function sends nothing and returns false. The effect keeps running, and the log gets a warning listing the existing REST devices.

The http_* functions don’t need a device; the full URL is in the script instead.

Rules

  • The functions send in the background and don’t wait for a response. They return true if the request was queued and false otherwise. The script never sees the response.
  • Requests are only sent when the frame actually goes out: for live effects and while the sequencer plays. In the timeline’s hover preview, while dragging the playhead, when paused and when exporting, the script runs but sends nothing.
  • Each effect sends at most 20 requests per second; anything above that is dropped. render() runs 60 times a second, so never call the functions unconditionally every frame, only when something changes.
  • In scripts with parallel = true only the first worker sends.
  • An invalid URL or method is a script error and shows up in the preview already.

Example: WLED follows the effect

This script colors the LEDs and sends the same color to the REST device WLED, at most ten times a second and only on change. sent is only updated once the request actually went out; otherwise a preview frame would swallow the first real request.

local sent, sent_at = nil, -1

function render()
  local r = (sin(time) * 0.5 + 0.5)
  for i = 0, led_count - 1 do set_led_rgb(i, r, 0.0, 0.0) end

  local path = string.format("/win&A=255&R=%d&G=0&B=0", math.floor(r * 255))
  if time < sent_at then sent_at = -1 end  -- segment restarted in a loop
  if path ~= sent and time - sent_at >= 0.1 and rest_get("WLED", path) then
    sent, sent_at = path, time
  end
end

Example: one request per beat

local was_beat = false

function render()
  if audio_beat and not was_beat then
    http_post("http://10.0.0.5/api/trigger", { scene = "flash" })
  end
  was_beat = audio_beat
end

In the sequencer and presets

Put effect scripts that use rest_get or http_get on the timeline as segments or save them as presets, like any other script. A preset can then also be fired as a cue through the external control API.

Don’t let segments that send to the same device overlap. Each script sends its own color, so a crossfade between two segments would make the device flicker. Fades in the timeline only affect the preview and the pixels, not the values a script sends over HTTP.

Through MCP

Through the MCP server an AI assistant can set up and test REST output:

  • add_device with protocol: "Rest"; ip may be a full base URL
  • set_rest_config sets URL, method, headers, timeout, mode, rate and script
  • preview_rest_request shows the requests for the current colors without sending
  • send_rest_device sends immediately, also in one-shot mode
  • add_segment with lua_source puts a custom script on the timeline, for example one that uses rest_get

Notes and limits

  • Responses are not evaluated. Status codes of 400 and above and connection errors count as failed and are written to the log (app.log) at most every 5 seconds per device.
  • REST devices are not part of the exports (.scn, .artnet, TPM2, WLED) and don’t appear in the export dialog’s device list.
  • A fixture’s color order doesn’t apply to REST; the script addresses channels by name.
  • If two REST devices share a name, rest_get uses the one added last.
  • WLED may apply its own gamma correction on top. If dark tones look too dark, raise them in the script.