LeCodesdocs

ARScene

A Scene rendered over the device camera feed with world tracking — everything from Scene (options, add/remove, events, overlays) applies. What it adds: an async open() that requests the camera and starts tracking, anchor nodes that follow the real world, and ready-made object-placement gestures via addControls.

At a glance

TypeScript
const scene = new ARScene()
const model = await Model.load(asset('./chair.glb'))
scene.root.add(model)
scene.add(model)

const controls = scene.addControls(model)     // drag / pinch / twist to place it
await scene.open()

model.addEventListener('track', () => console.log('anchored to the world'))

Creating & opening

TypeScript
new ARScene(options?: SceneOptions & {
  mode?: 'default' | 'markers' | 'arcore' | 'detached'   // tracking mode; default 'default'
})

scene.mode                         // read-only: the mode it was created with
scene.useWarmRender = true         // open() pre-warms shaders first (default true)

await scene.open(): Promise<void>  // request camera → warm render → launch AR → become active
scene.close(): void                // stop the AR session and tear down the view

open() is asynchronous — it asks for camera permission, warm-renders (when useWarmRender is on), and launches tracking before the scene becomes active. It rejects if the camera is denied or AR can't launch, so await it and handle failure.

Anchors

TypeScript
scene.root: Node                                              // world root anchor (read-only)
scene.createAnchor(source: FetchResponse, physicalWidth = 0.2): Node   // image-target anchor

An anchor is a Node whose transform is driven by tracking — parent your content under it. root is the world origin anchor; createAnchor makes one that follows a tracking source (e.g. an image target fetched with fetch), with physicalWidth the target's real-world width in meters (default 0.2).

Anchor nodes report tracking state:

TypeScript
anchor.addEventListener('track', () => {})      // began tracking
anchor.addEventListener('untrack', () => {})    // lost tracking
anchor.isTracked                                // current state (false for non-anchor nodes)
Note

root is not available in 'markers' mode. Anchor support is host-gated — the web viewer does not implement createAnchor.

Placement controls — addControls

TypeScript
scene.addControls(target: Node, options?: {
  minScale?: number   // smallest uniform scale the pinch allows; default 0.08
  maxScale?: number   // largest; default 2
  pan?: boolean       // one-finger drag; default true
  pinch?: boolean     // two-finger scale; default true
  twist?: boolean     // two-finger rotate; default true
}): { remove(): void }

The staple AR object-placement interaction, prebuilt:

  • One finger drags the target across the ground plane, relative to where the camera looks (screen-up moves it away from you). Height (world Y) stays fixed.
  • Two fingers pinch to scale (uniform, clamped by minScale/maxScale) and twist to rotate the target around its Y axis.

Built entirely on the public touch API (scene touchstart + ev.track) — it's a convenience, not a primitive. Call .remove() on the returned handle to detach the listeners.

Note

the pinch clamp allows slight overshoot past the stated bounds — the effective range is [minScale × 0.4, maxScale × 1.5].

See also

  • Scene — everything inherited: options, add/remove, overlays, warmRender, events
  • Nodetrack/untrack events, isTracked, hierarchy
  • Model — loading the GLB you're placing
  • Pointer events & gestures — the touch surface addControls is built on