About quest-spatial
A guidance skill for Android teams building immersive Quest apps with Meta Spatial SDK. It covers toolchain checks, ECS and panel design, runtime budgets, hybrid apps, and common implementation pitfalls.
Key features
- 01Android and Kotlin Spatial SDK guidance
- 02Toolchain and dependency checks
- 03ECS and panel architecture
- 04Runtime budget and performance notes
- 05Hybrid activity interaction patterns
- 06Troubleshooting known SDK pitfalls
Use cases
- 01Choosing between Quest development paths
- 02Planning panel and scene workloads
- 03Building a hybrid 2D and immersive app
- 04Auditing a Spatial SDK project
Install
Copy the command and run it in a terminal.
npx skills add https:/ / github.com/ ssheleg/ xr-dev --skill quest-spatialAfter installing, type/quest-spatialin Claude Code to confirm it loaded.
Bundled files
References and scripts installed along with SKILL.md.
references/budgets-and-traps.mdbuild-and-audit.mddocs-map.mdhybrid-activities.mdsamples-map.mdDetails
From the project READMEMeta Spatial SDK: the Android lane onto Horizon OS
Spatial SDK lets an Android team ship an immersive app in Kotlin: Android Studio, Gradle, Jetpack Compose, the libraries they already use — with OpenXR underneath and an ECS data model on top. A panel in the scene is a real Android view, not a texture someone drew to look like one.
For a whole-product roadmap or stage audit, use quest-lifecycle; a single technical task stays with this owner. If absent, identify the current stage, its evidence and the next prerequisite inline.
Read references/hybrid-activities.md when mixing panel and immersive activities: exclusive/cooperative modes, state ownership and transition tests.
Step 0 — is this the right lane?
| The team and the app | Lane |
|---|---|
| Android developers; panels, 2D UI, media, an app that is also a 2D app | Spatial SDK — this skill |
| Maximum control of the renderer, custom Vulkan, a game engine of your own | quest-native (C/C++ OpenXR) |
| Existing Unity content, a Unity team, asset-store pipeline | Meta's hz-unity-* skills |
| Ships on the web, or must run outside a headset too | quest-webxr |
| An existing 2D Android app that only needs to run on Quest | Meta's hz-android-2d-porting — porting, not rebuilding |
Spatial SDK is built around panels and an ECS scene. Use the runtime estimates
below to choose a representative workload early, not as hard API ceilings.
Read references/build-and-audit.md when implementing or auditing a project;
it provides the inline procedure when Meta's companion is absent.
Step 1 — inspect the actual toolchain
Sample snapshot read on 2026-09-20 (meta-quest/Meta-Spatial-SDK-Samples, MIT).
Read the consumer wrapper/catalog first; do not upgrade an existing project
to these values without checking its SDK and build compatibility:
| Piece | Version |
|---|---|
| Headset | Quest 2 / 3 / 3S / Pro on Horizon OS v69 or newer |
| Android Studio | Narwhal (2025.1.1) or newer — required by AGP 8.11 |
| Gradle | 9.4.1 (wrapper) |
| Android Gradle Plugin | 8.11.1 |
| Kotlin | 2.1.0 |
| JDK | 17 (bundled with Android Studio; separate only for command-line builds) |
| Meta Spatial Editor | required by every sample except MrukSample and PremiumMediaSample |
| NDK | only for custom shaders (MediaPlayerSample, PremiumMediaSample) |
The upgrade trap, stated by Meta: AGP 8.5 or earlier running the Gradle 9.x wrapper fails during native/CMake model sync, because that AGP calls a Gradle API removed in Gradle 9. Moving AGP to 8.11.1 resolves it — the error message does not point there.
Dependencies are modular; take what the app uses:
com.meta.spatial:meta-spatial-sdk (core, always), -vr (immersive
functionality, most apps), -toolkit (the common components and systems),
-physics, -ovrmetrics, and the rest listed in
documentation/spatial-sdk/spatial-sdk-packages.
Step 2 — the budgets, before the architecture
Meta publishes workload estimates and recommendations
(spatial-sdk-runtime-guidelines, rechecked 2026-09-21). Use them in the first
sketch, retaining their device/content/refresh assumptions:
| Measured/recommended item | Snapshot estimate |
|---|---|
| Entity operations per tick (a read or write in a system) | 2,000 |
| Physics objects | 500 |
| 3D GLB objects in view | 100 when they cover >50% of the viewport; more only if they cover less |
| Scene-graph entities | ~1,000 |
| Panel resolution cost | each extra 480,000 pixels ≈ +1% GPU |
The panel tables describe isolated test workloads and differ by kind:
| Panel type | FPS dips below 90 | FPS stays below 90 |
|---|---|---|
| Empty view | 20 | 40 |
| UI-only view | 15 | 40 |
| Image view | 15 | 40 |
| Web view | 5 | 30 |
| Video view | 3 | 5 |
| Activity-based | 2 | 2 |
| Panel with layers | 5 | 15 |
These measurements do not establish a hard three-video/two-activity limit or guarantee that mixed workloads fit. Test the real panel/media/object mix in a release build; consider reuse/virtualization instead of scaling panels with items.
Step 3 — the traps that cost a day each
Full text and workarounds: references/budgets-and-traps.md.
- Debug builds are much slower than release — Meta lists it as a known issue, not as a surprise. Judge performance on the release variant, or the numbers mean nothing.
- Entering immersive can kill other apps' audio. Request
AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCKunless exclusive audio is genuinely required, and handleOnAudioFocusChangeListener. finish()on an immersive activity may not bring the 2D panel back, and with panels onenableLayer = trueit can crash insidelibMetaSpatialSDK.so.enableLayeris deprecated — uselayerConfig.PanelRenderMode.Mesh()still creates a compositor layer; the workaround ispanelConfig.layerConfig = nullafter converting the settings.- Layers sharing one
SceneSwapchainignore per-layersetClip()— all show the last clip set.
Step 4 — where to read
The index is documentation/spatial-sdk/llms.txt/, and every page has a
Markdown twin at documentation/spatial-sdk/<slug>.md. The prefix says which
question a page answers. Counts below are the dated source snapshot, not a live inventory:
| Prefix | Count | Holds |
|---|---|---|
spatial-sdk-* | 97 | the SDK itself: -ecs, -component, -systems, -queries, -2dpanel-* (8), -isdk-* (5, interaction), -ui-* (10, the Horizon OS UI Set), -mruk, -physics, -animations, -glxf, -gltfs, -custom-shaders, -hot-reload, -known-issues, -runtime-guidelines |
spatial-editor-* | 20 | the visual editor, its CLI, asset library, compositions |
ps-* | 43 | platform services: entitlement, IAP, leaderboards, achievements, cloud backup, attestation |
ts-* | 24 | troubleshooting: adb, logcat, profilers, device setup |
os-*, po-*, platform-* | 5 | compositor, CPU/GPU levels, memory, audio |
hybrid-*, horizon-billing-*, add-spatial-sdk-to-app | 5 | hybrid apps and Google-Play-billing compatibility |
Slug tables worth keeping open: references/docs-map.md.
Step 5 — the samples answer faster than the prose
github.com/meta-quest/Meta-Spatial-SDK-Samples (MIT). Enumerate its current
sample directories rather than relying on a prose count. Open the one
that already does what is being built — references/samples-map.md maps each
sample to the question it answers, and notes that the repository ships its own
AGENTS.md, .mcp.json and per-agent configuration, so an agent can be
pointed straight at a clone.
Hybrid apps, in one paragraph
A hybrid app moves between 2D panel activities and immersive activities in one
package. Panel activities run in three contexts — Home, Overlay (the
Quest-button menu) and Embedded inside an immersive activity. Two
interaction models: exclusive (one at a time; terminate the old activity
after starting the new one) and cooperative (several concurrent).
HybridSample is the reference implementation.
What this skill hands off
| Next question | Where |
|---|---|
| Write the ECS code, panels, gradle wiring | Meta's hz-spatial-sdk when present; otherwise this skill's build/audit procedure |
| It runs but drops frames | quest-perf |
| Get it onto a headset, read logs, capture | quest-tooling |
| Ship it | quest-store |
Versions, budgets and known issues above were read from the samples repository
and developers.meta.com on 2026-09-20; both move — re-check with
metavr docs search "spatial sdk runtime guidelines" before quoting a number.
Triggers
More phrases that route here: "spatial panels" / "панели в VR", "Meta Spatial SDK", "Spatial Editor", "hybrid app" / "гибридное приложение", "glXF", "com.meta.spatial", "how many panels can I open" / "сколько панелей выдержит", "Spatial SDK samples" / "примеры Spatial SDK".
Moved here from the description in 0.4.4: "Kotlin app for Quest", "приложение на Kotlin для Quest".