Develop
Develop
Select your platform

Add Spatial SDK to an existing 2D app

Updated: Sep 17, 2025
By the end of this tutorial, you’ll have used Spatial SDK to add a 3D virtual environment to an existing 2D Android app, where the app will appear as an interactive panel. For your first time through this tutorial, you should use the template app provided below since its file structure matches the instructions exactly. However, you can also adapt these steps for your own Android app by downloading the template and copying key files.
If you’d rather make a Spatial SDK app from scratch, see Create a new Spatial SDK app. To learn about best practices that will further enhance your app’s user experience and broaden its audience, see the design guidelines.

Download and open the template Android app

Before you add a 3D environment, you’ll preview the app as a regular 2D phone app.
  1. Download or clone the MediaSpatialAppTemplate GitHub repository, which is a template Android phone app.
  2. If you downloaded the template app, unzip it. If you cloned the app, the folder is already unzipped.
  3. In Android Studio, select File > Open.
    The Open File or Project window appears.
  4. In the Open File or Project window, expand the unzipped folder and then double-click MediaSpatialAppTemplate-main.
  5. If Android Studio prompts you to upgrade and sync Gradle, upgrade it to the recommended version. This may take a while.
  6. In the top toolbar, in the device dropdown to the left of the green Run ‘app’ button, set the Android emulator to Medium Phone API 35 or any other phone emulator.
  7. Click the green play button (the Run ‘app’ button).
    Here’s what you should see in the Android Studio emulator.
    Screenshot of the emulator running the sample app
    “(c) copyright 2008, Blender Foundation, www.blender.org”
  8. In Android Studio, click the red Stop button to stop the app.

Add a Meta Quest build variant

To add Spatial SDK to the template app, you are going to create a new build variant. A build variant lets you have a mobile version of your app (for phones) and a Quest version (for Meta Quest devices) in the same codebase.
Note: The sample app doesn’t use Groovy. If you’re following this tutorial using your own app and it uses Groovy (build.gradle instead of build.gradle.kts), read the build variant page for Groovy syntax.
  1. On the left side of Android Studio, click the folder icon to open the project directory.
    The project directory panel appears.
  2. At the top of the panel, ensure the file view setting is set to Project Files and not Android. This ensures your project directory layout matches the screenshots in this tutorial.
  3. Open app/build.gradle.kts. This file is different from just build.gradle.kts, which isn’t in the app folder.
  4. Add this code just before the end of the android block. It specifies two build types: one for mobile and one for Quest.
     buildFeatures { buildConfig = true }
     flavorDimensions += "device"
     productFlavors {
       create("mobile") { dimension = "device" }
       create("quest") { dimension = "device" }
       }
    
  5. Save the file.
  6. In the project directory, copy app/src/main/AndroidManifest.xml to the src/mobile and src/quest folders.
    You’ll see this error: Unresolved class 'MainActivity', but that’s expected.
  7. Sync the project with Gradle by clicking the Sync Project with Gradle Files button (the elephant icon) in the toolbar.
    GIF of the Gradle sync icon being clicked
  8. In the top toolbar, select Build > Select Build Variant.
    The Build Variants panel appears on the left.
  9. In the Build Variants panel, click mobileDebug (default).
    A dropdown appears.
  10. From the dropdown, select questDebug as the active build variant.
    Here’s a video showing the change:
    Now your app will use src/quest/AndroidManifest.xml when building. If you switch the build variant back to one of the mobile flavors, then your app will use src/mobile/AndroidManifest.xml.

Add Spatial SDK Gradle plugin and dependencies

