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