One of ShapeBox's most powerful — and underappreciated — features is its plugin system. Under the hood, ShapeBox itself is built as a collection of first-party plugins. The same API your plugins use is the one we use every day.
This post is a technical deep dive, aimed at developers who want to extend ShapeBox for their own workflows or publish to the marketplace.
Plugin anatomy
A ShapeBox plugin is a TypeScript package that exports a default Plugin object:
import { definePlugin } from '@shapebox/sdk'
export default definePlugin({
id: 'my-awesome-plugin',
version: '1.0.0',
name: 'My Awesome Plugin',
setup(ctx) {
// Register UI panels, components, commands, and hooks here
ctx.registerPanel({
id: 'my-panel',
label: 'Awesome',
render: () => import('./components/MyPanel'),
})
},
})
The plugin context (ctx)
The ctx object exposes the full ShapeBox API surface:
ctx.scene— read and write the 3D scene graph.ctx.selection— observe and manipulate the current selection.ctx.history— push undoable commands.ctx.registerPanel()— add a UI panel to the sidebar.ctx.registerCommand()— add a keyboard-shortcut command.ctx.registerPropertyEditor()— override how a specific property type renders.ctx.hooks— React-style hooks for reactive state.
Lifecycle hooks
Plugins can hook into the editor lifecycle:
setup(ctx) {
ctx.hooks.onSceneLoad(() => {
console.log('Scene loaded!')
})
ctx.hooks.onSelectionChange((selected) => {
console.log('Selected objects:', selected.map(obj => obj.id))
})
}
Writing a custom physics behaviour
Here's a minimal plugin that adds a "Floaty" behaviour — objects gently bob up and down:
import { definePlugin, PhysicsBehaviour } from '@shapebox/sdk'
class FloatyBehaviour extends PhysicsBehaviour {
private time = 0
private amplitude = 0.5
private frequency = 1
tick(dt: number) {
this.time += dt
const offset = Math.sin(this.time * this.frequency * Math.PI * 2) * this.amplitude
this.object.position.y = this.object.initialPosition.y + offset
}
}
export default definePlugin({
id: 'floaty-behaviour',
version: '1.0.0',
name: 'Floaty',
setup(ctx) {
ctx.registerBehaviour('floaty', FloatyBehaviour, {
properties: [
{ key: 'amplitude', type: 'number', default: 0.5, min: 0, max: 5 },
{ key: 'frequency', type: 'number', default: 1, min: 0.1, max: 10 },
],
})
},
})
Testing your plugin locally
# Install the ShapeBox CLI
bun add -g @shapebox/cli
# Start the dev server with hot-reload
shapebox dev
# Open ShapeBox and go to Settings → Plugins → Load Local Plugin
The CLI watches your source files and hot-reloads the plugin in the editor without a full page refresh.
Publishing to the marketplace
Once you're happy with your plugin:
shapebox publish
This runs type-checking, bundles your plugin, and submits it for review. After a brief (usually < 24h) review, your plugin goes live in the marketplace.
Best practices
- Keep plugins focused — do one thing well.
- Respect the undo stack — always use
ctx.history.push()for mutations so users can undo. - Support dark mode — ShapeBox is dark by default; test both themes.
- Write types — the SDK is fully typed; use it.
- Document your properties — add
descriptionstrings to every registered property.
Have questions about the plugin API? Join us in #plugin-dev on Discord. We're always happy to help.