Spatial SDK is deployed to Maven Central so it can be added to your app. In this step, you will add it to your project by editing your project level build.gradle.kts file and app/build.gradle.kts file to include the necessary dependencies.
Note: If you’re using your own Android app for this tutorial instead of the sample app, your project structure is different. Follow Setup the Spatial SDK Gradle plugin instead of this section.
Note: The sample app doesn’t use Groovy. If you’re following this tutorial using your own app and it uses Groovy (build.gradle instead of build.gradle.kts), read the build dependencies page for Groovy syntax.
  1. In your project level build.gradle.kts file (not app/build.gradle.kts), replace the plugins block with this code.
    Note: If you choose to upgrade either the “org.jetbrains.kotlin.android” or “com.google.devtools.ksp” package versions at a later point, they must use the same version (for example, 2.0.20). Otherwise, you’ll get an error.
     plugins {
       id("com.android.application") version "8.1.0" apply false
       id("org.jetbrains.kotlin.android") version "2.0.20" apply false
       id("com.meta.spatial.plugin") version "0.9.2" apply true
       id("com.google.devtools.ksp") version "2.0.20-1.0.24" apply true
     }
    
  2. Save the file.
  3. In app/build.gradle.kts, replace the plugins block with this code.
     plugins {
         id("com.android.application")
         id("org.jetbrains.kotlin.android")
         id("com.google.devtools.ksp")
         id("com.meta.spatial.plugin")
     }
    
  4. On the line before the dependencies block, add this code to create a variable for the Spatial SDK version.
     val metaSpatialSdkVersion = "0.9.2"
    
  5. At the end of the dependencies block, add this code to import the relevant SDK packages.
     implementation("com.meta.spatial:meta-spatial-sdk:$metaSpatialSdkVersion")
     implementation("com.meta.spatial:meta-spatial-sdk-toolkit:$metaSpatialSdkVersion")
     implementation("com.meta.spatial:meta-spatial-sdk-vr:$metaSpatialSdkVersion")
     implementation("com.meta.spatial:meta-spatial-sdk-physics:$metaSpatialSdkVersion")
     ksp("com.meta.spatial.plugin:com.meta.spatial.plugin.gradle.plugin:0.9.2")
    
    You’ll get a reference warning about ksp, but that’s okay. You’ll fix it by syncing with Gradle later in this section.
    There are other packages available besides the three listed above. For more information on all of the available Spatial SDK packages, see Spatial SDK Packages.
    The variable declaration and dependencies block should now look like this.
     val metaSpatialSdkVersion = "0.9.2"
    
     dependencies {
     ...
    
     implementation("com.meta.spatial:meta-spatial-sdk:$metaSpatialSdkVersion")
     implementation("com.meta.spatial:meta-spatial-sdk-toolkit:$metaSpatialSdkVersion")
     implementation("com.meta.spatial:meta-spatial-sdk-vr:$metaSpatialSdkVersion")
     implementation("com.meta.spatial:meta-spatial-sdk-physics:$metaSpatialSdkVersion")
     ksp("com.meta.spatial.plugin:com.meta.spatial.plugin.gradle.plugin:0.9.2")
     }
    
  6. Below the dependencies block, add this code to tell your Spatial SDK project where the Spatial Editor files are stored and to enable custom components in Spatial Editor.
    You’ll get a reference warning about this code, but that’s okay. You’ll fix it by syncing with Gradle in the next step.
     val projectDir = layout.projectDirectory
     val sceneDirectory = projectDir.dir("spatial_editor/MediaApp")
     spatial {
       allowUsageDataCollection = true
       scenes {
         exportItems {
           item {
             projectPath.set(sceneDirectory.file("Main.metaspatial"))
             outputPath.set(projectDir.dir("src/quest/assets/scenes"))
           }
         }
       }
     }
    
  7. Sync the project with Gradle by clicking the Sync Project with Gradle Files button (the elephant icon).
    GIF of the Gradle sync icon being clicked

Create an immersive activity

To launch your app in VR, your app must include a new Android activity that subclasses the core Android activity AppSystemActivity. The sample app has already done this for you in the file ImmersiveActivity.kt.
  1. Open app/src/quest/java/com/meta/media/template/ImmersiveActivity.kt.
    The file’s contents are commented out by default.
  2. Select the contents of the entire file and uncomment them by clicking Code > Comment with Block Comment in the toolbar.
    The line baseTextureAndroidResourceId = R.drawable.skydome produces an error, but you’ll fix that in the next section.

Import environment assets

ImmersiveActivity.kt references the files environment.glb and R.drawable.skydome. Those files provide the default 3D skybox and landscape, so you’ll need to add them to your project.
  1. Download those files using this link.
    ZIP of environment.glb and skydome.jpg
  2. Unzip the downloaded folder.
  3. Follow the step for your operating system:
    • For Windows: In the extracted folder, open the Assets subfolder.
    • For Mac: In the extracted folder, open the __MACOSX/Assets subfolder.
  4. From the environment subfolder, drag environment.glb (for Windows) or ._environment.glb (for Mac) to the app/src/quest/assets directory in Android Studio.
    Note: Mesh assets should always be stored in the assets/ folder.
  5. Return to the extracted folder’s Assets subfolder.
  6. From the extracted folder, drag skydome.jpg to the app/src/quest/res/drawable directory in Android Studio.

