ViroARSceneNavigator

Entry point for AR applications with ViroReact.

ViroARSceneNavigator is the entry point for AR applications with Viro. For more information about developing in AR, see our Develop page for Augmented Reality (AR).

Props

initialScene (* required)

PropKeyPropType
{ scene: JSX.Element }The initial AR scene to display for your application on application start.

Note: ViroARSceneNavigator only accepts ViroARScene.

autofocus

TypeDescription
booleanWhether or not to enable autofocus for this AR session. Default value is true for iOS, false for Android. False on Android, since currently (as of ARCore 1.4.0), ARCore's tracking is optimized for auto-focus turned off.

bloomEnabled

TypeDescription
booleanEnable or disable bloom. Bloom adds a soft glow to bright areas in scene, simulating the way bright highlights appear to the human eye. To make an object bloom, this property must be enabled, and the object's threshold for bloom must be set via its material's bloomThreshold property.

hdrEnabled

TypeDescription
booleanWhen HDR rendering is enabled, Viro uses a deeper color space and renders to a floating point texture, then applies a tone-mapping algorithm to preserve fine details in both bright and dark regions of the scene. If HDR is disabled, then features like Bloom and PBR will not work, and tone-mapping will be disabled. HDR is not supported on all devices.

numberOfTrackedImages

TypeDescription
numberiOS 12+/ARKit 2.0+ only. Tracked images are images that track continuously. This property represents the max amount of images that should be tracked concurrently. For example, if this number is set to 3, then the first 3 images visible in a scene will be tracked. Even if there are 5 total ViroARImageMarkers only the first 3 will be tracked; if one marker leaves the view, then an untracked marker will then be tracked. Keep in mind that this number should be kept low as the higher the number, the worse the performance.

pbrEnabled

TypeDescription
booleanEnable or disable physically-based rendering. Physically based rendering, or PBR, produces more realistic lighting results for your scenes, and provides a more intuitive workflow for artists. To use PBR, this property must be enabled, and materials must use the physicallyBased lighting model. PBR is controlled by a variety of properties, see PBR guide for details.

shadowsEnabled

TypeDescription
booleanEnable or disable rendering dynamic shadows. If shadows are disabled here, shadow casting lights will simply not cast a shadow.

videoQuality

TypeDescription
"High" | "Low"iOS 11.3+/ARKit 1.5+ only. Sets the video quality either to the highest or lowest possible if the device supports more than 1 applicable video quality. Default value is "High".

viroAppProps

TypeDescription
objectA javascript object containing properties for this viro app. One use would be to pass in properties from native if you're using a hybrid application. Access these props from within ViroARScene objects through this.props.sceneNavigator.viroAppProps.

worldAlignment

TypeDescription
"Gravity" | "GravityAndHeading" | "Camera"iOS only — has no effect on Android/ARCore. This property determines the initial orientation/alignment of the world's coordinate system. Gravity — origin is the device's starting location and the X-Z plane is perpendicular to gravity. GravityAndHeading — origin is the device's starting location and the X and Z axes are latitudinally and longitudinally aligned. Camera — coordinate system is locked to the camera's current orientation.

occlusionMode

TypeDescription
peopleOnly | depthBasedControls how real-world objects obscure virtual 3D content to maintain accurate depth perception in the scene. peopleOnly: Virtual objects are only hidden when a person passes in front of them. depthBased: Virtual objects are hidden by any physical object (walls, furniture, etc.) detected in the depth map. Note: Ensure you have downloaded the CoreML model, as referenced in the installation guide, in order to enable this on non-LiDAR iOS devices.

provider

TypeDescription
"none" | "arcore" | "reactvision"New in v2.53.0. Replaces the separate cloudAnchorProvider and geospatialAnchorProvider props (now deprecated). Controls both cloud anchor and geospatial anchor backends simultaneously. Defaults to "reactvision" — routes through the ReactVision platform. Requires an API key and project ID configured via your Expo plugin config or native Info.plist / AndroidManifest.xml (see ReactVision Studio Setup). Set to "arcore" to use Google Cloud Anchors and ARCore Geospatial (requires a googleCloudApiKey). Set to "none" to disable both. The existing hostCloudAnchor, resolveCloudAnchor, and onCloudAnchorStateChange API is unchanged regardless of provider.

