A React Native demo of three-vrm with custom High-Performance Native Renderers.
✅ Web: Three.js + three-vrm (Standard implementation)
✅ Android: Native renderer via Filament + gltfio (Android 12+)
✅ iOS: Native renderer via VRMKit + RealityKit (iOS 18+)
Because this project uses Expo Prebuild, the ios/ and android/ folders are transient and can be regenerated at any time. To keep our custom native code persistent and manageable, we use a specialized Expo Plugin: plugins/withNativeRestore.js.
- Code Synchronization: It maintains a "Source of Truth" in the
/nativetop-level directory.native/android/vrm↔android/app/src/main/java/.../vrmnative/ios/NativeModules↔ios/NativeModules
- Automatic Injection:
- Android: Automatically registers the
NativeVrmPackageinMainApplication.ktand adds Filament/Kotlin-Math dependencies tobuild.gradle. - iOS: Automatically registers all
.swift,.m, and.hfiles fromNativeModulesinto the Xcode PBXProject.
- Android: Automatically registers the
- Environment Setup: Ensures the correct Android SDK path is configured in
local.properties.
The iOS renderer leverages RealityKit, Apple's high-performance AR/Rendering framework, wrapped by the VRMKit library.
- Loader:
ios/NativeModules/NativeVRMView.swift - Engine: RealityKit (using physically based materials).
- VRM 1.x Compatibility: Since VRMKit 0.7.1 is optimized for VRM 0.x, we use a Metadata Shim (in
VRMShimUtils.swift) that intercept VRM 1.x models and injects a legacy VRM 0.x extension on-the-fly. This allows the RealityKit loader to recognize bone mappings and expressions for modern models.
- Y-Axis Correction: VRM 0.x models are automatically rotated 180° to face the camera, matching the Three.js convention.
- Bone Mapping: RealityKit entities are mapped to normalized humanoid bone names for consistent animation.
The Android renderer uses Google's Filament, a real-time physically based rendering engine used in Google Search and Maps.
- Loader: Uses
gltfiofor efficient GLB parsing. - Surface: Renders into a
TextureViewviaUiHelperandSwapChainfor seamless integration with React Native. - Render Loop: Driven by a hardware-synced
Choreographercallback.
- MToon Approximation: Filament's standard PBR materials are procedurally adjusted based on VRM metadata to simulate anime-style shading (setting
baseColorFactor,shadeColor, and disabling culling for hair). - Native Lip Sync: Features a dedicated RMS-based audio analyzer that drives mouth morph targets directly on the GPU without JS overhead.
- Spring Bones: A native Kotlin implementation of the VRM Spring Bone physics system handles hair and clothing movement.
Animations are computed in the Javascript thread using three-vrm to ensure logic parity across all platforms. The results are mirrored to the native renderers at 60fps.
graph LR
subgraph "Javascript (Three.js)"
A[Animation Mixer] --> B[VRM Model]
B --> C[Bone Quaternions]
B --> D[Expression Weights]
end
subgraph "Bridge (Native Props)"
C --> E{Delta Calc}
D --> F[Direct Dispatch]
end
subgraph "Native (RealityKit / Filament)"
E --> G[Apply Humanoid Bones]
F --> H[Apply BlendShapes]
G --> I[Native Render]
H --> I
end
Located in src/features/fiberCanvas.tsx, it uses a requestAnimationFrame loop to sample the JS model state and dispatch it to native views using setNativeProps for maximum performance, bypassing the standard React diffing cycle.
To support models with varying rest poses (T-Pose vs. A-Pose), we send Relative Deltas from the T-Pose rather than absolute rotations. The native logic applies these deltas to its own internal rest transform.
| Android (Filament) | iOS (RealityKit) |
|---|---|
Android-LipSync.mp4
iOS-LipSync.mp4
- VRMKit package: tattn
- Audio files: Akira Hoshino
Note: JDK 21 is required to build the Android project due to the Filament native renderer. Please ensure your
JAVA_HOMEor system environment is configured for JDK 21.
- Clone this repo.
- Run
bun i. - Run
npx expo prebuild(to generate native folders and trigger the restore plugin). - Run
npx expo run:androidornpx expo run:ios.
To test the extra animations:
- Download the motion pack.
- Extract to
assets/animations/motion_pack.
The NativeVRMView component does not work in Expo Go. You must use a Development Build or run via npx expo run:[android|ios].