Open Brush Lua Plugin Development
This skill helps you create and modify Lua plugins for Open Brush, a VR painting application. You have access to the complete Open Brush Lua API documentation.
Quick Start
Plugin File Structure
CRITICAL: Plugin Naming Convention Plugins MUST be named with the correct prefix or Open Brush won't recognize them:
- PointerScript.PluginName.lua (e.g., PointerScript.Wobble.lua)
- SymmetryScript.PluginName.lua (e.g., SymmetryScript.ManyAround.lua)
- ToolScript.PluginName.lua (e.g., ToolScript.Circle.lua)
- BackgroundScript.PluginName.lua (e.g., BackgroundScript.Lines.lua)
A basic plugin structure:
-- Plugin metadata (in Settings table)
Settings = {
description = "What this plugin does"
}
-- Optional parameters (exposed as UI widgets)
Parameters = {
speed = {label = "Speed", type = "float", min = 1, max = 100, default = 50},
count = {label = "Count", type = "int", min = 1, max = 10, default = 5},
color = {label = "Color", type = "color", default = Color.red},
enabled = {label = "Enabled", type = "toggle", default = true},
name = {label = "Name", type = "text", default = "Untitled"},
mode = {label = "Mode", type = "list", items = {"A", "B", "C"}, default = "A"},
}
-- Main function (required) - runs every frame
function Main()
-- Your plugin logic here
-- Return value determines plugin type (Transform, Path, PathList, or nothing)
end
-- Optional: runs once when plugin starts
function Start()
-- Setup code
end
-- Optional: runs once when plugin ends
function End()
-- Cleanup code
end
Where to Find Information
IMPORTANT: All documentation files are located inside this skill's directory.
The skill directory is typically at: ~/.claude/skills/open-brush-plugin-skill/ (or C:\Users\USERNAME\.claude\skills\open-brush-plugin-skill\ on Windows)
All paths below are relative to the skill directory, NOT the user's project directory.
When creating a new plugin:
- Read
references/instructions.mdfor critical API syntax rules and plugin structure - Check
references/examples/directory for similar working code (PointerScript., SymmetryScript., ToolScript., BackgroundScript.) - Read
references/guides/example-plugins/for explanations of what example plugins do - Read
references/guides/writing-plugins/for step-by-step tutorials on each plugin type
When you need API details:
- Check
references/lua-modules/__autocomplete.luafor complete list of available classes/methods/properties - Read specific files in
references/api-docs/directory: app.md, brush.md, vector3.md, path.md, etc.
External reference (for context only): https://icosa.gitbook.io/open-brush-plugin-scripting-docs
Instructions for AI Agents
Critical API Rules
MOST IMPORTANT: Only use APIs listed in references/lua-modules/__autocomplete.lua. NEVER invent methods or properties. If unsure, say "I'm unsure - let me check the API documentation" and read __autocomplete.lua. Favor the Open Brush API over standard Lua library functions.
Core Syntax Rules:
- Constructors:
ClassName:New(...)(capital N, always use colon) - Properties:
object.property(dot notation) - Methods:
object:method()(colon notation) - API classes start with capital letters (e.g.,
Vector3,Transform,Path) - Methods do NOT return self - no method chaining
- Use
Math(capital M) library, not lua'smath
Plugin Structure: All plugins define:
Main()- called every frame (required)Start()- called when plugin begins (optional)End()- called when plugin ends (optional)Settingstable - plugin metadata (optional)Parameterstable - UI sliders for user input (optional)
Settings = {
description = "Plugin description",
space = "pointer" -- or "canvas", "world", etc.
}
Parameters = {
speed = {label = "Speed", type = "float", min = 1, max = 100, default = 50},
count = {label = "Count", type = "int", min = 1, max = 10, default = 5},
color = {label = "Color", type = "color", default = Color.red},
enabled = {label = "Enabled", type = "toggle", default = true},
name = {label = "Name", type = "text", default = "Untitled"},
mode = {label = "Mode", type = "list", items = {"A", "B", "C"}, default = "A"},
}
-- Access as: Parameters.speed, Parameters.count, etc.
Four Plugin Types:
-
Pointer Plugin - Returns single
Transform- Modifies the user's primary brush pointer position while user draws
-
Symmetry Plugin - Returns
Pathor list ofTransform- Controls multiple brush pointers while the user draws
- Each transform generates one additional stroke
- Important to understand its special use of coordinate spaces (especially the symmetry widget)
- Can be combined with Pointer plugins
- Overrides and replaces mirror or multimirror symmetry modes
-
Tool Plugin - Returns
PathorPathList- Generates a complete stroke or strokes in one action based on the users actions
- Return the stroke data as Path or PathList in Main() - do not use Path:Draw() etc directly
- Typically triggered on button press/release
- Active mirror, multimirror or symmetry plugin modes are automatically applied to the output
-
Background Plugin - Returns nothing
- Runs autonomously every frame
- Draws strokes using explicit
Draw()methods
Important Constraints:
- Brush type cannot change during a stroke - only between strokes
- Brush size cannot change during a stroke but modifying pressure within a stroke often affects size
- Brush color cannot change during a stroke but color overrides can be applied within a stroke.
- Understand coordinate spaces - default varies by plugin type (check Settings.space)
- Transform scale component affects stroke width/thickness
For complete details, examples, and edge cases, read references/instructions.md
Common Gotchas
-
Coordinate Spaces: By default, Pointer/Tool plugins use
space="pointer"(relative to brush hand) while Symmetry plugins use the symmetry widget as origin. Override withSettings.space="canvas"orSettings.space="pointer". -
Path Smoothing: Open Brush smooths paths for hand-drawn strokes. For geometric shapes, add extra points with
Path:SubdivideSegments(n)to prevent rounding. -
Multiple Active Plugins: You can run multiple Background plugins simultaneously, but only one of each other type (Pointer/Symmetry/Tool). You can combine the effects of Pointer and Symmetry. Or Symmetry and Tool.
Plugin Development Workflow
When helping users with Open Brush Lua plugins:
- Check example plugins for similar functionality - Consult both
references/examples/(actual code) andreferences/guides/example-plugins/(explanations). The examples demonstrate working patterns. - Verify API calls - Check
references/lua-modules/__autocomplete.luabefore using any API methods or properties - Ask clarifying questions about what the plugin should do before writing code
- Provide complete, working examples that users can copy and test
- Consider performance - Warn if operations might be slow (e.g., processing thousands of strokes every frame)
Example Plugin Templates
Simple Stroke Counter (Background Plugin)
Settings = {
description = "Counts total strokes in sketch"
}
local hasCountedOnce = false
function Main()
if not hasCountedOnce then
local sketch = App.sketch()
local strokes = sketch:strokes()
local count = strokes:count()
print("Total strokes: " .. count)
hasCountedOnce = true
end
end
Position Logger (Background Plugin)
Settings = {
description = "Logs user position every 2 seconds"
}
local timer = Timer:New()
function Start()
timer:Start()
end
function Main()
if timer:Elapsed() > 2.0 then
local pos = User.position()
print(string.format("User at: %.2f, %.2f, %.2f", pos.x, pos.y, pos.z))
timer:Reset()
end
end
Additional Resources
- API Documentation Repository: https://github.com/icosa-foundation/open-brush-plugin-scripting-docs
- Main Documentation Repository: https://github.com/icosa-foundation/open-brush-docs
- Example Plugins: Browse the
LuaScriptExamplesdirectory in the API docs repo for real-world plugin examples
