Events API
Events and Methods covers the everyday workflow — click a button, respond to a collision. This is the reference layer underneath: the complete event catalog, exactly what each event object carries, and how bubbling and compilation actually work.
The full event catalog
Every node gets the same base set of attachable events — not just the ones you'd expect from a mouse:
| Event | Fires when |
|---|---|
| `click` | Pointer click on the node |
| `dblclick` | Double-click |
| `mousedown` / `mouseup` | Pointer button pressed / released on the node |
| `mouseenter` / `mouseleave` | Pointer enters / leaves the node's bounds |
| `pointerdown` / `pointerup` | Same as mousedown/up, under the pointer-event family |
| `pointerenter` / `pointerleave` | Same as mouseenter/leave, under the pointer-event family |
| `tick` | Fires every frame during play — the event-driven counterpart to reading time/frame/dt reactively in a formula |
collide is added to this list specifically when a node has the Physics module attached — it isn't offered on a node that doesn't collide with anything. Some node types add their own: a List node, for instance, contributes an itemClick event on top of the common set. A component definition can also declare its own custom events, which then show up in the "Add Event" dropdown for every instance of that component, the same way a built-in event would.
One nuance worth being precise about: mousemove and pointermove genuinely exist and bubble through the node hierarchy internally, but they aren't in the catalog that populates the panel's "Add Event" dropdown — so while the mechanism handles them, they're not something you attach your own handler to from the properties panel today.
Event objects
Mouse and pointer events carry:
event.target // VNode — the node that was interacted with
event.x // number — pointer X in world coordinates
event.y // number — pointer Y in world coordinates
event.offsetX // number — pointer X relative to the node
event.offsetY // number — pointer Y relative to the node
event.button // number — 0 = left, 1 = middle, 2 = right
event.ctrlKey // boolean
event.shiftKey // booleanoffsetX/offsetY are lazy — they're only computed if your handler code actually reads them, since deriving a node-relative offset requires inverting that node's transform matrix, work that's skipped entirely when you don't need it.
The collide event carries a different, physics-specific shape:
event.target // VNode — the other node in the collision
event.targetId // string — name of the other node
event.collider // string — name of this node's collider
event.targetCollider // string — name of the other node's collider
event.normal // {x, y} — contact surface normal
event.point // {x, y} — contact point in world coordinatesAutocomplete is context-aware: type event. inside a handler, and the suggestions shown depend on which event type that handler is actually attached to — a click handler sees the mouse-event shape, a collide handler sees the collision shape, not a generic merged list of every possible field.
Writing a handler
In the properties panel: select a node, open its Events section, click Add Event, choose the event type, and write the handler body directly in the code editor. Inside that body, this refers to the node the handler is attached to, and event carries whatever that event type provides.
// Simple: rotate on click
this.rotation += 15
// Conditional: react only to a specific kind of collision
if (event.target.is("bullet")) {
this.destroy()
}
// Stateful: track clicks across calls
this.clickCount = (this.clickCount || 0) + 1
if (this.clickCount >= 3) {
this.destroy()
}Calling methods from events
A method is a named function defined on a node — like an event handler, but callable on demand rather than tied to a trigger. Define one the same way (switch the panel's action dropdown from an event type to Methods, name it, write the body), then call it from any event handler:
this.reset() // call a method on the node itself
Ball.launch() // call a method defined on a different nodeMethods can technically be called from a formula too, but that's a side effect inside something meant to be a pure value — events are where method calls belong.
Bubbling
Mouse and pointer events bubble through the node hierarchy the same way DOM events do — click on a child inside a frame, and the frame's own click handler fires too, walking from the deepest node up through each parent frame to the root. Call event.stopPropagation() inside a handler to stop that walk at the current node.
// On a child node — absorb the click, never let it reach the parent frame
event.stopPropagation()
this.opacity = 0.5Compilation
Event handlers compile through the same pipeline as formulas, in the event compile mode: parsed as a full statement block, node references rewritten to ctx.resolve(), method calls rewritten to ctx.callMethod(), and the same inline VLQ source maps attached so a handler is debuggable in DevTools exactly like a formula is. A compile error logs to the console with line and column information, and — importantly — if a handler fails to compile, whatever handler was previously working stays in place. Broken code never silently replaces working code.
Code editor shortcuts
| Shortcut | Action |
|---|---|
| Tab / Shift+Tab | Indent / dedent (multi-line aware) |
| Ctrl+D | Duplicate line |
| Ctrl+/ | Toggle comment |
| Ctrl+Shift+K | Delete line |
| Ctrl+Enter | Commit and close |
| Shift+Enter | Apply without closing |
| Ctrl+F | Search |
For the day-to-day story - why you'd reach for an event vs. a formula, and worked examples - see Events and methods. For the compiler internals these handlers share with formulas, see Formula compiler and runtime Internals.