Develop

Gaussian Splat sample

Updated: May 8, 2026

Overview

The Gaussian Splat sample demonstrates how to render photorealistic 3D scene captures using the experimental Splat API in Spatial SDK. You learn how to register the splat feature, create splat entities, swap between scenes at runtime, and build a Jetpack Compose control panel for user interaction. The Splat API is experimental and requires opt-in via @OptIn(SpatialSDKExperimentalSplatAPI::class).

Learning objectives

Complete this guide to learn how to:
  • Register SplatFeature and enable experimental splat rendering
  • Create splat entities from bundled .spz assets using apk:// URIs
  • Swap between splats dynamically using setComponent(Splat(uri))
  • Handle controller button input with ControllerListenerSystem
  • Build a Compose control panel using SpatialTheme and ComposeViewPanelRegistration

Requirements

  • Meta Quest device running Horizon OS
  • Android development environment with Spatial SDK
For detailed SDK version requirements, build prerequisites, and toolchain setup, see the sample’s README. For general platform setup, see Getting started with Spatial SDK.

Get started

Clone or download the Meta Spatial SDK Samples repository and open the SplatSample directory in Android Studio. Build the project and deploy to your Meta Quest device using the Run button or adb install. When launched, the sample loads a splat scene with a floating control panel where you can switch between two bundled Gaussian Splat scenes. For detailed build instructions, dependency versions, and troubleshooting, see the sample’s README.

Explore the sample

File / SceneWhat it demonstratesKey concepts
Main activity orchestrating splat rendering, feature registration, controller input, and panel interaction
SplatFeature registration, splat entity creation, controller button handling, panel positioning
Jetpack Compose control panel for selecting splat scenes
SpatialTheme, dark/light mode support, interactive UI with preview images
Build configuration for splat support
meta-spatial-sdk-splat dependency
Manifest requirements for experimental features
com.oculus.experimental.enabled feature declaration
app/scenes/Composition.glxf
GLXF scene composition for room environment
Environment model, floor, IBL lighting

Runtime behavior

When you run this sample, you see a photorealistic 3D Gaussian Splat scene (Menlo Park by default) rendered within a virtual room environment. A floating Compose panel displays preview thumbnails for two bundled scenes. Click a thumbnail to swap splats; the panel becomes non-interactive during loading and re-enables when the new scene completes. Use controller buttons to reposition the panel (A button: snap in front of view, B button: recenter entire scene). The splat entity supports locomotion, so you can walk through the scene using standard movement controls.

Key concepts

Registering the SplatFeature

The sample registers SplatFeature alongside other features in registerFeatures(). Notice how the activity opts into the experimental API with the @OptIn annotation at class level, and the feature requires both spatialContext and systemManager to initialize.
override fun registerFeatures(): List<SpatialFeature> {
    return listOf(VRFeature(this), SplatFeature(this.spatialContext, systemManager), ComposeFeature())
}
The class itself carries @OptIn(SpatialSDKExperimentalSplatAPI::class) to enable the experimental splat API. See SplatSampleActivity.kt for the complete feature registration.

Creating a splat entity

The sample creates a splat entity by attaching Splat, Transform, Scale, and SupportsLocomotion components. Notice the -90 degree rotation on the X axis to align the splat’s coordinate space with the room environment.
splatEntity = Entity.create(listOf(
    Splat(splatPath),
    Transform(Pose(Vector3(0f, 0f, 0f), Quaternion(-90f, 0f, 0f))),
    Scale(Vector3(1f)), SupportsLocomotion(),
))
See the initializeSplat() method in SplatSampleActivity.kt.

Swapping splats at runtime

The sample swaps between splats by calling setComponent() with a new Splat instance pointing to a different URI. The SDK automatically unloads the previous splat and loads the new one. Notice how the sample disables panel interaction during loading to prevent race conditions.
setPanelInteractive(false)
splatEntity.setComponent(Splat(newSplatPath.toUri()))
The panel re-enables when a SplatLoadEventArgs.EVENT_NAME event fires, signaling the splat has finished loading. See the loadSplat() method and event listener in SplatSampleActivity.kt.

Handling controller input

The sample uses ControllerListenerSystem to detect button presses on the right controller. Notice the bitwise operations to filter for ButtonA and ButtonB in the changedButtons and buttonState masks.
if ((controller.changedButtons and ButtonBits.ButtonA) != 0 &&
    (controller.buttonState and ButtonBits.ButtonA) != 0) {
    positionPanelInFrontOfUser(panelOffset)
}
See the ControllerListenerSystem inner class in SplatSampleActivity.kt for the complete button handling logic.

Building a Compose control panel

The sample creates a Compose panel entity using ComposeViewPanelRegistration and SpatialTheme from the UISet library. Notice how the panel supports dark and light modes via isSystemInDarkTheme() and includes Grabbable(type = GrabbableType.PIVOT_Y) to allow users to reposition it.
panelEntity = Entity.createPanelEntity(
    R.id.control_panel,
    Transform(Pose(Vector3(0f, panelHeight, 0f), Quaternion(0f, 180f, 0f))),
    Grabbable(type = GrabbableType.PIVOT_Y, minHeight = 0.75f, maxHeight = 2.5f),
)
See onSceneReady() in SplatSampleActivity.kt and the Compose UI in SplatControlPanel.kt.

Extend the sample

  • Load splats from other sources: The Splat component accepts a Uri, and NetworkedAssetLoader is initialized with OkHttp in this sample. Experiment with passing network or local file URIs to loadSplat() instead of bundled apk:// resources.
  • Add loading indicators: Enhance SplatControlPanel.kt to display a progress spinner while splats load, using the SplatLoadEventArgs timing.
  • Combine with spatial anchors: Use the MrukSample as a reference to position splat entities relative to real-world room geometry.