Events & Interactivity

Scenes are not just videos — in the WASM player (and any host that embeds the engine) objects can react to input. Interactivity is declared in the scene (events), dispatched through an event bus, and resolved against the timeline. Everything stays deterministic: an event changes playback state or property overrides, and rendering remains a pure function of time + state.

Declaring events

Each entry binds one object and one trigger to one action:

"events": [
  { "object": "play_button", "trigger": "click",
    "action": { "type": "play_from", "value": 0.0 } },
  { "object": "node_3", "trigger": "click",
    "action": { "type": "show_tooltip", "text": "Hidden layer, ReLU" } }
]

trigger is a free-form string matched exactly; the host decides what gestures produce which triggers (the JS SDK maps canvas clicks through hit_test to click on the topmost hit object).

Actions

typeFieldsEffect
jump_to_timevalueSeek the playhead (seconds).
play_fromvalueSeek and start playback.
pausePause playback.
set_propertytarget, property, valueOverride a property immediately.
tween_totarget, property, value, duration, easingAnimate a property to a value from the current playhead.
show_tooltiptextAsk the host to display a transient overlay.
emit_customevent_name, payloadSend a named event with payload to the host application.

In emit_custom payloads, $drag.* placeholders (e.g. "$drag.from", "$drag.to") are substituted from the incoming event's payload at dispatch time — useful for wiring drag gestures back into application logic.

The event bus

lumina_core::EventBus owns a PlaybackState and dispatches host events:

  • The host constructs an Event { object_id, trigger, payload } (usually from hit_test) and calls process_event.
  • Every declared entry matching that object + trigger fires.
  • The returned EventOutcome { actions, current_time, playing, emitted } tells the host what to do: update its clock, apply overrides, show tooltips, forward emitted events.

In the browser this is wrapped by LuminaEngine.process_event / LuminaEngine.hit_test(x, y, time); hit-testing is geometry-aware for all 17 object types (polygon ray-casting, segment distance for lines and béziers, recursive group transforms) and respects z-order.

Scene patching

For programmatic editing — AI loops, editors, live-coding — the engine offers semantic patch operations that understand the scene's structure (unlike raw RFC-6902 JSON Patch, which is also available):

OpEffect
add_object / remove_objectInsert or delete an object; removal cascades to its timeline entries, events, and group memberships.
update_propertyChange an object's initial property.
add_keyframe / update_keyframe / remove_keyframeEdit timeline entries for one object + time.
add_event / remove_eventEdit interactivity declarations.
update_canvasChange canvas dimensions/fps/duration/background.

Apply them in-process via lumina_core::scene_patch::apply_patch, or over HTTP with POST /scene_patch — the server applies the patch and re-validates the scene in one round trip, returning structured errors with fix_suggestions on failure (see the AI Integration Cookbook).