Script Engine: eigene Effekte mit Lua
Eigene LED-Effekte in Lua schreiben, testen und speichern – mit Parametern, Audio-Reaktion, 2D-Matrizen und Mehrkern-Rendering.
Aktualisiert am
Wenn die mitgelieferten Effekte nicht reichen, schreibst du in PixDrive Studio deine eigenen. Die Script Engine führt Lua 5.4 aus: Dein Skript wird für jedes Bild (Frame) aufgerufen und setzt die Farbe jeder einzelnen LED. Änderungen siehst du sofort in der Vorschau und auf deinen Geräten.
Diese Anleitung führt dich vom ersten Skript bis zu Parametern, Audio-Reaktion, 2D-Matrizen und Mehrkern-Rendering.
Dein erstes Skript
- Öffne oben in der Werkzeugleiste den Bereich Skripte. Du siehst die LED-Vorschau, darunter den Code-Editor und rechts die Seitenleiste Parameter.
- Klicke links unter Beispiele auf Rainbow. Der Code wird in den Editor geladen.
- Wähle in der Geräteliste die Fixtures aus, auf denen der Effekt laufen soll. Ohne Auswahl läuft das Skript auf allen Fixtures.
- Klicke unten auf Ausführen. In der Statusleiste erscheint „Skript läuft“, die Vorschau und deine LEDs zeigen den Regenbogen.
- Ändere im Code eine Zahl, zum Beispiel die Geschwindigkeit, und klicke erneut auf Ausführen.
- Mit Speichern legst du das Skript unter einem Namen in der Bibliothek ab.
Zum Stoppen klickst du in der Liste Aktive Effekte beim laufenden Skript auf das ×. Laufende Skripte erkennst du dort an einem gelben Punkt.
Aufbau eines Skripts
Jedes Skript muss eine Funktion render() definieren. Sie wird einmal pro Frame aufgerufen. Code außerhalb von render() läuft nur einmal beim Start – ideal, um Werte vorzubereiten.
-- läuft einmal beim Start
local offset = 0.0
function render()
-- läuft in jedem Frame
offset = offset + delta * 60.0
for i = 0, led_count - 1 do
local hue = (i / led_count * 360.0 + offset) % 360.0
set_led_hsv(i, hue, 1.0, 1.0)
end
end
Ein paar Lua-Grundlagen, die du ständig brauchst:
- Variablen mit
localanlegen:local x = 5 - Schleife über alle LEDs:
for i = 0, led_count - 1 do … end - Bedingung:
if x > 0 then … else … end - Kommentare:
-- eine Zeileoder--[[ mehrere Zeilen ]] - Division liefert immer eine Kommazahl:
1 / 2ergibt0.5. Ganzzahlige Division mit//:7 // 2ergibt3.
LEDs setzen
LEDs werden direkt über Funktionen gesetzt. Die Indizes laufen von 0 bis led_count - 1.
| Funktion | Wirkung |
|---|---|
set_led_hsv(i, h, s, v) |
Farbe als Farbton, Sättigung, Helligkeit. h: 0–360, s und v: 0.0–1.0 |
set_led_rgb(i, r, g, b) |
Farbe als Rot, Grün, Blau, jeweils 0.0–1.0 |
set_led_rgba(i, r, g, b, a) |
wie RGB, zusätzlich mit Transparenz a (0.0–1.0) |
set_led_cct(i, helligkeit, ct) |
nur Weiß: ct 0.0 = warm (2700 K) bis 1.0 = kalt (6500 K) |
Farbwerte außerhalb von 0.0–1.0 begrenzt du mit clamp(wert, 0.0, 1.0), den Farbton hältst du mit % 360.0 im gültigen Bereich.
Verfügbare Variablen
Diese Werte setzt PixDrive vor jedem Frame:
| Variable | Bedeutung |
|---|---|
time |
Sekunden seit Start des Effekts |
delta |
Sekunden seit dem letzten Frame – für gleichmäßige Bewegung unabhängig von der Framerate |
frame |
Nummer des aktuellen Frames, zählt ab 0 |
led_count |
Anzahl der LEDs |
matrix_breite |
Breite der Matrix in Pixeln, 0 bei einem LED-Streifen |
matrix_hoehe |
Höhe der Matrix in Pixeln, 0 bei einem LED-Streifen |
led_start, led_end |
LED-Bereich dieses Rechenkerns, siehe Leistung |
Parameter: Werte live verstellen
Mit Parametern machst du Werte wie Geschwindigkeit oder Farbton einstellbar, ohne den Code anzufassen.
- Klicke in der Seitenleiste Parameter auf + Neuer Parameter.
- Vergib eine Parameter ID (z. B.
speed) und einen Parameter Name (z. B. „Geschwindigkeit“). - Wähle den Typ Float (Kommazahl), Integer (Ganzzahl) oder Boolean (an/aus) und trage bei Zahlen Min, Max und Default ein.
- Bestätige mit OK.
Im Skript steht der Wert dann als Variable mit dem Präfix param_ bereit – aus speed wird param_speed:
-- @params: [{"id":"speed","name":"Geschwindigkeit","param_type":{"type":"Float","config":{"default":1.0,"min":0.1,"max":5.0,"step":0.05}},"automatable":true}]
function render()
local t = time * param_speed
for i = 0, led_count - 1 do
set_led_hsv(i, (i / led_count * 360.0 + t * 60.0) % 360.0, 1.0, 1.0)
end
end
Die erste Zeile mit -- @params: schreibt PixDrive automatisch, sobald du einen Parameter anlegst oder entfernst. Du kannst sie auch von Hand bearbeiten – dann muss die ganze Definition in einer Zeile stehen, die Seitenleiste übernimmt die Änderung.
Während das Skript läuft, erscheinen unter der Parameterliste Regler. Änderungen wirken sofort auf die laufende Ausgabe.
2D-Matrizen
Für LED-Matrizen liefern pos_x(i) und pos_y(i) die Position einer LED im Bild, jeweils von 0.0 bis 1.0 (pos_x: links → rechts, pos_y: oben → unten). Verkabelung, Startecke und Schlangenlinien-Führung der Matrix sind dabei schon berücksichtigt.
-- konzentrische Ringe
function render()
for i = 0, led_count - 1 do
local x = pos_x(i) - 0.5
local y = pos_y(i) - 0.5
local dist = sqrt(x * x + y * y)
set_led_hsv(i, (dist * 720.0 - time * 90.0) % 360.0, 1.0, 1.0)
end
end
Soll ein Skript auf Streifen und Matrizen funktionieren, prüfst du matrix_breite > 0 und rechnest bei Streifen mit i / led_count.
Auf Musik reagieren
Ist in den Einstellungen ein Mikrofon aktiviert, stehen Audiowerte zur Verfügung. Ohne Mikrofon sind sie 0.0 bzw. false – dein Skript läuft trotzdem.
| Variable | Bedeutung |
|---|---|
audio_bass |
Bass (60–250 Hz), 0.0–1.0 |
audio_mids |
Mitten (250 Hz–2 kHz), 0.0–1.0 |
audio_highs |
Höhen (2–16 kHz), 0.0–1.0 |
audio_volume |
Gesamtlautstärke, 0.0–1.0 |
audio_beat |
true, wenn ein Beat erkannt wurde |
-- Helligkeit folgt dem Bass
function render()
for i = 0, led_count - 1 do
local hue = (i / led_count * 360.0 + time * 30.0) % 360.0
set_led_hsv(i, hue, 1.0, audio_bass * 0.8 + 0.2)
end
end
Mathe-Funktionen
Die wichtigsten Funktionen stehen ohne math.-Präfix bereit:
| Funktion | Ergebnis |
|---|---|
sin(x), cos(x), tan(x) |
Winkelfunktionen im Bogenmaß – eine volle Umdrehung ist 2 * pi |
atan2(y, x) |
Winkel eines Punkts, −π bis π |
sqrt(x), pow(x, y), abs(x) |
Wurzel, Potenz, Betrag |
floor(x), ceil(x) |
ab- bzw. aufrunden |
min(a, b), max(a, b) |
kleinerer bzw. größerer Wert |
clamp(x, lo, hi) |
x auf den Bereich lo–hi begrenzen |
lerp(a, b, t) |
zwischen a und b überblenden, t von 0.0 bis 1.0 |
fmod(x, y) |
Rest einer Division, wie x % y |
pi |
3.14159… |
Alle weiteren Funktionen der Lua-Standardbibliothek, etwa math.random, kannst du ebenfalls nutzen.
Werte zwischen Frames merken
Variablen, die du außerhalb von render() anlegst, behalten ihren Wert von Frame zu Frame:
-- ein Lichtpunkt läuft über den Streifen
local position = 0.0
function render()
position = position + delta * 30.0
if position >= led_count then position = 0.0 end
local p = floor(position)
for i = 0, led_count - 1 do
local bright = clamp(1.0 - abs(i - p) * 0.3, 0.0, 1.0)
set_led_hsv(i, 40.0, 1.0, bright)
end
end
Nutze für Bewegung delta statt einer festen Schrittweite pro Frame – so läuft der Effekt bei jeder Framerate gleich schnell.
Leistung bei vielen LEDs
Standardmäßig rechnet ein Skript auf einem Prozessorkern. Bei sehr vielen LEDs und aufwendigen Berechnungen pro Pixel kann die Framerate sinken. Dann schaltest du Mehrkern-Rendering ein:
- Setze ganz oben im Skript
parallel = true. - Laufe in der Schleife von
led_startbisled_end - 1statt von0bisled_count - 1. Für Positionen rechnest du weiter mitled_count.
parallel = true
function render()
for i = led_start, led_end - 1 do
set_led_hsv(i, (i / led_count * 360.0 + time * 60.0) % 360.0, 1.0, 1.0)
end
end
Alternativ nimmt dir for_each_led(function(i) … end) die Schleife ab. Gut zu wissen:
- Aufgeteilt wird erst ab 1024 LEDs, auf bis zu 8 Kerne. Darunter läuft das Skript normal auf einem Kern.
- Jeder Kern hat seine eigenen Variablen. Werte, die du dir zwischen Frames merkst, sind nicht zwischen den Kernen geteilt.
- Schreibt ein Kern auf LEDs außerhalb seines Bereichs, wird das ignoriert.
Weitere Tipps: Berechne Werte, die für alle LEDs gleich sind, vor der Schleife, und lege Tabellen außerhalb von render() an, statt sie in jedem Frame neu zu erzeugen.
Fehler finden
- Validieren prüft den Code, ohne ihn auszuführen – etwa ob die Syntax stimmt und
render()vorhanden ist. - Fehler erscheinen rot unter dem Editor, zum Beispiel
[Laufzeit] Zeile 12: …. Die betroffene Zeile wird im Editor markiert. - Ein Laufzeitfehler, der in jedem Frame auftritt, wird nur einmal angezeigt. Mit Leeren setzt du die Liste zurück.
Häufige Ursachen: eine fehlende render()-Funktion, ein vergessenes end, oder eine Parameter-Variable ohne das Präfix param_.
Skripte weiterverwenden
- Bibliothek: Gespeicherte Skripte findest du links unter Bibliothek. Ein Klick lädt sie in den Editor. Speichern überschreibt das geladene Skript direkt, Als neues Skript speichern legt eine Kopie an. Das Papierkorb-Symbol löscht ein Skript sofort und ohne Rückfrage.
- Sequenzer: Im Sequenzer legst du ein Bibliotheks-Skript mit einem Klick als 5-Sekunden-Clip an der Abspielposition ab.
- Presets: Ein neues Preset übernimmt alle gerade laufenden Effekte, also auch ein Skript, das du mit Ausführen gestartet hast.
- Export und Import: Über .zfx exportierst du ein Skript als Effekt-Datei, Importieren lädt eine
.zfx-Datei in den Editor.
Skripte mit KI schreiben
Der Button LLM kopiert eine vollständige Beschreibung der Script-API in die Zwischenablage. Füge sie in einen KI-Chat deiner Wahl ein und beschreibe den gewünschten Effekt – den erzeugten Code kopierst du in den Editor und startest ihn mit Ausführen.
Demo-Version
In der Demo ist die Script Engine voll nutzbar, Skripte dürfen aber höchstens 20 Zeilen lang sein. Die -- @params:-Zeile zählt mit, die aktuelle Länge zeigt der Editor oben an. Längere Skripte lassen sich weiter validieren und speichern, starten aber erst mit einer Lizenz. Der Sequenzer ist in der Demo nicht verfügbar.