This sample demonstrates how to build an application that seamlessly switches between a traditional 2D Android panel interface and an immersive VR environment while sharing the same Compose UI code.
Structure an app with separate 2D panel and immersive VR activities
Share a single Compose UI between 2D and immersive modes using composePanel
Switch from 2D to immersive mode using standard Android intents
Return from immersive to 2D mode using the extra_launch_in_home_pending_intent pattern
Register panels in immersive mode and position them using a GLXF scene
Requirements
Meta Quest device running Horizon OS
Android Studio with Spatial SDK configured
For detailed SDK versions, toolchain requirements, and setup steps, see the sample README.
Get started
Clone the Meta Spatial SDK Samples repository and open the HybridSample project in Android Studio. Build and deploy the app to your Quest device using the standard Android deployment workflow. The sample launches in 2D panel mode by default, where you can tap the button to switch to immersive VR mode.
For complete build instructions and troubleshooting, refer to the sample’s README.
Explore the sample
The sample consists of two activities that share a common UI component:
When you launch the sample, you see a 2D Android panel displaying a themed interface with a Switch to immersive view button. Clicking the button transitions you into an immersive VR environment where you see the same UI rendered as a floating panel in 3D space, surrounded by a procedural skybox and environment. The panel now shows a Switch to 2D view button. Clicking it returns you to the traditional 2D panel interface.
Key concepts
Shared Compose UI across modes
The sample defines a single HybridPanel composable in PancakeActivity.kt that accepts buttonText and onButtonClick parameters. In 2D mode, the activity calls setContent { HybridPanel(...) } directly. In immersive mode, HybridSampleActivity.kt uses the same composable inside composePanel:
This pattern eliminates UI duplication and ensures visual consistency. For more on panel registration, see 2D panel registration.
Switching from 2D to immersive
The sample switches from 2D to immersive mode using a standard Android intent. PancakeActivity creates an intent targeting HybridSampleActivity with ACTION_MAIN and FLAG_ACTIVITY_NEW_TASK, then calls startActivity(). See the onCreate() method in PancakeActivity.kt for the complete implementation.
Returning from immersive to 2D
Returning to 2D mode requires a more involved pattern. The launchPanelModeInHome() method in HybridSampleActivity.kt works in three steps:
Creates an Intent targeting PancakeActivity with ACTION_MAIN and FLAG_ACTIVITY_NEW_TASK
Wraps it in a PendingIntent with FLAG_UPDATE_CURRENT and FLAG_IMMUTABLE
Sends a HOME intent (ACTION_MAIN + CATEGORY_HOME) with the PendingIntent attached as the extra_launch_in_home_pending_intent extra
This pattern ensures the 2D activity launches within the Horizon OS home environment rather than inside the immersive session.
Panel placement using GLXF scenes
The sample positions the immersive panel using a GLXF scene authored in Meta Spatial Editor. The scene contains a Panel node with a resource ID that matches the PanelRegistration ID. The registration uses R.id.hybrid_panel, which corresponds to the panel node in the scene file, and the Spatial SDK places the panel at the authored position and orientation.
Modify the shared HybridPanel composable to add interactive elements like sliders or toggles, and observe how changes appear identically in both 2D and immersive modes.
Add a second panel in immersive mode to display additional UI elements positioned at a different location in the scene.
Replace the procedural skybox with a custom environment using the techniques shown in the EnvironmentsSample.