Update manifests

  1. Replace the contents of quest/AndroidManifest.xml with this code, which points to your new ImmersiveActivity.kt and adds the necessary permissions/features for VR support.
     <?xml version="1.0" encoding="utf-8"?>
     <manifest
         xmlns:android="http://schemas.android.com/apk/res/android"
         xmlns:horizonos="http://schemas.horizonos/sdk">
    
         <horizonos:uses-horizonos-sdk
             horizonos:minSdkVersion="69"
             horizonos:targetSdkVersion="69"
             />
    
         <uses-feature
             android:name="android.hardware.vr.headtracking"
             android:required="true"
             />
    
         <uses-feature
             android:name="oculus.software.handtracking"
             android:required="false"
             />
         <uses-permission android:name="com.oculus.permission.HAND_TRACKING" />
    
         <uses-feature
             android:name="com.oculus.feature.PASSTHROUGH"
             android:required="false"
             />
    
         <uses-feature
             android:name="com.oculus.feature.VIRTUAL_KEYBOARD"
             android:required="false"
             />
         <uses-feature android:glEsVersion="0x00030001" />
    
         <uses-feature
             android:name="oculus.software.overlay_keyboard"
             android:required="false"
             />
    
         <uses-feature
             android:name="com.oculus.feature.RENDER_MODEL"
             android:required="false"
             />
         <uses-permission android:name="com.oculus.permission.RENDER_MODEL" />
    
         <uses-permission android:name="android.permission.INTERNET" />
    
         <application
             android:allowBackup="true"
             android:dataExtractionRules="@xml/data_extraction_rules"
             android:fullBackupContent="@xml/backup_rules"
             android:icon="@drawable/icon_media_app"
             android:label="@string/app_name"
             android:roundIcon="@drawable/icon_media_app"
             android:supportsRtl="true"
             android:theme="@style/Theme.AppCompat.NoActionBar">
    
             <meta-data
                 android:name="com.oculus.supportedDevices"
                 android:value="quest2|questpro|quest3"
                 />
             <meta-data
                 android:name="com.oculus.handtracking.version"
                 android:value="V2.0"
                 />
             <meta-data android:name="com.oculus.vr.focusaware" android:value="true" />
    
             <uses-native-library
                 android:name="libossdk.oculus.so"
                 android:required="true"
                 />
    
             <activity
                 android:name=".MainActivity"
                 android:exported="true"
                 android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"
                 android:allowEmbedded="true">
             </activity>
             <activity
                 android:name=".ImmersiveActivity"
                 android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen"
                 android:launchMode="singleTask"
                 android:excludeFromRecents="false"
                 android:screenOrientation="landscape"
                 android:configChanges="screenSize|screenLayout|orientation|keyboardHidden|keyboard|navigation|uiMode"
                 android:exported="true">
                 <intent-filter>
                     <action android:name="android.intent.action.MAIN" />
                     <category android:name="com.oculus.intent.category.VR" />
                     <category android:name="android.intent.category.LAUNCHER" />
                 </intent-filter>
             </activity>
         </application>
     </manifest>
    
  2. In main/AndroidManifest.xml, delete the <activity> block of code that’s inside the <application> block.
    You delete that code because when you use build variants, Android merges your main/AndroidManifest.xml and quest/AndroidManifest.xml together, which leads to conflicts between the LAUNCHER activities if MainActivity references aren’t removed.
     <activity
         android:name=".MainActivity"
         android:exported="true"
         android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"
         android:allowEmbedded="true">
         <intent-filter>
             <action android:name="android.intent.action.MAIN" />
             <category android:name="android.intent.category.LAUNCHER" />
         </intent-filter>
     </activity>
    
    Once you delete the <activity> section, the contents of main/AndroidManifest.xml should look like this.
     <?xml version="1.0" encoding="utf-8"?>
     <manifest xmlns:android="http://schemas.android.com/apk/res/android">
    
         <uses-permission android:name="android.permission.INTERNET" />
    
         <application
             android:allowBackup="true"
             android:dataExtractionRules="@xml/data_extraction_rules"
             android:fullBackupContent="@xml/backup_rules"
             android:icon="@drawable/icon_media_app"
             android:label="@string/app_name"
             android:roundIcon="@drawable/icon_media_app"
             android:supportsRtl="true"
             android:theme="@style/Theme.AppCompat.NoActionBar">
         </application>
     </manifest>
    
    Here’s what the new manifest does:
    • Adds the required features and permissions for VR support on the Quest.
    • Removes the <intent-filter> from the MainActivity so that it is no longer the LAUNCHER activity.
    • Adds the ImmersiveActivity and sets it as the LAUNCHER.

Deploy and run the app

By this point, you’ve updated your LAUNCHER activity to be the new ImmersiveActivity, so your app is ready for you to experience in VR.
  1. Plug your Meta Quest headset into your computer.
  2. In the top toolbar of Android Studio, in the device dropdown to the left of the green play button (the Run ‘app’ button), set the Android emulator to your headset. For example, Oculus Quest 3.
  3. Deploy your app to the headset by clicking the Run ‘app’ button in Android Studio.
    Run and deploy the app to your Quest
You should see the 2D app appear as a panel in a 3D environment, like this.

(Optional) Explore the Spatial SDK app directories

In Android Studio, familiarize yourself with the structure of the Spatial SDK app to effectively navigate and utilize its features.
  • App directory:
    • app/src/main/java: Contains source files for the Spatial SDK or immersive application.
    • app/scenes: Contains the Main.metaspatial file, 3D models, and textures.
    • app/scenes/Composition: Includes the Main.metaspatialcomp file, the main composition file.
  • Gradle directory: Configuration has been pre-set for you.
Note: If your project directory doesn’t match the file paths listed above, make sure the drop-down at the top of the directory is set to Project.

(Optional) Open the composition in Spatial Editor

Spatial SDK stores information about a scene’s 2D and 3D objects and their associated audio, materials, and components in a composition file. To edit a composition file, you open it in the Meta Spatial Editor.
  1. In the Android Studio file hierarchy, open the Main.metaspatial file.
  2. Right-click the file.
  3. Select either Open In > Finder (for Mac), or Open In > Explorer (for Windows).
  4. Double-click the file to open it in Spatial Editor.

Next steps

Design guidelines

Did you find this page helpful?
Thumbs up icon
Thumbs down icon