Skip to content

Events

Events trigger scripts. Scenery has two kinds: object events that fire for a specific object, and scene events that fire globally.

Object events

Attach to individual objects. The triggering object is available in scriptContext.sourceEvent.

EventFires when
On Will AppearObject is about to be added to scene
On Did AppearObject has appeared
On RemovalObject is removed from scene
On Tap GestureObject is tapped
On GestureObject is dragged, rotated, or resized (needs a gesture enabled)
On Camera CollisionCamera enters/exits object bounds
On Object CollisionPhysics collision with another object
Distance FieldCamera or target crosses distance threshold
Look DirectionUser looks at/away from object (or object looks at target)
Media PlaybackVideo/audio begins, reaches timestamp, or ends
javascript
// On Tap Gesture – scale up the tapped object
scriptContext.sourceEvent.objectEntity.animateTo(
    { scale: Vector3(1.2, 1.2, 1.2) },
    0.2
);

Scene events

Fire regardless of which object is involved.

EventFires when
On Experience StartExperience begins
On Experience LoadExperience finishes loading
On Screen TapUser taps anywhere on screen
On RenderEvery frame
On ScheduleAfter a delay or at specific time
On Variable ChangeA variable is modified
On Audio AnalysisAudio levels update (ambient or microphone)
javascript
// On Render – rotate every frame
var cube = scene.findEntity("YOUR_OBJECT_ID");
cube.representation.rotation = cube.representation.rotation.multiply(
    Rotation(0, scriptContext.sourceEvent.deltaTime, 0)
);

Subscribing in code

You can also subscribe to events programmatically:

javascript
var cube = scene.findEntity("YOUR_OBJECT_ID");

// Object event
cube.on('tap', function(e) {
    e.objectEntity.animateTo({ scale: Vector3(1.2, 1.2, 1.2) }, 0.2);
});

// Scene event
scene.on('render', function(e) {
    cube.representation.rotation = cube.representation.rotation.multiply(
        Rotation(0, e.deltaTime, 0)
    );
});

// One-time listener
scene.once('start', function() {
    console.log('Experience started');
});

Throttling

For expensive operations, throttle the render loop:

javascript
scene.on('render', { throttle: 0.1 }, function(e) {
    // Runs every 100ms instead of every frame
});

Unsubscribing

javascript
var eventId = cube.on('tap', function() {});
// Later...
cube.off(eventId);

objectEntity vs representationEntity

Tap and collision events give you both objectEntity (the top-level container) and representationEntity (the specific visual that was interacted with). Use objectEntity when you need to find sibling representations like audio sources.

Gesture and screen events

Beyond taps, you can react to live manipulation, multi-tap gestures, and raw screen gestures.

Manipulating an object

If an object has a gesture enabled (Drag, Rotate or Resize), on('gesture') fires continuously while the user manipulates it, so you can react to the movement itself. Read e.phase ('began' / 'changed' / 'ended') and, by gesture kind, e.translation / e.rotation / e.scale (world-space):

javascript
box.on('gesture', function(e) {
    if (e.phase === 'ended') {
        console.log('let go');
    }
});

doubleTap and longPress are discrete object gestures – these are available in code (not as editor triggers):

javascript
box.on('doubleTap', function() { /* ... */ });
box.on('longPress', { minimumDuration: 0.5 }, function() { /* ... */ });

Screen gestures

pan, pinch and rotate fire on any such gesture anywhere on screen, independent of scene objects – the raw input for mechanics like flick-to-launch. Read e.position (normalized) and, by kind, translation / scale / rotation plus velocity:

javascript
// Flick up in the bottom of the screen to launch the ball
scene.on('pan', { area: [0, 0.6, 1, 0.4], phase: 'ended' }, function(e) {
    // e.velocity is in screen points/sec – scale it to a world-space push (tune to taste)
    var power = Math.max(0, -e.velocity.y) * 0.002;
    ball.applyImpulse(new Vector3(0, power, -power * 2), { space: 'world' });
});

Outside AR (the desktop preview, the Mac app), a one-finger pan also orbits the camera by default. So if your handler reacts to a pan, that one gesture does two things at once – your mechanic runs and the camera swings. consume prevents that: return true from its predicate and the gesture is handled by your script alone, so the camera ignores it. The predicate runs as the gesture begins, so you can claim only the pans that start on your object and leave the rest for the camera:

javascript
// Claim the pan only when it starts on the ball; every other pan still orbits the camera
scene.on('pan', { consume: function(e) { return e.hits(ball); } }, function(e) { /* ... */ });

Omit consume for the smart default – a gesture is claimed automatically when it begins on a scene object. See scene.on for the full payload and consume options.

Screen gestures are for touch screens

pan, pinch and rotate fire on iPhone, iPad and Mac. On Apple Vision Pro input is spatial (you look and pinch), so screen gestures aren't delivered – drive interaction from UI panels and buttons there instead. Gate a portable script with environment.features.has('screenGestures') and provide a fallback.

Event data

How you access event data depends on how your script runs:

Run Script action – use scriptContext.sourceEvent:

javascript
var entity = scriptContext.sourceEvent.objectEntity;
var dt = scriptContext.sourceEvent.deltaTime;

Event subscriptions – data is passed to your callback:

javascript
scene.on('render', function(e) {
    var dt = e.deltaTime;
});

cube.on('tap', function(e) {
    var tapped = e.objectEntity;
});

Properties by event type:

EventProperties
renderdeltaTime, time
tapobjectEntity, representationEntity, screenPosition
gestureobjectEntity, representationEntity, phase, translation/rotation/scale
doubleTap, longPressobjectEntity, representationEntity
pan, pinch, rotateposition, beganPosition, phase, translation/scale/rotation, velocity
screenTapposition
distanceobjectEntity, distance, crossed
lookAtobjectEntity, isLooking
cameraCollisionobjectEntity, representationEntity
physicsCollisionobjectEntity, representationEntity, position, impulse
variableChangevariableId, value
mediaPlaybackobjectEntity, status, time, duration
audioAnalysiskind, loudness, bass, lowMid, mid, upperMid, presence, brilliance, air, treble
add, appear, removeobjectEntity, representationEntity

TIP

Use console.log(scriptContext.sourceEvent) or console.log(e) to inspect all available properties.