Architecture

Components

Build once. Use everywhere. Typed I/O, instances, logic.

Group shapes into reusable components with typed inputs, computed outputs, and encapsulated logic. Instances share the definition but each has its own state.


From shapes to building blocks

Select a group of shapes. Click “Create Component.” Now you have a reusable building block with a defined interface.

Button component

A reusable button with label, color, and hover state


Inputs and outputs

Inputs

Parameters that control the component from outside. Set by the parent scene or wired with formulas.

Component "Button"
  Inputs:
    label: string = "Click me"
    color: Color = "#3498DB"
    width: number = 200
    disabled: boolean = false

Inside the component, formulas reference inputs with $input:

Background.color = if($input.disabled, "#999", $input.color)
Background.width = $input.width

Outputs

Computed values exposed to the parent. Read-only from outside.

  Outputs:
    isHovered: boolean
    computedHeight: number

Parent formulas can read outputs:

StatusText.text = if(Button.isHovered, "Ready", "Waiting")

How it compares

AspectFigmaFlashUnityFormo
ParametersVariant props, text overridesMovieClip varsInspector fieldsTyped inputs, any type
OutputsNoneNonePublic fieldsComputed outputs
Internal logicNoneActionScriptC# scriptsReactive formulas
ExportStatic assets.swfGame binaryReact components
Event handlingNoneMovieClip eventsUnityEventFormula events

Instances

Place a component — the editor creates an instance. Internal nodes are cloned from the definition. Each instance has independent state.

Change the definition — all instances update. Override an input on one instance — only that instance changes.

Scene
├── Button "Save"     → label: "Save",   color: "#2ECC71"
├── Button "Cancel"   → label: "Cancel", color: "#E74C3C"
└── Button "Delete"   → label: "Delete", color: "#E74C3C", disabled: true

Three buttons. One definition. Different inputs.


Non-visual components

Not all components draw to the canvas. Non-visual components are logic-only — like Delphi’s Timer or VB’s Winsock control. They appear in the scene tree with an icon but have no canvas representation.

Timer

Component "Timer" (non-visual)
  Inputs:  interval: number = 1000, active: boolean = false
  Outputs: elapsed: number, tick: event

API Connection

Component "ApiConnection" (non-visual)
  Inputs:  url: string, method: string = "GET"
  Outputs: data: any, loading: boolean, error: string
  Actions: fetch()

State

Component "State" (non-visual)
  Inputs:  initialValue: any
  Outputs: value: any
  Actions: set(val), reset()

Wire non-visual outputs to visual components:

ApiConnection "api"  → loading: boolean
Spinner component    → visible = api.loading
DataTable component  → data = api.data

Custom editors

Build custom inspector panels for your components. When a user selects an instance, the custom editor appears instead of the default property list.

Editor for "DataChart"
├── "Chart Type" → Dropdown bound to $target.input.chartType
├── "Primary Color" → ColorInput bound to $target.input.primaryColor
├── "Bar Width" → Slider bound to $target.input.barWidth
├── "Show Legend" → Checkbox bound to $target.input.showLegend
└── "Reset" → Button triggers resetDefaults($target)

Custom editors are themselves components — components all the way down. The default properties panel, the timeline, even the toolbar are components. Learn one system, customize everything.


Component slots

Components can define slots — typed inputs that accept nodes from the parent scene. Useful for IK targets, eye tracking, snap points.

Component "Character"
  Slots:
    leftFootTarget:  VNode  ← drag a node here
    rightFootTarget: VNode
    lookAtTarget:    VNode

During authoring, each slot creates a placeholder node for testing. At runtime, placeholders are replaced by actual scene nodes.

Formulas reference slots naturally:

EyeRotation = atan2($lookAtTarget.y - this.y, $lookAtTarget.x - this.x)

Nesting

Components contain other component instances. Inputs wire through the hierarchy:

Page component
├── Input: theme.primaryColor
└── Contains: Button instance
    └── Input: color = $input.theme.primaryColor

Change the page theme — all buttons update. One formula, unlimited depth.


Export

Components map directly to React components:

// Auto-generated from Component "Button"
function Button({ label = "Click me", color = "#3498DB", disabled = false }) {
  return (
    <button style={{ backgroundColor: disabled ? "#999" : color }}>
      {label}
    </button>
  );
}

Non-visual components become custom hooks:

function useTimer(interval: number, active: boolean) {
  const [elapsed, setElapsed] = useState(0);
  useEffect(() => {
    if (!active) return;
    const id = setInterval(() => setElapsed(e => e + interval), interval);
    return () => clearInterval(id);
  }, [interval, active]);
  return { elapsed };
}

Export details


Join the Beta →