📘

Deprecated props

cloudAnchorProvider and geospatialAnchorProvider still compile with a deprecation warning but will be removed in a future release. Use provider instead.

depthEnabled

TypeDescription
booleanActivates the depth sensor without enabling occlusion rendering. On iOS, uses LiDAR on supported devices with a monocular depth estimator as fallback (all devices). On Android, uses ARCore Depth API (requires ARCore 1.18+). Virtual objects are not occluded, but depth data becomes available for performARHitTest (returns DepthPoint results) and distance measurement use-cases. When occlusionMode="depthBased" is set at the same time, occlusionMode takes precedence and full depth-based occlusion is used instead. Default: false.

depthDebugEnabled

TypeDescription
booleanDebug visualisation of the depth texture over the camera feed. When sampling LiDAR/ARCore depth, colours represent depth values: magenta = no data, blue = near, red = far. When monocular depth is active (see preferMonocularDepth), the overlay uses the Turbo colormap across a 0–10 m range. Useful for verifying depth coverage before relying on hit-test results. Requires depthEnabled={true}. Both iOS and Android. Default: false.

preferMonocularDepth (iOS only)

TypeDescription
booleaniOS only. When true, forces iOS to use the monocular (neural) depth estimator even on LiDAR-equipped devices. Monocular depth is automatically used on non-LiDAR devices when depth-based occlusion is enabled; this prop allows forcing it on LiDAR devices for consistency across device types, for testing/comparison, or to extend range beyond LiDAR's ~5 m limit. Default: false (LiDAR used when available). See Monocular Depth below for model selection and requirements.

worldMeshEnabled

TypeDescription
booleanEnable real-world mesh generation, surfacing the scanned environment as a subscribable, physics-ready mesh. When enabled, virtual physics objects collide with detected real-world geometry (floors, walls, tables, etc.). Requires depth-sensing capability — iOS: LiDAR scanner (iPhone 12 Pro+, iPad Pro 2020+); Android: ToF sensor or ARCore Depth API support. Default: false. See World Mesh below.

worldMeshConfig

TypeDescription
ViroWorldMeshConfigFine-grained control over physics mesh simplification and debug wireframe draw. Only used when worldMeshEnabled is true. See the type definition under World Mesh below.

monocularDepthScale (iOS only)

TypeDescription
numberiOS only. Multiplies neural depth values by this factor. Use < 1.0 if the model overestimates distances (virtual objects appear visible through real surfaces). Typical range: 0.7–1.3. Default: 1.0.

monocularDepthTargetFPS (iOS only)

TypeDescription
numberiOS only. Maximum neural-depth inference rate. Automatically throttled further by device thermal state (Fair → 3 fps, Serious → 2 fps, Critical → stopped). The depth texture from the last frame continues to be used for occlusion while throttled. Default: 5.

frontCameraEnabled

TypeDescription
booleanUse the front (selfie) camera as the AR session background. iOS: uses ARFaceTrackingConfiguration (TrueDepth camera); gravity-aligned world coordinate system is preserved so 3D content at fixed world positions is world-locked. Requires iPhone X or newer. Plane detection and LiDAR are unavailable. Android: uses ARCore Augmented Faces (AR_AUGMENTED_FACE_MODE_MESH3D) with the front camera, falling back to front-camera-only mode if Augmented Faces is unsupported. Default: false. See Front Camera (Selfie) below for the full list of what works and what does not.

Callbacks

onCloudAnchorStateChange

TypeDescription
(event: ViroCloudAnchorStateChangeEvent) => voidFired when a cloud anchor state changes, including progress updates during hosting/resolving operations.

onWorldMeshUpdated (new in v2.56.0)

TypeDescription
(stats: ViroWorldMeshStats) => voidFired when the world mesh is updated. Provides statistics about the current mesh state.
type ViroWorldMeshStats = {
  vertexCount: number;
  triangleCount: number;
  source: "lidar" | "monocular" | "plane";
};

World Mesh (new in v2.56.0)

Set worldMeshEnabled={true} to surface the real-world geometry as a subscribable, physics-ready mesh. Tune it with worldMeshConfig:

