### Install ViroReact with npm
Source: https://github.com/reactvision/viro/blob/main/README.md
Use this command to install the ViroReact library in your project. Ensure you have Node.js and npm installed.
```bash
npm install @reactvision/react-viro
```
--------------------------------
### Minimal React Viro Setup (No ARCore)
Source: https://github.com/reactvision/viro/blob/main/readmes/IOS_CONFIGURATION.md
Use this configuration for apps that only require basic AR features without ARCore integration.
```json
{
"expo": {
"name": "MyARApp",
"plugins": [
["@reactvision/react-viro", {
"iosLinkage": "static"
}]
]
}
}
```
--------------------------------
### Install React Viro using NPM
Source: https://github.com/reactvision/viro/blob/main/readmes/INSTALL.md
Use this command to add the React Viro library to your project dependencies via NPM.
```bash
npm install --save @reactvision/react-viro
```
--------------------------------
### Install React Viro using Yarn
Source: https://github.com/reactvision/viro/blob/main/readmes/INSTALL.md
Use this command to add the React Viro library to your project dependencies via Yarn.
```bash
yarn add @reactvision/react-viro
```
--------------------------------
### Example of Weak Framework Flags in xcconfig
Source: https://github.com/reactvision/viro/blob/main/readmes/IOS_CONFIGURATION.md
This is an example of the expected output when verifying weak linking flags for ARCore frameworks in your xcconfig file.
```text
OTHER_LDFLAGS = ... -weak_framework "ARCoreBase" -weak_framework "ARCoreCloudAnchors" ...
```
--------------------------------
### React Viro Setup for Full ARCore Features
Source: https://github.com/reactvision/viro/blob/main/readmes/IOS_CONFIGURATION.md
This setup enables all ARCore capabilities, including geospatial anchoring and camera/location permissions.
```json
{
"expo": {
"name": "MyARApp",
"plugins": [
["@reactvision/react-viro", {
"ios": {
"includeARCore": true,
"cameraUsagePermission": "This app uses the camera for AR experiences",
"locationUsagePermission": "This app uses your location for AR experiences"
},
"cloudAnchorProvider": "arcore",
"geospatialAnchorProvider": "arcore",
"googleCloudApiKey": "AIza..."
}]
]
}
}
```
--------------------------------
### Fullscreen Camera Setup with ViroCameraTexture
Source: https://github.com/reactvision/viro/blob/main/docs/ViroCameraTexture.md
This snippet demonstrates how to set up a fullscreen camera view using ViroScene and ViroCameraTexture. It configures a quad to fill the screen and displays the front camera feed. The `onCameraReady` callback is used to ensure the camera is ready before rendering the quad.
```tsx
import React, { useState } from 'react';
import { Dimensions } from 'react-native';
import {
ViroScene,
ViroSceneNavigator,
ViroQuad,
ViroCameraTexture,
ViroMaterials,
} from '@reactvision/react-viro';
ViroMaterials.createMaterials({
mirror: { lightingModel: 'Constant' },
});
const DIST = 1;
const WIDTH = 2 * DIST; // fills 90° horizontal FOV
const { width: sw, height: sh } = Dimensions.get('window');
const HEIGHT = WIDTH * (sh / sw); // match screen aspect ratio
function FullscreenSelfieScene() {
const [ready, setReady] = useState(false);
return (
setReady(true)}
onError={(e) => console.error(e.nativeEvent.error)}
/>
);
}
export default function App() {
return (
);
}
```
--------------------------------
### ViroGameLoop Example
Source: https://github.com/reactvision/viro/blob/main/docs/PLATFORM_EXTENSIONS.md
Example of using ViroGameLoop within a ViroARScene to manage fixed and regular updates for a ViroBox.
```tsx
import { ViroARScene, ViroBox, ViroGameLoop } from "@reactvision/react-viro";
import { useRef } from "react";
const boxRef = useRef(null);
{
// deterministic physics step
}}
onUpdate={({ nativeEvent: { dt } }) => {
// interpolate / render
}}
/>
;
```
--------------------------------
### Basic ViroVirtualJoystick Usage
Source: https://github.com/reactvision/viro/blob/main/docs/PLATFORM_EXTENSIONS.md
Example of how to use ViroVirtualJoystick. Ensure explicit width and height are set in the style prop. The onStickChange callback updates module-level state.
```tsx
import { ViroVirtualJoystick } from "@reactvision/react-viro";
const ctrl = { x: 0, y: 0 }; // module-level, shared with game loop
{
ctrl.x = e.nativeEvent.x;
ctrl.y = e.nativeEvent.y;
}}
style={{ width: 120, height: 120 }}
/>;
```
--------------------------------
### React Viro Setup for Cloud Anchors Only
Source: https://github.com/reactvision/viro/blob/main/readmes/IOS_CONFIGURATION.md
Configure for shared AR experiences using cloud anchors, without enabling geospatial features.
```json
{
"expo": {
"name": "MyARApp",
"plugins": [
["@reactvision/react-viro", {
"cloudAnchorProvider": "arcore",
"googleCloudApiKey": "AIza..."
}]
]
}
}
```
--------------------------------
### Defining a VR Scene with ViroScene
Source: https://github.com/reactvision/viro/blob/main/docs/QUEST_SETUP.md
This example shows how to define a VR scene using ViroScene as the root component. It includes essential elements like lighting and a 360 image, suitable for VR environments on Quest.
```tsx
import {
ViroScene,
ViroAmbientLight,
ViroController,
Viro360Image,
} from "@reactvision/react-viro";
export function MyVRScene() {
return (
{/* …your content… */}
);
}
```
--------------------------------
### Run Expo Prebuild
Source: https://github.com/reactvision/viro/blob/main/docs/QUEST_SETUP.md
Execute 'npx expo prebuild --clean' to generate necessary Android files and manifest entries for VR support. This command is crucial after initial setup or upgrades.
```bash
npx expo prebuild --clean
```
--------------------------------
### ViroVirtualButton Component Usage
Source: https://github.com/reactvision/viro/blob/main/docs/PLATFORM_EXTENSIONS.md
Example of using the ViroVirtualButton component with custom properties like controllerId, button, size, tintColor, and event handlers.
```tsx
console.log("A pressed")}
onPressOut={() => console.log("A released")}
style={{ width: 48, height: 48 }}
/>
```
--------------------------------
### Correct Call Order for StreamingAudioManager
Source: https://github.com/reactvision/viro/blob/main/docs/PLATFORM_EXTENSIONS.md
Demonstrates the essential sequence of operations for using StreamingAudioManager, emphasizing the critical need to push samples before starting playback to prevent underruns.
```typescript
// 1. Create
StreamingAudioManager.create("voice");
// 2. Configure stream format
StreamingAudioManager.beginStreaming("voice", 22050, 1);
// 3. Pre-fill buffer BEFORE play
StreamingAudioManager.pushSamples("voice", firstChunkBase64);
// 4. Start playback
StreamingAudioManager.play("voice");
// 5. Continue pushing on each chunk
StreamingAudioManager.pushSamples("voice", nextChunkBase64);
// 6. Clean up
StreamingAudioManager.destroy("voice");
```
--------------------------------
### Convert Binary Plists to XML for Pod Install UTF-8 Error
Source: https://github.com/reactvision/viro/blob/main/readmes/IOS_CONFIGURATION.md
If you encounter 'invalid byte sequence in UTF-8' errors during `pod install`, convert binary Info.plist files to XML format within the ios/Pods directory.
```bash
cd ios/Pods
find . -name "Info.plist" -exec plutil -convert xml1 {} \;
```
--------------------------------
### Implement Custom VR Root with Navigator Bridge
Source: https://github.com/reactvision/viro/blob/main/docs/QUEST_SETUP.md
For custom roots bypassing `ViroQuestEntryPoint`, directly use `ViroVRSceneNavigator` and subscribe to `VRQuestNavigatorBridge` to handle navigation operations like push and pop. This setup is necessary if you need to manage intents and operations manually.
```tsx
import {
ViroVRSceneNavigator,
VRQuestNavigatorBridge,
} from "@reactvision/react-viro";
export default function VRQuestRoot() {
// ViroQuestEntryPoint does all of this automatically — only needed for
// custom roots that bypass it.
const [intent, setIntent] = useState(() => VRQuestNavigatorBridge.getIntent());
const navRef = useRef(null);
useEffect(() => VRQuestNavigatorBridge.onIntent(setIntent), []);
useEffect(() => {
if (!intent) return;
return VRQuestNavigatorBridge.subscribeOps((op) => {
if (op.type === "push") navRef.current?.push(op.scene);
else if (op.type === "pop") navRef.current?.pop();
// …etc
});
}, [intent?.intentKey]);
if (!intent) return null;
return (
);
}
```
--------------------------------
### Record Video with ViroCameraTexture
Source: https://github.com/reactvision/viro/blob/main/docs/ViroCameraTexture.md
Starts and stops video recording of the camera feed to an MP4 file. Specify an outputPath for custom file locations. Ensure microphone permissions are granted for audio recording.
```tsx
import React, { useRef, useState } from 'react';
import { Button } from 'react-native';
import { ViroARScene, ViroQuad, ViroCameraTexture, ViroMaterials } from '@reactvision/react-viro';
ViroMaterials.createMaterials({ cam: { lightingModel: 'Constant' } });
function Scene() {
const cameraRef = useRef(null);
const [recording, setRecording] = useState(false);
const toggleRecording = async () => {
if (!recording) {
const result = await cameraRef.current?.startRecording();
if (result?.success) {
setRecording(true);
console.log('Recording started, will write to', result.url);
}
} else {
const result = await cameraRef.current?.stopRecording();
setRecording(false);
if (result?.success) {
console.log('Video saved to', result.url);
}
}
};
return (
);
}
```
--------------------------------
### ViroGameLoop Component
Source: https://github.com/reactvision/viro/blob/main/docs/PLATFORM_EXTENSIONS.md
The ViroGameLoop component provides a headless node that fires per-frame JavaScript callbacks from the native render loop. It can be mounted within a ViroARScene or ViroScene to start the loop and unmounted to stop it. It supports `onUpdate`, `onLateUpdate`, and `onFixedUpdate` callbacks, with an option to enable a fixed-step accumulator via `fixedHz`.
```APIDOC
## ViroGameLoop — F5
A headless (zero-size) node that fires per-frame JS callbacks from the native render loop. Mount it anywhere inside a `ViroARScene` or `ViroScene` to start the loop. Unmount it to stop.
### Import
```tsx
import { ViroGameLoop } from "@reactvision/react-viro";
```
### Props
| Prop | Type | Default | Description |
| --------------- | -------------------------------------- | ------- | ------------------------------------------------------------------------------------------------ |
| `onUpdate` | `(e: ViroGameLoopUpdateEvent) => void` | — | Fires every rendered frame. |
| `onLateUpdate` | `(e: ViroGameLoopUpdateEvent) => void` | — | Fires after physics simulation. |
| `fixedHz` | `number` | — | When set, enables the fixed-step accumulator. |
| `onFixedUpdate` | `(e: ViroGameLoopFixedEvent) => void` | — | Fires at `fixedHz` regardless of render rate. Spiral-of-death protection: max 4 ticks per frame. |
```typescript
type ViroGameLoopUpdateEvent = {
nativeEvent: { dt: number; elapsed: number };
};
type ViroGameLoopFixedEvent = {
nativeEvent: { dt: number };
};
```
> **Note:** `dt` and `elapsed` arrive as strings on Android due to New Architecture Fabric constraints and are parsed to `number` by the component wrapper automatically.
### Hooks
```typescript
import { useGameLoop, useLateUpdate, useFixedUpdate } from '@reactvision/react-viro';
useGameLoop((dt: number, elapsed: number) => void): void;
useLateUpdate((dt: number, elapsed: number) => void): void;
useFixedUpdate((dt: number) => void, fixedHz?: number): void;
```
### Utility: bypass reconciler
For high-frequency position/rotation updates, use these helpers to bypass the React reconciler:
```typescript
import { ViroGameLoopUtils } from "@reactvision/react-viro";
ViroGameLoopUtils.setPosition(nodeRef, x, y, z);
ViroGameLoopUtils.setRotation(nodeRef, x, y, z);
ViroGameLoopUtils.setScale(nodeRef, x, y, z);
```
### Example
```tsx
import { ViroARScene, ViroBox, ViroGameLoop } from "@reactvision/react-viro";
import { useRef } from "react";
const boxRef = useRef(null);
{
// deterministic physics step
}}
onUpdate={({ nativeEvent: { dt } }) => {
// interpolate / render
}}
/>
;
```
```
--------------------------------
### startRecording / stopRecording
Source: https://github.com/reactvision/viro/blob/main/docs/ViroCameraTexture.md
Records the camera feed to an MP4 file. `startRecording` initiates the session, and `stopRecording` finalizes the file and returns its path.
```APIDOC
## startRecording(options?) / stopRecording()
### Description
Record the camera feed to an MP4. `startRecording` resolves once the recording session is open; `stopRecording` finalises the file and resolves with its path.
### Method Signatures
```typescript
startRecording(options?: { outputPath?: string }): Promise
stopRecording(): Promise
```
### Parameters
#### `startRecording` Options
- `outputPath` (string) - Optional - Absolute path for the output file (e.g. from `react-native-fs`). Omit to use a default cache-directory path chosen by the native layer.
### Returns
- `Promise` - A promise that resolves with a `ViroCaptureResult` object.
```ts
type ViroCaptureResult =
| { success: true; url: string } // absolute path of the written file
| { success: false; error: string } // human-readable error
```
### Example
```tsx
import React, { useRef, useState } from 'react';
import { Button } from 'react-native';
import { ViroARScene, ViroQuad, ViroCameraTexture, ViroMaterials } from '@reactvision/react-viro';
ViroMaterials.createMaterials({ cam: { lightingModel: 'Constant' } });
function Scene() {
const cameraRef = useRef(null);
const [recording, setRecording] = useState(false);
const toggleRecording = async () => {
if (!recording) {
const result = await cameraRef.current?.startRecording();
if (result?.success) {
setRecording(true);
console.log('Recording started, will write to', result.url);
}
} else {
const result = await cameraRef.current?.stopRecording();
setRecording(false);
if (result?.success) {
console.log('Video saved to', result.url);
}
}
};
return (
);
}
```
```
--------------------------------
### Using ViroXRSceneNavigator for Cross-Platform Entry
Source: https://github.com/reactvision/viro/blob/main/docs/QUEST_SETUP.md
This snippet demonstrates the basic usage of ViroXRSceneNavigator, the primary entry point for cross-platform VR/AR experiences. It configures initial scenes for both AR and VR modes.
```tsx
import { ViroXRSceneNavigator } from "@reactvision/react-viro";
export default function MyScreen() {
return (
);
}
```
--------------------------------
### Basic ViroCameraTexture Usage
Source: https://github.com/reactvision/viro/blob/main/docs/ViroCameraTexture.md
Register a material and attach the ViroCameraTexture to a ViroQuad. Ensure camera permissions are handled at runtime.
```tsx
import { ViroMaterials, ViroQuad, ViroCameraTexture } from '@reactvision/react-viro';
// 1. Register the material — only lightingModel is required.
// ViroCameraTexture sets the diffuse texture on it automatically.
ViroMaterials.createMaterials({
selfieMat: { lightingModel: 'Constant' },
});
// 2. Place a surface and attach the camera texture to its material.
function SelfieScene() {
return (
console.log('Camera ready')}
onError={(e) => console.error(e.nativeEvent.error)}
/>
);
}
```
--------------------------------
### Import ViroVirtualButton
Source: https://github.com/reactvision/viro/blob/main/docs/PLATFORM_EXTENSIONS.md
Import the ViroVirtualButton component from the @reactvision/react-viro package.
```tsx
import { ViroVirtualButton } from "@reactvision/react-viro";
```
--------------------------------
### Handle VR Exit with onExitViro Callback
Source: https://github.com/reactvision/viro/blob/main/docs/QUEST_SETUP.md
Use the `onExitViro` prop on `ViroXRSceneNavigator` or `StudioSceneNavigator` to define behavior after the VR session ends, such as navigating back in the panel.
```tsx
// ViroXRSceneNavigator
navigation.goBack()}
/>
// StudioSceneNavigator
navigation.goBack()}
/>
```
--------------------------------
### Camera Position Considerations with AR Scenes
Source: https://github.com/reactvision/viro/blob/main/docs/ViroCameraTexture.md
Explains the interaction between ViroCameraTexture's `cameraPosition` prop and ViroARScene, highlighting potential conflicts and recommended usage.
```APIDOC
## Camera position and AR scenes
`ViroARScene` already uses the **back camera** via ARKit / ARCore for tracking and background rendering. `ViroCameraTexture` is independent of that feed — it opens its own camera session.
| Scene type | `cameraPosition` | Result |
|---|---|---|
| `ViroARScene` | `"front"` | ✅ Two different physical cameras run concurrently — AR tracking on the back, selfie on the front |
| `ViroARScene` | `"back"` | ❌ The OS will deny a second exclusive session on the same sensor already held by ARCore/ARKit |
| `ViroScene` (non-AR) | `"front"` or `"back"` | ✅ No AR framework running, either camera works |
The intended use-case inside an AR scene is always the **front camera** for a selfie overlay while the back camera drives AR tracking.
```
--------------------------------
### Configure Static Framework Linkage for iOS
Source: https://github.com/reactvision/viro/blob/main/readmes/IOS_CONFIGURATION.md
Use this configuration to set static framework linkage for iOS. This option provides a faster launch time and a single binary.
```json
{
"expo": {
"plugins": [
["@reactvision/react-viro", {
"iosLinkage": "static"
}]
]
}
}
```
--------------------------------
### Import StreamingAudioManager
Source: https://github.com/reactvision/viro/blob/main/docs/PLATFORM_EXTENSIONS.md
Import the StreamingAudioManager class from the React-Viro library.
```typescript
import { StreamingAudioManager } from "@reactvision/react-viro";
```
--------------------------------
### Switch Cameras at Runtime
Source: https://github.com/reactvision/viro/blob/main/docs/ViroCameraTexture.md
Update the `cameraPosition` prop to switch between 'front' and 'back' cameras. A brief black frame may appear during the transition.
```tsx
const [position, setPosition] = React.useState<'front' | 'back'>('front');
setPosition(p => p === 'front' ? 'back' : 'front')}
/>
```
--------------------------------
### StreamingAudioManager API Overview
Source: https://github.com/reactvision/viro/blob/main/docs/PLATFORM_EXTENSIONS.md
Reference for the core methods of StreamingAudioManager, including creating players, managing streams, and controlling playback. Ensure correct call order for optimal performance.
```typescript
// Create a named player
StreamingAudioManager.create(playerId: string): void;
// Begin streaming — must be called before pushSamples
StreamingAudioManager.beginStreaming(
playerId: string,
sampleRate: number, // e.g. 22050, 44100, 48000
channels: number // 1 = mono, 2 = stereo
): void;
// Playback control
StreamingAudioManager.play(playerId: string): void;
StreamingAudioManager.pause(playerId: string): void;
StreamingAudioManager.setVolume(playerId: string, volume: number): void; // 0.0–1.0
StreamingAudioManager.setMuted(playerId: string, muted: boolean): void;
// Push audio data — base64-encoded float32 PCM, interleaved, little-endian
StreamingAudioManager.pushSamples(playerId: string, base64Samples: string): void;
// Release resources
StreamingAudioManager.destroy(playerId: string): void;
```
--------------------------------
### Import ViroVirtualJoystick
Source: https://github.com/reactvision/viro/blob/main/docs/PLATFORM_EXTENSIONS.md
Import the ViroVirtualJoystick component from the @reactvision/react-viro package.
```tsx
import { ViroVirtualJoystick } from "@reactvision/react-viro";
```
--------------------------------
### Capture Photo with ViroCameraTexture
Source: https://github.com/reactvision/viro/blob/main/docs/ViroCameraTexture.md
Saves the current camera frame as a JPEG. Specify an outputPath for custom file locations, or omit to use a default cache directory. The promise resolves with the file path upon successful save.
```tsx
import React, { useRef } from 'react';
import { Button } from 'react-native';
import { ViroARScene, ViroQuad, ViroCameraTexture, ViroMaterials } from '@reactvision/react-viro';
ViroMaterials.createMaterials({ cam: { lightingModel: 'Constant' } });
function Scene() {
const cameraRef = useRef(null);
const takePhoto = async () => {
const result = await cameraRef.current?.capturePhoto();
if (result?.success) {
console.log('Photo saved to', result.url);
} else {
console.error('Capture failed:', result?.error);
}
};
return (
{/* Trigger takePhoto from your UI */}
);
}
```
--------------------------------
### World Mesh Configuration
Source: https://github.com/reactvision/viro/blob/main/docs/PLATFORM_EXTENSIONS.md
Props for enabling and configuring the real-world geometry mesh, including physics and debug drawing.
```APIDOC
## ViroARSceneNavigator - World Mesh
### Description
Surfaces the real-world geometry as a subscribable, physics-ready mesh.
### Props
- `worldMeshEnabled` (boolean) - Default: `false` - Enable real-world mesh generation.
- `worldMeshConfig` (ViroWorldMeshConfig) - Fine-grained control over physics and debug draw.
- `onWorldMeshUpdated` ((stats: ViroWorldMeshStats) => void) - Fired when the mesh is updated.
### ViroWorldMeshConfig Type
```typescript
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
};
```
### ViroWorldMeshStats Type
```typescript
type ViroWorldMeshStats = {
vertexCount: number;
triangleCount: number;
source: "lidar" | "monocular" | "plane";
};
```
### Source Priority
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 — physics build runs on a background thread; render thread is never blocked
### Physics Usage
For physics bodies to respond to the world mesh, add `physicsWorld` to your scene:
```tsx
```
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 it.
```
--------------------------------
### Navigate VR Scenes via Ref
Source: https://github.com/reactvision/viro/blob/main/docs/QUEST_SETUP.md
Access the `arSceneNavigator` ref from `ViroXRSceneNavigator` to control scene navigation programmatically. This works for both AR and VR on Quest.
```tsx
const navRef = useRef(null);
// Push a new scene
navRef.current?.arSceneNavigator?.push({ scene: DetailScene });
// Pop back
navRef.current?.arSceneNavigator?.pop();
```
--------------------------------
### Recenter Tracking and Toggle Passthrough
Source: https://github.com/reactvision/viro/blob/main/docs/QUEST_SETUP.md
Utilize `VRModuleOpenXR.recenterTracking` and `setPassthroughEnabled` by obtaining the native view tag using the `useVRViewTag` hook. Ensure the view tag is available before calling these native functions.
```tsx
import { VRModuleOpenXR, useVRViewTag } from "@reactvision/react-viro";
function MyVRScene() {
const viewTag = useVRViewTag();
const recenter = () => {
if (viewTag != null) VRModuleOpenXR?.recenterTracking?.(viewTag);
};
return (
...
);
}
```
--------------------------------
### ViroCameraTexture Props
Source: https://github.com/reactvision/viro/blob/main/docs/ViroCameraTexture.md
This section details the available props for the ViroCameraTexture component, outlining their types, default values, and descriptions.
```APIDOC
## ViroCameraTexture Props
| Prop | Type | Default | Description |
|---|---|---|---|
| `material` | `string` | **required** | Name of a material registered via `ViroMaterials.createMaterials`. The component sets this material's diffuse texture to the live camera feed. |
| `cameraPosition` | `"front" | "back"` | `"front"` | Which physical camera to open. The front camera feed is automatically mirrored by the OS for a natural selfie effect. |
| `paused` | `boolean` | `false` | When `true`, frame updates stop and the last captured frame is held. Drops CPU and battery usage to near zero. |
| `onCameraReady` | `() => void` | — | Fires once when the first real camera frame is available and the texture is ready. Called from the native Camera2 / AVFoundation callback, not synchronously on mount. |
| `onError` | `(event) => void` | — | Fires when the camera fails to initialise — permission denied, hardware error, or invalid material name. |
```
--------------------------------
### Bind Live Camera Feed to Material
Source: https://github.com/reactvision/viro/blob/main/docs/PLATFORM_EXTENSIONS.md
Use ViroCameraTexture to display a live device camera feed on any geometry's material. This is suitable for mirror effects or picture-in-picture.
```tsx
import {
ViroMaterials,
ViroQuad,
ViroCameraTexture,
} from "@reactvision/react-viro";
ViroMaterials.createMaterials({
mirrorMat: { lightingModel: "Constant" },
});
function MirrorScene() {
const cameraRef = useRef(null);
return (
console.log("Camera ready")}
/>
);
}
```
--------------------------------
### Android Camera Permission
Source: https://github.com/reactvision/viro/blob/main/docs/ViroCameraTexture.md
Declare the camera permission in your AndroidManifest.xml file. This is required for ViroCameraTexture to function.
```xml
```
--------------------------------
### Android Permissions for Audio Recording
Source: https://github.com/reactvision/viro/blob/main/docs/ViroCameraTexture.md
Declare the RECORD_AUDIO permission in AndroidManifest.xml to enable audio recording alongside video.
```xml
```
--------------------------------
### Reading Controller State from C++
Source: https://github.com/reactvision/viro/blob/main/docs/PLATFORM_EXTENSIONS.md
Accessing the state of a virtual controller, including joystick positions and button states, from C++ using VROVirtualControllerRegistry.
```cpp
// virocore — safe from any thread
auto state = VROVirtualControllerRegistry::instance().peek("p1");
if (state) {
auto snap = state->snapshot();
float leftX = snap.stickLX;
float leftY = snap.stickLY;
bool aPressed = (snap.buttonBits >> VROButtonIndex_A) & 1;
}
```
--------------------------------
### React Viro Custom Permissions Configuration
Source: https://github.com/reactvision/viro/blob/main/readmes/IOS_CONFIGURATION.md
Customize the permission messages for camera, microphone, photos, and location access within your AR application.
```json
{
"expo": {
"plugins": [
["@reactvision/react-viro", {
"ios": {
"cameraUsagePermission": "We need camera access to show AR content",
"microphoneUsagePermission": "We need microphone access for voice commands",
"photosPermission": "We need photo access to save AR screenshots",
"savePhotosPermission": "We need permission to save AR screenshots",
"locationUsagePermission": "We need location for geospatial AR features"
}
}]
]
}
}
```
--------------------------------
### iOS Permissions for Audio Recording
Source: https://github.com/reactvision/viro/blob/main/docs/ViroCameraTexture.md
Add the NSMicrophoneUsageDescription key to Info.plist for iOS to request microphone access for audio recording.
```xml
NSMicrophoneUsageDescription
Used to record audio with video captures
```
--------------------------------
### ViroVirtualButton
Source: https://github.com/reactvision/viro/blob/main/docs/PLATFORM_EXTENSIONS.md
A native-rendered circular button that pairs with ViroVirtualJoystick via a shared controllerId. It allows for custom button actions and styling.
```APIDOC
## ViroVirtualButton
### Description
A native-rendered circular button. Pairs with `ViroVirtualJoystick` via a shared `controllerId`.
### Import
```tsx
import { ViroVirtualButton } from "@reactvision/react-viro";
```
### Props
#### Required Props
- **controllerId** (string) - Required - Shared controller ID.
- **button** (ViroButtonName) - Required - Which button bit to set.
#### Optional Props
- **size** (number) - Optional - Circle diameter in dp/points. Default: `44`.
- **tintColor** (string | number) - Optional - Button fill color. Default: `rgba(255,255,255,0.6)`.
- **onPressIn** ((e) => void) - Optional - Fires on touch-down.
- **onPressOut** ((e) => void) - Optional - Fires on touch-up or cancel.
- **style** (StyleProp) - Optional - Must include explicit `width` and `height`.
### Type Definitions
```typescript
type ViroButtonName =
| "A"
| "B"
| "X"
| "Y"
| "Z"
| "L1"
| "R1"
| "L2"
| "R2"
| "Start"
| "Select";
```
### Example
```tsx
console.log("A pressed")}
onPressOut={() => console.log("A released")}
style={{ width: 48, height: 48 }}
/>
```
```
--------------------------------
### ViroVirtualJoystick Component
Source: https://github.com/reactvision/viro/blob/main/docs/PLATFORM_EXTENSIONS.md
The ViroVirtualJoystick component is a native-rendered on-screen joystick that writes stick state to a VROInputState registry entry with single-digit ms latency, avoiding JS bridge round-trips for core functionality. It requires explicit width and height in its style prop.
```APIDOC
## ViroVirtualJoystick
### Description
A native-rendered on-screen joystick that writes stick state to a `VROInputState` registry entry with single-digit ms latency (no JS bridge round-trip).
### Import
```tsx
import { ViroVirtualJoystick } from "@reactvision/react-viro";
```
### Props
| Prop | Type | Default | Description |
| --------------- | ---------------------- | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
| `controllerId` | `string` | **required** | Identifies the shared controller state. Must match the `controllerId` of any `ViroVirtualButton` you want to share state with. |
| `stickSide` | `"left" | "right"` | `"left"` | Which analog stick this joystick drives. |
| `radius` | `number` | `60` | Outer ring radius in dp/points. |
| `tintColor` | `string | number` | `rgba(255,255,255,0.6)` | Color of the ring and knob. |
| `onStickChange` | `(e) => void` | — | Fires when stick value changes. `e.nativeEvent.x` and `.y` are `number` in `[-1, 1]`. |
| `style` | `StyleProp` | — | Must include explicit `width` and `height` — native views have no intrinsic size. |
> **Important:** Always set `style={{ width: N, height: N }}`. Without explicit size the view collapses.
### Example
```tsx
import { ViroVirtualJoystick } from "@reactvision/react-viro";
const ctrl = { x: 0, y: 0 }; // module-level, shared with game loop
{
ctrl.x = e.nativeEvent.x;
ctrl.y = e.nativeEvent.y;
}}
style={{ width: 120, height: 120 }}
/>;
```
### Architecture note
Touch events never cross the JS bridge. The native view (iOS `UIView`, Android `View`) computes normalized stick deflection and writes directly to a `VROInputState` via `VROVirtualControllerRegistry`. The `onStickChange` callback is a secondary, optional JS notification fired in the same tick.
```
--------------------------------
### Push Scene from Inside VR Scene
Source: https://github.com/reactvision/viro/blob/main/docs/QUEST_SETUP.md
Use the `sceneNavigator` prop passed to a VR scene to push new scenes. This method avoids the need for bridges or refs.
```tsx
export function MyVRScene({ sceneNavigator }: any) {
return (
sceneNavigator.push({ scene: DetailScene })}>
{/* … */}
);
}
```
--------------------------------
### StreamingAudioManager API Reference
Source: https://github.com/reactvision/viro/blob/main/docs/PLATFORM_EXTENSIONS.md
This section details the methods available for managing real-time PCM audio streaming using the StreamingAudioManager.
```APIDOC
## StreamingAudioManager
Runtime PCM audio streaming. Feed raw float32 PCM chunks from any source (TTS, synthesizer, game engine audio) and play them in real time.
### Import
```typescript
import { StreamingAudioManager } from "@reactvision/react-viro";
```
### API
#### `create(playerId: string): void`
**Description:** Creates a named audio player.
**Method:** `create`
**Parameters:**
* `playerId` (string) - Required - A unique identifier for the audio player.
#### `beginStreaming(playerId: string, sampleRate: number, channels: number): void`
**Description:** Begins the audio streaming configuration for a player. Must be called before `pushSamples`.
**Method:** `beginStreaming`
**Parameters:**
* `playerId` (string) - Required - The identifier of the audio player.
* `sampleRate` (number) - Required - The sample rate of the audio stream (e.g., 22050, 44100, 48000).
* `channels` (number) - Required - The number of audio channels (1 for mono, 2 for stereo).
#### `play(playerId: string): void`
**Description:** Starts playback for the specified audio player.
**Method:** `play`
**Parameters:**
* `playerId` (string) - Required - The identifier of the audio player.
#### `pause(playerId: string): void`
**Description:** Pauses playback for the specified audio player.
**Method:** `pause`
**Parameters:**
* `playerId` (string) - Required - The identifier of the audio player.
#### `setVolume(playerId: string, volume: number): void`
**Description:** Sets the playback volume for the specified audio player.
**Method:** `setVolume`
**Parameters:**
* `playerId` (string) - Required - The identifier of the audio player.
* `volume` (number) - Required - The volume level, ranging from 0.0 (silent) to 1.0 (maximum).
#### `setMuted(playerId: string, muted: boolean): void`
**Description:** Mutes or unmutes the specified audio player.
**Method:** `setMuted`
**Parameters:**
* `playerId` (string) - Required - The identifier of the audio player.
* `muted` (boolean) - Required - `true` to mute, `false` to unmute.
#### `pushSamples(playerId: string, base64Samples: string): void`
**Description:** Pushes audio data to the player's buffer. Samples should be base64-encoded float32 PCM, interleaved, and little-endian.
**Method:** `pushSamples`
**Parameters:**
* `playerId` (string) - Required - The identifier of the audio player.
* `base64Samples` (string) - Required - The base64-encoded audio sample data.
#### `destroy(playerId: string): void`
**Description:** Releases all resources associated with the specified audio player.
**Method:** `destroy`
**Parameters:**
* `playerId` (string) - Required - The identifier of the audio player.
### Correct Call Order Example
```typescript
// 1. Create
StreamingAudioManager.create("voice");
// 2. Configure stream format
StreamingAudioManager.beginStreaming("voice", 22050, 1);
// 3. Pre-fill buffer BEFORE play
StreamingAudioManager.pushSamples("voice", firstChunkBase64);
// 4. Start playback
StreamingAudioManager.play("voice");
// 5. Continue pushing on each chunk
StreamingAudioManager.pushSamples("voice", nextChunkBase64);
// 6. Clean up
StreamingAudioManager.destroy("voice");
```
> **Critical:** Call `pushSamples` before `play`. If the ring buffer is empty when playback starts, the audio thread will underrun.
### Encoding Samples Example
```typescript
function float32ToBase64(samples: Float32Array): string {
const bytes = new Uint8Array(samples.buffer);
let binary = "";
bytes.forEach((b) => (binary += String.fromCharCode(b)));
return btoa(binary);
}
```
```
--------------------------------
### Enable VR Renderer Flags
Source: https://github.com/reactvision/viro/blob/main/docs/QUEST_SETUP.md
Use `passthroughEnabled`, `handTrackingEnabled`, `hdrEnabled`, and `bloomEnabled` props on `ViroXRSceneNavigator` to control VR rendering features. These props are automatically passed to the native `ViroVRSceneNavigator`.
```tsx
```
--------------------------------
### Verify ARCore Weak Linking Flags
Source: https://github.com/reactvision/viro/blob/main/readmes/IOS_CONFIGURATION.md
Check your app target's xcconfig file for weak framework flags related to ARCore. This confirms that ARCore frameworks are set to be weakly linked.
```bash
# Check your app target's xcconfig for weak framework flags
cat ios/Pods/Target\ Support\ Files/Pods-YourApp/Pods-YourApp.debug.xcconfig | grep ARCore
```
--------------------------------
### iOS Camera Permission
Source: https://github.com/reactvision/viro/blob/main/docs/ViroCameraTexture.md
Add the camera usage description to your iOS Info.plist file. This informs the user why the camera permission is needed.
```xml
NSCameraUsageDescription
Used to display live camera feed in AR scenes
```
--------------------------------
### Typical AR Selfie Pattern
Source: https://github.com/reactvision/viro/blob/main/docs/ViroCameraTexture.md
A common pattern for creating an AR selfie experience. It uses `ViroCameraTexture` to capture the front camera feed and displays it on a `ViroQuad` positioned in front of the user. The `onCameraReady` callback is used to control the visibility of the quad.
```tsx
import React, { useState } from 'react';
import {
ViroARScene,
ViroARSceneNavigator,
ViroQuad,
ViroCameraTexture,
ViroMaterials,
} from '@reactvision/react-viro';
ViroMaterials.createMaterials({
selfie: { lightingModel: 'Constant' },
});
function SelfieARScene() {
const [ready, setReady] = useState(false);
return (
{/* A portrait-ratio quad floating in front of the user */}
setReady(true)}
onError={(e) => console.error('Camera error:', e.nativeEvent.error)}
/>
);
}
export default function App() {
return (
);
}
```
--------------------------------
### ViroWorldMeshConfig and ViroWorldMeshStats Types
Source: https://github.com/reactvision/viro/blob/main/docs/PLATFORM_EXTENSIONS.md
Defines the configuration options for world mesh generation and the statistics returned when the mesh is updated. Use `worldMeshConfig` for fine-grained control over physics and debug drawing.
```typescript
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
};
type ViroWorldMeshStats = {
vertexCount: number;
triangleCount: number;
source: "lidar" | "monocular" | "plane";
};
```
--------------------------------
### Import ViroGameLoop
Source: https://github.com/reactvision/viro/blob/main/docs/PLATFORM_EXTENSIONS.md
Import the ViroGameLoop component from the @reactvision/react-viro package.
```tsx
import { ViroGameLoop } from "@reactvision/react-viro";
```
--------------------------------
### Physics Usage with ViroARScene
Source: https://github.com/reactvision/viro/blob/main/docs/PLATFORM_EXTENSIONS.md
Enables physics simulation in your AR scene by adding a `physicsWorld` prop. This is required for JS physics objects to interact with the AR environment.
```tsx
```