Develop

Body tracking sample

Updated: May 8, 2026

Overview

Body tracking in the Spatial SDK is an experimental API that requires explicit opt-in. This sample demonstrates how to use the low-level body tracking API to retrieve joint poses and skeleton topology in real time, and visualize the tracked skeleton using procedural geometry.

Learning objectives

Complete this guide to learn how to:
  • Configure body tracking with joint sets and fidelity levels
  • Poll for joint pose data each frame and check validity flags
  • Detect skeleton topology changes and rebuild visualization geometry
  • Transform joint pose data into 3D spatial representations
  • Clean up body tracking resources when the session ends

Requirements

You’ll need:
  • Meta Quest 2, Quest 3, or Quest 3S
  • Android development environment with Spatial SDK
  • Body tracking capability enabled on your device
For complete SDK setup and toolchain requirements, see the sample README.

Get started

Clone the Meta Spatial SDK Samples repository and open the BodyTrackingSample project in Android Studio. Build and deploy the sample to your Quest device.
For detailed build instructions, see the sample README.

Explore the sample

File / SceneWhat it demonstratesKey concepts
Complete body tracking lifecycle: configuration, per-frame polling, visualization, cleanup
setBodyTrackingJointSet(), updateBodyTrackingBuffersAtTime(), skeleton change detection
Required permissions and device features for body tracking
com.oculus.software.body_tracking, com.oculus.experimental.enabled

Runtime behavior

When you run this sample, you see a real-time visualization of your tracked skeleton rendered as a collection of thin blue boxes connecting each joint to its parent. As you move, the skeleton updates each frame to match your body pose. The visualization covers all 84 joints of the full-body tracking model, from the root through the spine, head, shoulders, arms, hands, and legs. The sample only updates bone positions when joint validity flags confirm the tracking data is usable.

Key concepts

Experimental API opt-in

All body tracking APIs are annotated with @SpatialSDKExperimentalAPI and require explicit opt-in at the call site:
@OptIn(SpatialSDKExperimentalAPI::class)
override fun onSceneReady() {
    scene.setBodyTrackingJointSet(JointSet.FULL_BODY)
}
This signals that the API surface is subject to change in future releases.

Three-stage lifecycle pattern

The sample follows a configure-poll-cleanup pattern. In onSceneReady(), the activity calls setBodyTrackingJointSet() and setBodyTrackingFidelity() to configure tracking before the first frame. In onSceneTick(), it calls updateBodyTrackingBuffersAtTime() each frame to retrieve current joint data. In onDestroy(), it calls releaseBodyTrackingBuffers() to free native memory.

Skeleton change detection

The sample tracks getSkeletonChangedCount() to detect when the skeleton topology changes. This monotonically increasing counter increments whenever the joint hierarchy or parent-child relationships change. When the count changes, the sample destroys existing visualization entities and recreates them using the new skeleton structure from the skeletonPoses list. See the change detection logic in onSceneTick().

Joint validity and tracking quality

Each JointPose includes a flags field that indicates tracking state. The sample checks flags and JointPose.ValidBits != 0 before using pose data. The constants LocationValidBit, LocationTrackedBit, OrientationValidBit, and OrientationTrackedBit allow fine-grained quality checks. See the validity check in the bone visualization loop in BodyTrackingSampleActivity.kt.

Bone visualization with oriented geometry

The sample visualizes each bone as a thin blue box stretched between the child joint and its parent. It calculates the bone length as the distance between the two positions, then uses Quaternion.lookRotation() to orient the box along the bone vector. This transforms joint pose data into 3D spatial geometry. See the bone creation code in BodyTrackingSampleActivity.kt.

Extend the sample

  • Replace visualization geometry: Swap the blue box visualization with custom 3D models or particle effects to create stylized skeleton representations
  • Add runtime controls: Build UI controls to toggle between JointSet.DEFAULT (upper body) and JointSet.FULL_BODY at runtime
  • Implement gesture detection: Analyze joint positions and velocities over time to recognize specific body poses or movements