Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/danielitoCode/Spatial/llms.txt

Use this file to discover all available pages before exploring further.

CameraState is a @Stable Compose state holder that represents Spatial’s orbit camera. It exposes the current camera orientation as observable Compose state properties (yaw, pitch, zoom) so your UI recomposes automatically whenever the camera moves — whether driven by touch gestures, programmatic calls, or running animations. You create one with rememberCameraState() and pass it to Scene.

Creating CameraState

val cameraState = rememberCameraState(
    yaw   = 20f.deg,
    pitch = (-12f).deg,
    zoom  = 1.25f,
)
ParameterTypeDescription
yawAngleHorizontal rotation around the world Y-axis, in degrees. 0° faces along the −Z axis.
pitchAngleVertical tilt in degrees. Positive values tilt up; clamped to a safe orbit range to prevent camera flipping.
zoomFloatVisual magnification factor. 1.0 is the default field of view. Values greater than 1.0 appear closer; values less than 1.0 appear farther away.

Reading Camera State

All four properties are backed by Compose mutable state, so reading them inside a composable registers a recomposition subscription:
val snapshot = cameraState.snapshot()
Text(
    "yaw=${"%.2f".format(snapshot.yaw)}  " +
    "pitch=${"%.2f".format(snapshot.pitch)}  " +
    "zoom=${"%.2f".format(snapshot.zoom)}"
)
The observable properties on CameraState itself are:
PropertyTypeDescription
yawAngleCurrent horizontal orbit angle.
pitchAngleCurrent vertical tilt angle.
zoomFloatCurrent zoom level.
versionLongMonotonically increasing counter incremented on every camera change.
sourceCameraUpdateSourceIdentifies what last moved the camera — Gesture, Remote, or Animation.

Programmatic Control

These methods update the camera synchronously and trigger an immediate recomposition:

orbitTo

Snaps the camera to an absolute yaw and/or pitch position:
cameraState.orbitTo(yaw = 180f.deg, pitch = (-15f).deg)

orbitBy

Applies a relative orbit delta — useful for button-driven nudges:
cameraState.orbitBy(deltaYawDegrees = 10f, deltaPitchDegrees = 0f)

zoomTo

Sets an absolute zoom level immediately:
cameraState.zoomTo(zoom = 2f)

zoomBy

Applies a multiplicative zoom delta. Values above 1f zoom in; values below 1f zoom out:
cameraState.zoomBy(scaleDelta = 1.1f) // zoom in 10%
cameraState.zoomBy(scaleDelta = 0.9f) // zoom out 10%

jumpTo

Instantly sets yaw, pitch, and zoom all at once — no animation, no intermediate frames:
cameraState.jumpTo(yaw = 0f.deg, pitch = 0f.deg, zoom = 1f)

syncSnapshot

Syncs the camera from an external CameraSnapshot — useful for restoring saved camera state:
cameraState.syncSnapshot(savedSnapshot)

Animated Transitions

animateTo is a suspend fun that smoothly animates the camera to a new orientation. Call it from a CoroutineScope:
val scope = rememberCoroutineScope()
val cameraSnapshot = cameraState.snapshot()

Button(
    onClick = {
        scope.launch {
            cameraState.animateTo(
                yaw   = (cameraSnapshot.yaw + 90f).deg,
                pitch = (-18f).deg,
                zoom  = 0.9f,
            )
        }
    }
) {
    Text("Rotate camera")
}
animateTo parameters:
ParameterTypeDefaultDescription
yawAngleCurrent yawTarget horizontal orbit angle.
pitchAngleCurrent pitchTarget vertical tilt. Automatically clamped to the safe orbit range.
zoomFloatCurrent zoomTarget zoom level.
durationMillisLong?nullOptional explicit duration that overrides the adaptive planner.
motionMotionSpecMotionSpec.AdaptiveControls how duration and easing are computed.
Omitting any parameter keeps that axis at its current value. For example, animateTo(zoom = 2f) animates only the zoom while leaving yaw and pitch unchanged.

Pitch Clamping

Pitch is automatically clamped to a safe orbit range on every update — both from gestures and programmatic calls. This prevents the camera from flipping over the poles, which would invert the orbit controls and feel disorienting. The clamping constants are defined on CameraSnapshot and applied transparently inside CameraState.

CameraUpdateSource

Every camera mutation records which subsystem initiated the change in the source property:
ValueMeaning
CameraUpdateSource.GestureThe user dragged or pinched the viewport.
CameraUpdateSource.RemoteA programmatic call (orbitTo, zoomTo, jumpTo, etc.) updated the camera.
CameraUpdateSource.AnimationA running animateTo coroutine is driving the camera.
The runtime uses source to apply conflict-resolution policies — for example, a gesture event while an animation is running cancels the animation rather than fighting it.
  • Gestures — configure touch gesture modes for the Scene
  • Motion — control animation duration and easing with MotionSpec

Build docs developers (and LLMs) love