type ViroWorldMeshConfig = {
  // Physics mesh simplification
  physicsCellSize?: number;       // Vertex clustering cell (default: 0.10 m). 0 = disabled.
  physicsMaxTriangles?: number;   // Stride decimation limit. 0 = no limit.

  // Debug wireframe
  debugDrawEnabled?: boolean;       // default: true
  debugDrawDepthTest?: boolean;     // occluded by real surfaces (default: true)
  debugDrawMaxTriangles?: number;   // default: 1000
  debugDrawLineThickness?: number;  // default: 0.001 m
};

Source priority (automatic, persistent):

  1. ARMeshAnchor — iOS 13.4+ LiDAR, native accumulation across frames
  2. Depth image — LiDAR or monocular depth, current frame only
  3. ARPlaneAnchor — all platforms, session-persistent polygon fallback

Performance guidance:

  • physicsCellSize=0.10 clusters vertices within 10 cm → gap-free simplified mesh.
  • physicsMaxTriangles=200 limits per-update cost when LiDAR produces dense meshes.
  • Async BVH construction — the physics build runs on a background thread; the render thread is never blocked.

Physics usage:

For JS physicsBody objects to respond to the world mesh, add physicsWorld to your scene:

<ViroARScene physicsWorld={{ gravity: [0, -9.81, 0] }}>
  <ViroSphere
    physicsBody={{
      type: "Dynamic",
      mass: 1,
      shape: { type: "Sphere", params: [0.05] },
    }}
    position={[0, 1, -1]}
  />
</ViroARScene>

The VROARWorldMesh physics body operates independently from JS physicsBody props — it uses Bullet directly without requiring physicsWorld on the scene. However, JS physics objects do require physicsWorld={{ gravity: [0,-9.81,0] }} on ViroARScene.

Monocular Depth

Use the device's neural depth estimator (DAv2 or DepthPro) instead of LiDAR for hit tests, occlusion, and world mesh on non-LiDAR devices — or to force monocular even when LiDAR is present (preferMonocularDepth={true}). Tune output with monocularDepthScale and inference rate with monocularDepthTargetFPS.

Model selection (automatic priority):

  1. DepthAnythingV2_metric_indoor.mlmodelc — DAv2 Hypersim, best indoor accuracy
  2. DepthAnythingV2_metric_outdoor.mlmodelc — DAv2 KITTI
  3. DepthPro.mlmodelc — Apple DepthPro
  4. DepthAnythingV2.mlmodelc — relative depth (requires calibration)

Bundle the model file in your app's iOS target → Copy Bundle Resources. See the Install Depth Model guide.

Requirements: iOS 14.0+, A12 Bionic or newer.

Performance: at the default 5 fps inference rate, the A18 Pro uses < 50% ANE capacity. The thermal throttle is automatic — you do not need to manage it. If the scene becomes hot, the inference rate reduces automatically and the depth texture from the last frame continues to be used for occlusion.

Front Camera (Selfie) (new in v2.56.0)

Set frontCameraEnabled={true} to switch the AR session to the front-facing camera.

<ViroARSceneNavigator
  frontCameraEnabled={true}
  initialScene={{ scene: SelfieCameraScene }}
  style={StyleSheet.absoluteFill}
/>

What still works with frontCameraEnabled={true}:

  • All 3D nodes, models, particles, text
  • Lighting (ViroAmbientLight, ViroSpotLight, etc.)
  • Physics (physicsBody, physicsWorld)
  • Game loop (ViroGameLoop)
  • PCM streaming audio (StreamingAudioManager)

What does NOT work:

  • Plane detection
  • World mesh / LiDAR
  • ViroARPlane, ViroARPlaneSelector
  • Image / object tracking

📘

World-tracking limitation

With frontCameraEnabled={true}, ViroNodes placed at fixed world positions appear world-locked on iOS (gravity alignment) but may appear screen-fixed on Android devices that fall back from Augmented Faces to front-camera-only mode. Do not use ViroARPlane or plane-detection callbacks — they will not fire.

For a camera feed bound to a material texture (mirror surfaces, picture-in-picture) rather than the full-screen AR background, see ViroCameraTexture.

Methods

The following functions are available on the ViroARSceneNavigator component. You can get the handle to the ViroARSceneNavigator from your ViroScene. Each ViroScene added to a ViroARSceneNavigator provides its parent navigator in the variable: this.props.sceneNavigator.

push({ scene: ViroARScene, passProps: Object })

