Getting Started
This guide walks you through creating your first Skydimo plugin: a simple Lua color-cycling effect. For native shared-library plugins, see Native-C Plugin Runtime.
This workflow is supported since 3.0.0-dev.4.
Prerequisites
- Skydimo installed and running
- A text editor
- Basic Lua knowledge (Lua 5.4 Reference)
Step 1: Create a Source Package Directory
Create a source folder under the development import queue:
import/plugin-dev/effect.my_first_effect/
The directory name must follow the pattern <type>.<id> where:
typeiscontroller,effect, orextensionidis a unique identifier using lowercase letters, numbers, and underscores
Step 2: Write manifest.json
Create import/plugin-dev/effect.my_first_effect/manifest.json:
{
"id": "my_first_effect",
"version": "1.0.0",
"name": "My First Effect",
"type": "effect",
"language": "lua",
"entry": "main.lua",
"category": "animation",
"icon": "Sparkles",
"permissions": ["log"],
"params": [
{
"key": "speed",
"label": "Speed",
"kind": "slider",
"default": 1.0,
"min": 0.1,
"max": 5.0,
"step": 0.1
},
{
"key": "color",
"label": "Color",
"kind": "color",
"default": "#FF6600"
}
]
}
Step 3: Write main.lua
Create import/plugin-dev/effect.my_first_effect/main.lua:
local plugin = {}
-- Parameters (updated by on_params)
local speed = 1.0
local base_r, base_g, base_b = 255, 102, 0
function plugin.on_init()
-- Called once when the effect is instantiated
end
function plugin.on_params(p)
-- Called whenever user changes parameters
if p.speed then
speed = p.speed
end
if p.color then
-- Color comes as {r, g, b} table
base_r = p.color[1] or base_r
base_g = p.color[2] or base_g
base_b = p.color[3] or base_b
end
end
function plugin.on_tick(elapsed, buffer, width, height)
local count = buffer:len()
for i = 1, count do
-- Calculate a wave based on position and time
local ratio = (i - 1) / count
local wave = math.sin((ratio + elapsed * speed) * math.pi * 2)
local brightness = (wave + 1) / 2 -- normalize 0..1
local r = math.floor(base_r * brightness)
local g = math.floor(base_g * brightness)
local b = math.floor(base_b * brightness)
buffer:set(i, r, g, b)
end
end
function plugin.on_shutdown()
-- Called when the effect is removed
end
return plugin
Step 4: Import and Load the Plugin
- In the Plugins page, trigger Refresh Plugins (or restart Core).
- Core imports your package and updates the plugin registry.
- Open any device and find My First Effect in the effect list.
- Adjust parameters to verify live behavior.
Use import/plugin-dev/ while developing. The source package is kept, so you can iterate quickly: edit files → refresh plugins → retest.
Step 5: Development Iteration Loop
Recommended loop:
- Edit source package in
import/plugin-dev/<type>.<id>/ - Trigger Refresh Plugins
- Verify behavior in UI / logs
- Repeat
When moving to production/distribution, package clean plugin content and install through import/download flows.
What's Next?
- Manifest Reference — All manifest options
- Plugin Management — Import queues, delete/reset behavior, and troubleshooting
- Effect Plugin Guide — Advanced effect techniques
- Controller Plugin Guide — Write hardware drivers
- Extension Plugin Guide — Build background services
- Native-C Plugin Runtime — Build plugins from C, Rust, C#, or another C ABI-capable language
- Native-C API Reference — ABI tables and host methods
Troubleshooting
Check the Core log output for Lua errors. Plugin errors are logged with the plugin ID prefix.
Common issues:
- Plugin not showing up: Verify the directory name matches
effect.<id>,manifest.jsonis valid JSON, and you triggered Refresh Plugins - Lua errors: Ensure your entry file returns a table with the expected callback functions
- Parameters not working: Make sure
paramskeys inmanifest.jsonmatch what you read inon_params - Changes not applied immediately: Trigger refresh again and confirm you are editing the source package under
import/plugin-dev/