Camera
The scene camera, reached at scene.camera. It's a Node, so the full transform
surface works — position, eulerAngles, lookAt, forward. On top of that it exposes
projection reads and screen→world ray helpers for picking.
You never construct one: every Scene creates its own camera (overlays share the
parent's). In an ARScene the camera is driven by device tracking — read it, don't
place it.
At a glance
scene.camera.position = [0, 2, 4]
scene.camera.lookAt([0, 0.5, 0])
scene.addEventListener('touchstart', ev => {
const ray = scene.camera.getRay(ev.clientX, ev.clientY)
const hit = Physics.raycast(ray.origin, ray.dir)
if (hit) console.log('tapped', hit.node?.name)
})Projection
camera.fov // vertical field of view, DEGREES — settable (default 60)
camera.near // near clip distance, settable (default 0.01)
camera.far // far clip distance = view range, settable (default 1000)
camera.setProjection({ fov: 45, far: 5000 }) // any subset, one call
camera.horizontalFov // horizontal field of view, degrees (read-only, derived from fov + aspect)
camera.displaySize // [width, height] of the 3D viewport, px (read-only)
camera.projectionMatrix // Float32Array(16), live — the host updates it in place on resize/fov changeThe aspect ratio stays host-owned: you set the lens, the host keeps it across every resize. Nothing
nearer than near or further than far draws, so far is the scene's view range — raise it for
open worlds. Shadows keep their own (shorter) range, so a long view distance costs nothing in
shadow sharpness.
In a scene file the same three live on the camera block:
camera: { position: [0, 6, 12], target: [0, 0, 0], fov: 45 } // top-level block
nodes: { cam: { camera: { fov: 45, far: 5000 } } } // …or the camera NODE (it wins)Hosts that predate the projection call keep the fixed defaults — setting it there is inert, not an error. See
docs/parity/MATRIX.md(gl-camera-projection).
Screen → world rays
camera.getViewDirection(screenX, screenY): Vec3 // world-space ray direction through a screen point
camera.getRay(screenX, screenY): Ray // origin = camera.worldPosition, dir = view directionScreen coordinates are logical px — the same space as ev.clientX / ev.clientY. getRay
pairs directly with Physics.raycast for tap-picking; Ray is documented in
ray-plane-noise.
there is no worldToScreen helper — the inverse projection is only available raw, via
projectionMatrix + the camera's worldMatrix.
Pitfalls
// ✗ moving the camera in an ARScene — tracking overwrites it every frame
arScene.camera.position = [0, 2, 4]
// ✓ move the WORLD instead: parent content under scene.root and place thatSee also
- Scene — where the camera comes from; overlays share it
- Node — the inherited transform surface (
position,lookAt,forward) - 3D physics & shapes — raycasting the rays you build here
- Ray, Plane, Noise — the
Rayreturned bygetRay