Push the given scene onto the scene stack, displaying the scene to the user.

Parameters

  • scene — Scene that will be pushed onto the stack and displayed to the user
  • passProps — Props to pass to the rendered scene

pop()

Pop the top-most scene of the stack, effectively going back to the previous scene.

pop(n: number)

Go back n scenes at once. If n is equal to 1 this is equivalent to calling pop().

jump({ scene: ViroARScene, passProps: Object })

Move to the given scene in the stack, removing it from its current position and placing it on top, thereby displaying it to the user. If the scene is not already on the stack, this method pushes the scene to the top of the stack. Best used in applications where the user jumps between a set of scenes frequently.

Parameters

  • scene — Scene that will be moved or pushed to the top of the stack and displayed to the user
  • passProps — Props to pass to the rendered scene

replace({ scene: ViroARScene, passProps: Object })

Replace the currently displayed scene (the scene at the top of the stack) with the given scene. This leaves the remainder of the stack unchanged.

Parameters

  • scene — Scene that will replace the scene at the top of the stack and be displayed to the user
  • passProps — Props to pass to the rendered scene

resetARSession(resetTracking: boolean, removeAnchors: boolean)

iOS Only. Resets the ARSession with a combination of the two flags: resetTracking — if true, the scene will realign with the camera's position and orientation. removeAnchors — if true, the anchors will all be removed (onAnchorRemoved will be called on the ViroARScene). It's recommended to also set removeAnchors to true if resetTracking is also set to true.

setWorldOrigin(offset: Object)

iOS 11.3+/ARKit 1.5+ Only! Transforms the world origin by the given offset. Multiple calls are additive, translating the worldOrigin by the given position/rotation. The offset Object can contain a position and/or a rotation key:

this.arSceneNavigator.setWorldOrigin({ position: [x, y, z], rotation: [x, y, z] });

startVideoRecording(fileName: string, saveToCameraRoll: bool, onError: func)

Starts a recording of the ViroARSceneNavigator capturing only Viro-rendered components and external audio (microphone and camera permissions required).

Parameters

  • fileName — name of the file saved to the app's temporary files. If the name matches an existing file, it will be overridden. A file extension is added automatically.
  • saveToCameraRoll — whether the file should also be saved to the user's Photos at the end of recording (requires photo/video saving permissions).
  • onError — a function invoked with an errorCode if recording encounters an issue.

async stopVideoRecording()

Async function called to stop video recording.

Returns an object with keys: success (true/false if stopping was successful), url (path to the file in the application's temporary files), errorCode (a number representing an error while stopping recording).

async takeScreenshot(fileName: string, saveToCameraRoll: bool)

Async function called to take a screenshot of the ViroARSceneNavigator capturing only Viro-rendered components.

Parameters

  • fileName — name of the file saved to the app's temporary files. A file extension is added automatically.
  • saveToCameraRoll — whether the file should also be saved to the user's Photos (requires photo/video saving permissions).

Returns an object with keys: success, url, errorCode.

async project(point: array)

Async function that takes a 3D vector representing a world position and returns a vector representing an x,y screen position.

Parameters

  • point — a 3D vector representing a 3D world position. For example, [-1, 4, -5] represents the point (x:-1, y:4, z:-5) in world space.

Returns an object with the key screenPosition — a 2D vector representing the projected screen position. screenPosition[0] is the x screen position; screenPosition[1] is the y screen position.

this.props.arSceneNavigator.project(projectPoint).then((returnPos) => {
  this.setState({
    screenX: returnPos["screenPosition"][0],
    screenY: returnPos["screenPosition"][1],
  });
});

async unproject(point: array)

Async function that takes a 3D vector representing a 2D screen position plus a value from 0 to 1 representing an interpolation from the near to far clipping plane, and returns a vector representing the x,y,z world-space position of the given screen position.

Parameters

  • point — a 3D vector. point[0] is the screen x position, point[1] is the screen y position, point[2] is a value from 0 to 1 (0 = near clipping plane, 1 = far clipping plane, interpolated in between).

Returns an object with the key position — a 3D vector representing the unprojected world position.

this.props.arSceneNavigator.unproject(unprojectPoint).then((returnDict) => {
  this.setState({ unprojectedPoint: returnDict["position"] });
});