Rendering, Brightness and LEDs
Companion does the drawing. It renders each control according to the user's configuration and pushes the result to your surface instance for the matching control. Your job is to put that onto the hardware — you don't draw button graphics yourself.
draw
draw(signal, drawProps) is called whenever a control changes. What drawProps contains depends
on what you asked for in that control's style preset:
controlId— which control this is (the id from your layout).image— aUint8Arrayof pixels, in the size and format you requested viabitmap.color— a hex background colour, if you requestedcolors.text— button text, if you requestedtext.leds— aUint8Arrayof packed RGB colours (3 bytes per segment, sosegments * 3bytes long) for the control's LED strip or ring, if you requestedleds. See Indicator LEDs below.pageNumber— the Companion page the surface is on, where applicable.
async draw(signal: AbortSignal, drawProps) {
if (signal.aborted) return
const control = this.layoutLookup.get(drawProps.controlId)
if (!control) return
if (drawProps.image) {
await this.device.fillKeyBuffer(control.keyIndex, drawProps.image)
} else if (drawProps.color) {
await this.device.fillKeyColor(control.keyIndex, drawProps.color)
}
}
The signal is an AbortSignal that fires if the draw is superseded before you finish — check it
and bail out early on slow hardware so you don't draw stale frames.
Because Companion pushes finished output, you generally don't need to track button state yourself — render what you're given, when you're given it.
Brightness and clearing
Two related methods round out output:
setBrightness(percent)—0–100. Only called if you setbrightness: trueinregisterProps.blank()— set all pixels to black/off, e.g. on shutdown.
Indicator LEDs
An addressable strip or ring of LEDs belonging to a control — an encoder's ring, a T-bar's LEDs —
is declared with leds on that control's style preset, and arrives as the leds
draw prop. A gauge on the button drives it, and Companion samples the gauge down to one colour per
segment for you:
if (drawProps.leds) {
for (let i = 0; i < segments; i++) {
const color = readLedColor(drawProps.leds, i)
await this.device.setEncoderColor(control.encoderIndex, i, color)
}
}
readLedColor(buffer, index) is exported from @companion-surface/base. For monochrome LEDs,
colorToIntensity(color) flattens a colour to a single 0–255 value.
LEDs that aren't attached to a control at all — standalone status lights — are instead modelled as output transfer variables rather than draws; see Input & variables.
See the generated reference for the exact
SurfaceDrawProps and pixel formats, and the
Elgato Stream Deck module for
real image handling.