Create an Omniverse Spatial Application#
Note
Applies to: Spatial Extensions, Kit 109.0.3+, CloudXR 6
This setup reference covers three methods for adding XR support to an Omniverse Kit application. All methods use the Kit App Template as a starting point and result in a Kit application that can stream spatial content to XR clients over CloudXR.
Choose the method that best fits your project:
Method |
Flexibility |
Reusability |
Complexity |
Best For |
|---|---|---|---|---|
Direct .kit file |
Low |
Low |
Low |
Quick prototypes, getting started |
Custom extension |
High |
High |
Medium |
Organizations, reusable XR configs |
Application layer |
Medium |
Medium |
Medium |
Multiple deployment modes (desktop, XR, streaming) |
Common First Steps#
All three methods begin the same way.
Clone Kit App Template#
git clone --single-branch --branch 109.0.3 https://github.com/NVIDIA-Omniverse/kit-app-template.git
cd kit-app-template
Create a New Application#
./repo.sh template new
.\repo.bat template new
Follow the prompts:
Select what you want to create: Application
Select desired template:
USD Viewer – a streaming-ready viewer with built-in message handlers for loading assets and interacting with the stage. Recommended for most XR projects. See the Kit App Template Companion Guide for general template documentation.
Kit Base Editor – a smaller-footprint editor. Good if you want to build your own UI and interaction from scratch.
USD Composer – a full authoring suite. Best for content creation workflows where you also want XR preview.
Note
Only the USD Viewer template includes a built-in
setupextension that handles messages from XR clients over the Kit message bus (e.g.,openStageRequest,selectPrimsRequest). If you choose Kit Base Editor or USD Composer, you will need to write your own message handling extension to process data channel messages from clients. See Scene Integration: Writing Custom Message Handlers for an example.Enter a name for your application (e.g.,
my.company.xr.viewer)Accept defaults for any required extensions
When prompted about application layers, select No (you can add layers later with Method C)
This creates your application .kit file in source/apps/.
XR Extensions#
Regardless of which method you use, the following extensions must be present in your application’s dependency tree. These are the XR-specific extensions – do NOT add 2D streaming extensions like omni.kit.livestream.webrtc.
"omni.kit.xr.core" = {}
"omni.kit.xr.ui.window.profile" = {}
"omni.kit.xr.ui.window.viewport" = {}
"omni.kit.xr.ui.stage" = {}
"omni.kit.xr.cloudxr" = {}
Alternatively, you can use a bundle that pulls in all of the above plus preconfigured settings:
"omni.kit.xr.bundle.generic" = {}
Or a device-specific bundle:
"omni.kit.xr.bundle.apple_vision_pro" = {} # Optimized for Apple Vision Pro
"omni.kit.xr.bundle.ipad" = {} # Optimized for iPad / tablet
Method A: Direct .kit File#
Best for: Quick prototyping and getting started fast.
Open your generated .kit file (e.g., source/apps/my.company.xr.viewer.kit) and add the XR extensions to the [dependencies] section and the XR settings to the [settings] section alongside any existing values:
[package]
title = "My XR Viewer"
version = "1.0.0"
description = "XR-enabled USD viewer"
[dependencies]
# ... existing dependencies from the template ...
# Add XR support
"omni.kit.xr.core" = {}
"omni.kit.xr.ui.window.profile" = {}
"omni.kit.xr.ui.window.viewport" = {}
"omni.kit.xr.ui.stage" = {}
"omni.kit.xr.cloudxr" = {}
[settings]
# ... keep any existing settings from the template ...
# Start XR on launch with the AR profile enabled
xr.profile.ar.enabled = true
defaults.xr.activeProfile = "ar"
# Use the OpenXR display runtime
defaults.xr.system.display = "OpenXR"
# Enable the CloudXR OpenXR runtime for remote streaming
persistent.xr.system.openxr.runtime = "cloudxr"
# Use the CloudXR native data channel for bidirectional JSON messaging between client and server
xr.openxr.preferNVOpaqueDataChannel = true
Save the file and proceed to Build and Launch.
Tip
For a full list of available settings and what they control, see the XR Settings Reference.
Method B: Custom Extension#
Best for: Reusable XR configurations across multiple applications, or when you need programmatic control over XR startup.
Step 1: Create the Extension#
./repo.sh template new
.\repo.bat template new
Follow the prompts:
Select what you want to create: Extension
Select desired template: basic_python
Enter name:
my.company.xr.startup
Step 2: Configure extension.toml#
Edit source/extensions/my.company.xr.startup/config/extension.toml:
[package]
version = "1.0.0"
title = "My XR Startup"
description = "Auto-start XR on application launch"
category = "XR"
[dependencies]
"omni.kit.xr.core" = {}
"omni.kit.xr.system.openxr" = {}
"omni.kit.xr.system.simulatedxr" = {}
"omni.kit.xr.ui.stage" = {}
"omni.kit.xr.ui.window.profile" = {}
"omni.kit.xr.ui.window.viewport" = {}
"omni.kit.xr.cloudxr" = {}
[settings]
# Start XR on launch with the AR profile enabled
xr.profile.ar.enabled = true
defaults.xr.activeProfile = "ar"
# Use the OpenXR display runtime
defaults.xr.system.display = "OpenXR"
# Enable the CloudXR OpenXR runtime for remote streaming
persistent.xr.system.openxr.runtime = "cloudxr"
# Use the CloudXR native data channel for bidirectional JSON messaging between client and server
xr.openxr.preferNVOpaqueDataChannel = true
[[python.module]]
name = "my.company.xr.startup"
Step 3: Write the Startup Extension#
Edit source/extensions/my.company.xr.startup/my/company/xr/startup/extension.py:
import omni.ext
import omni.kit.xr.core
import omni.kit.app
class MyXRStartupExtension(omni.ext.IExt):
"""Auto-start XR when this extension loads."""
def on_startup(self, ext_id):
self.xr_core = omni.kit.xr.core.XRCore.get_singleton()
self._enable_xr_when_ready()
def _enable_xr_when_ready(self):
import asyncio
async def _start():
# Wait a frame for the XR system to initialize
await omni.kit.app.get_app().next_update_async()
self.xr_core.request_enable_profile("ar")
print("[XR Startup] AR profile enabled")
asyncio.ensure_future(_start())
def on_shutdown(self):
if self.xr_core and self.xr_core.is_xr_enabled():
self.xr_core.request_disable_profile()
Step 4: Add to Your Application .kit File#
[dependencies]
# ... existing dependencies ...
"my.company.xr.startup" = {}
[settings.app.exts]
folders.'++' = [
"${app}/../../exts"
]
For more detail on building reusable XR bundle extensions, see the Custom Bundles development guide.
Method C: Application Layer#
Best for: Teams that need multiple deployment modes – for example, a desktop mode (no XR), an XR mode (for headsets), and a streaming mode.
Step 1: Keep Your Base Application Clean#
Your base .kit file should contain only the core application dependencies – no XR extensions:
# source/apps/my.company.viewer.kit (base application)
[package]
title = "My Viewer"
version = "1.0.0"
description = "Base viewer application"
[dependencies]
# Core dependencies only -- no XR here
"omni.kit.uiapp" = {}
"omni.kit.viewport.window" = {}
# ... other application-specific extensions ...
Step 2: Create an XR Layer#
Create a new .kit file that layers XR on top of the base application:
# source/apps/my.company.viewer.xr.kit (XR layer)
[package]
title = "My Viewer -- XR Mode"
version = "1.0.0"
description = "XR configuration layer"
keywords = ["app", "layer", "xr"]
[dependencies]
# Reference the base application
"my.company.viewer" = {}
# Add XR support
"omni.kit.xr.core" = {}
"omni.kit.xr.ui.window.profile" = {}
"omni.kit.xr.ui.window.viewport" = {}
"omni.kit.xr.ui.stage" = {}
"omni.kit.xr.cloudxr" = {}
# Optional: add your startup extension from Method B
# "my.company.xr.startup" = {}
[settings]
app.name = "My Viewer (XR Mode)"
app.window.title = "My Viewer -- XR"
# Start XR on launch with the AR profile enabled
xr.profile.ar.enabled = true
defaults.xr.activeProfile = "ar"
# Use the OpenXR display runtime
defaults.xr.system.display = "OpenXR"
# Enable the CloudXR OpenXR runtime for remote streaming
persistent.xr.system.openxr.runtime = "cloudxr"
# Use the CloudXR native data channel for bidirectional JSON messaging between client and server
xr.openxr.preferNVOpaqueDataChannel = true
# Performance settings for XR
rtx.ecoMode.enabled = false
rtx.post.aa.op = 3
[settings.app.exts]
folders.'++' = [
"${app}/../../exts"
]
Step 3: Register the Layer in template.toml#
For the new XR layer to appear as a selectable option in Kit App Template, register it in your template.toml file:
[templates."xr_layer"]
class = "ApplicationLayerTemplate"
name = "XR Streaming"
url = "."
subpath = "apps/my.company.viewer.xr.kit"
type = "xr"
variables.version = "1.0.0"
This allows repo.sh template new (or repo.bat template new) to list your XR layer when creating new application configurations.
Step 4: Launch Different Configurations#
# Desktop mode (no XR)
./repo.sh launch # select my.company.viewer
# XR mode
./repo.sh launch # select my.company.viewer.xr
Example Directory Structure#
kit-app-template/
├── source/
│ ├── apps/
│ │ ├── my.company.viewer.kit # Base application
│ │ └── my.company.viewer.xr.kit # XR layer
│ └── extensions/
│ └── my.company.xr.startup/ # Optional startup extension
└── repo.toml
For more on Kit App Template integration, CI/CD, and multi-platform builds, see the Kit App Integration development guide.
Which Bundle to Use#
Bundle |
Includes |
Best For |
|---|---|---|
|
OpenXR + SimulatedXR + all UI |
Cross-platform, general use |
|
CloudXR + AR profile + alpha blending |
Apple Vision Pro |
|
CloudXR + AR profile + touch nav |
iPad / tablet |
You can use a bundle instead of listing individual extensions. Bundles provide preconfigured settings for their target platform.
Tip
If in doubt, use omni.kit.xr.bundle.generic. It includes all XR extensions and works with every supported client. Use a device-specific bundle only when you need its optimized defaults.
Note
If you list individual extensions (as shown in the examples above), you have more control over which components are included and what settings are applied.
Verifying XR Setup#
You can verify your XR setup programmatically:
import omni.kit.xr.core
xr_core = omni.kit.xr.core.XRCore.get_singleton()
if xr_core.is_xr_enabled():
print("XR is enabled")
profile = xr_core.get_current_profile_name()
print(f"Current profile: {profile}")
if xr_core.is_xr_display_enabled():
print("XR display is active")
systems = xr_core.get_system_names()
print(f"Available XR systems: {systems}")
else:
print("XR is not enabled")
For the full Python API reference, see the API Reference.
Next Steps#
Next step:
Build and Launch – Build your app, load a scene, and start CloudXR
See also:
XR Settings Reference – Configure quality, profiles, and rendering
System Architecture – Understand the full pipeline
Advanced Kit XR Development – Core concepts, API reference, custom tools, and more