--- name: r3f-physics description: React Three Fiber physics with Rapier - RigidBody, colliders, forces, joints, sensors. Use when adding physics simulation, collision detection, character controllers, or creating interactive physics-based experiences. --- # React Three Fiber Physics (Rapier) ## Quick Start ```tsx import { Canvas } from '@react-three/fiber' import { Physics, RigidBody, CuboidCollider } from '@react-three/rapier' import { Suspense } from 'react' function Scene() { return ( {/* Falling box */} {/* Static ground */} ) } ``` ## Installation ```bash npm install @react-three/rapier ``` ## Physics Component The root component that creates the physics world. ```tsx import { Physics } from '@react-three/rapier' {/* Physics objects */} ``` ### On-Demand Rendering For performance optimization with static scenes: ```tsx {/* Physics only triggers render when bodies are active */} ``` ## RigidBody Makes objects participate in physics simulation. ### Basic Usage ```tsx import { RigidBody } from '@react-three/rapier' // Dynamic body (affected by forces/gravity) // Fixed body (immovable) // Kinematic body (moved programmatically) ``` ### RigidBody Types | Type | Description | |------|-------------| | `dynamic` | Affected by forces, gravity, collisions (default) | | `fixed` | Immovable, infinite mass | | `kinematicPosition` | Moved via setNextKinematicTranslation | | `kinematicVelocity` | Moved via setNextKinematicRotation | ### RigidBody Properties ```tsx ``` ## Colliders ### Automatic Colliders RigidBody auto-generates colliders from child meshes: ```tsx // Global default {/* Gets hull collider */} // Per-body override ``` ### Collider Types | Type | Description | Best For | |------|-------------|----------| | `cuboid` | Box shape | Boxes, crates | | `ball` | Sphere shape | Balls, spherical objects | | `hull` | Convex hull | Complex convex shapes | | `trimesh` | Triangle mesh | Concave/complex static geometry | ### Manual Colliders ```tsx import { CuboidCollider, BallCollider, CapsuleCollider, CylinderCollider, ConeCollider, HeightfieldCollider, TrimeshCollider, ConvexHullCollider } from '@react-three/rapier' // Standalone collider (static) // Inside RigidBody (compound collider) {/* Additional colliders */} // Collider args reference ``` ### Mesh Colliders For complex shapes: ```tsx import { MeshCollider } from '@react-three/rapier' // Convex hull for dynamic bodies ``` ## Applying Forces ### Using Refs ```tsx import { RigidBody, RapierRigidBody } from '@react-three/rapier' import { useRef, useEffect } from 'react' function ForcefulBox() { const rigidBody = useRef(null) useEffect(() => { if (rigidBody.current) { // One-time impulse (instantaneous) rigidBody.current.applyImpulse({ x: 0, y: 10, z: 0 }, true) // Continuous force (apply each frame) rigidBody.current.addForce({ x: 0, y: 10, z: 0 }, true) // Torque (rotation) rigidBody.current.applyTorqueImpulse({ x: 0, y: 5, z: 0 }, true) rigidBody.current.addTorque({ x: 0, y: 5, z: 0 }, true) } }, []) return ( ) } ``` ### In useFrame ```tsx import { useFrame } from '@react-three/fiber' function ContinuousForce() { const rigidBody = useRef(null) useFrame(() => { if (rigidBody.current) { // Apply force every frame rigidBody.current.addForce({ x: 0, y: 20, z: 0 }, true) } }) return ( ) } ``` ### Getting/Setting Position ```tsx import { vec3, quat, euler } from '@react-three/rapier' function PositionControl() { const rigidBody = useRef(null) const teleport = () => { if (rigidBody.current) { // Get current transform const position = vec3(rigidBody.current.translation()) const rotation = quat(rigidBody.current.rotation()) // Set new transform rigidBody.current.setTranslation({ x: 0, y: 10, z: 0 }, true) rigidBody.current.setRotation({ x: 0, y: 0, z: 0, w: 1 }, true) // Set velocities rigidBody.current.setLinvel({ x: 0, y: 0, z: 0 }, true) rigidBody.current.setAngvel({ x: 0, y: 0, z: 0 }, true) } } return ( ) } ``` ## Collision Events ### On RigidBody ```tsx { console.log('Collision with', other.rigidBodyObject?.name) console.log('Contact point', manifold.solverContactPoint(0)) }} onCollisionExit={({ target, other }) => { console.log('Collision ended with', other.rigidBodyObject?.name) }} onContactForce={({ totalForce }) => { console.log('Contact force:', totalForce) }} onSleep={() => console.log('Body went to sleep')} onWake={() => console.log('Body woke up')} > ``` ### On Colliders ```tsx console.log('Collider hit')} onCollisionExit={(payload) => console.log('Collider exit')} /> ``` ## Sensors Detect overlaps without physical collision: ```tsx {/* Visible mesh */} {/* Invisible sensor trigger */} console.log('Entered trigger zone')} onIntersectionExit={() => console.log('Exited trigger zone')} /> // Goal detection example console.log('Goal!')} /> ``` ## Collision Groups Control which objects can collide: ```tsx import { interactionGroups } from '@react-three/rapier' // Group 0, interacts with groups 0, 1, 2 // Group 12, interacts with all groups // Groups 0 and 5, only interacts with group 7 // On RigidBody (applies to all auto-generated colliders) ... ``` ## Joints Connect rigid bodies together. ### Fixed Joint Bodies don't move relative to each other: ```tsx import { useFixedJoint, RapierRigidBody } from '@react-three/rapier' function FixedJointExample() { const bodyA = useRef(null) const bodyB = useRef(null) useFixedJoint(bodyA, bodyB, [ [0, 0, 0], // Position in bodyA's local space [0, 0, 0, 1], // Orientation in bodyA's local space (quaternion) [0, -1, 0], // Position in bodyB's local space [0, 0, 0, 1], // Orientation in bodyB's local space ]) return ( <> ) } ``` ### Revolute Joint (Hinge) Rotation around one axis: ```tsx import { useRevoluteJoint } from '@react-three/rapier' function HingeDoor() { const frame = useRef(null) const door = useRef(null) useRevoluteJoint(frame, door, [ [0.5, 0, 0], // Joint position in frame's local space [-0.5, 0, 0], // Joint position in door's local space [0, 1, 0], // Rotation axis ]) return ( <> ) } ``` ### Spherical Joint (Ball-Socket) Rotation in all directions: ```tsx import { useSphericalJoint } from '@react-three/rapier' function BallJoint() { const bodyA = useRef(null) const bodyB = useRef(null) useSphericalJoint(bodyA, bodyB, [ [0, -0.5, 0], // Position in bodyA's local space [0, 0.5, 0], // Position in bodyB's local space ]) return ( <> ) } ``` ### Prismatic Joint (Slider) Translation along one axis: ```tsx import { usePrismaticJoint } from '@react-three/rapier' function Slider() { const track = useRef(null) const slider = useRef(null) usePrismaticJoint(track, slider, [ [0, 0, 0], // Position in track's local space [0, 0, 0], // Position in slider's local space [1, 0, 0], // Axis of translation ]) return ( <> ) } ``` ### Spring Joint Elastic connection: ```tsx import { useSpringJoint } from '@react-three/rapier' function SpringConnection() { const anchor = useRef(null) const ball = useRef(null) useSpringJoint(anchor, ball, [ [0, 0, 0], // Position in anchor's local space [0, 0, 0], // Position in ball's local space 2, // Rest length 1000, // Stiffness 10, // Damping ]) return ( <> ) } ``` ### Rope Joint Maximum distance constraint: ```tsx import { useRopeJoint } from '@react-three/rapier' function RopeConnection() { const anchor = useRef(null) const weight = useRef(null) useRopeJoint(anchor, weight, [ [0, 0, 0], // Position in anchor's local space [0, 0, 0], // Position in weight's local space 3, // Max distance (rope length) ]) return ( <> ) } ``` ### Motorized Joints ```tsx import { useRevoluteJoint } from '@react-three/rapier' import { useFrame } from '@react-three/fiber' function MotorizedWheel({ bodyA, bodyB }) { const joint = useRevoluteJoint(bodyA, bodyB, [ [0, 0, 0], [0, 0, 0], [0, 0, 1], // Rotation axis ]) useFrame(() => { if (joint.current) { // Configure motor: velocity, damping joint.current.configureMotorVelocity(10, 2) } }) return null } ``` ## Instanced Physics Efficient physics for many identical objects: ```tsx import { InstancedRigidBodies, RapierRigidBody } from '@react-three/rapier' import { useRef, useMemo } from 'react' function InstancedBalls() { const COUNT = 100 const rigidBodies = useRef(null) const instances = useMemo(() => { return Array.from({ length: COUNT }, (_, i) => ({ key: `ball-${i}`, position: [ (Math.random() - 0.5) * 10, Math.random() * 10 + 5, (Math.random() - 0.5) * 10, ] as [number, number, number], rotation: [0, 0, 0] as [number, number, number], })) }, []) return ( ) } ``` ## Accessing the World ```tsx import { useRapier } from '@react-three/rapier' import { useEffect } from 'react' function WorldAccess() { const { world, rapier } = useRapier() useEffect(() => { // Change gravity world.setGravity({ x: 0, y: -20, z: 0 }) // Iterate over bodies world.bodies.forEach((body) => { console.log(body.translation()) }) }, [world]) return null } ``` ### Manual Stepping ```tsx function ManualStep() { const { step } = useRapier() const advancePhysics = () => { step(1 / 60) // Advance by one frame } return } ``` ### World Snapshots Save and restore physics state: ```tsx function SnapshotSystem() { const { world, setWorld, rapier } = useRapier() const snapshot = useRef() const saveState = () => { snapshot.current = world.takeSnapshot() } const loadState = () => { if (snapshot.current) { setWorld(rapier.World.restoreSnapshot(snapshot.current)) } } return ( <> ) } ``` ## Attractors From `@react-three/rapier-addons`: ```tsx import { Attractor } from '@react-three/rapier-addons' // Attract nearby bodies // Repel bodies // Selective attraction (only affect certain groups) ``` ## Debug Visualization ```tsx {/* All colliders shown as wireframes */} // Conditional debug ... ``` ## Performance Tips 1. **Use appropriate collider types**: `cuboid` and `ball` are fastest 2. **Avoid `trimesh` for dynamic bodies**: Use `hull` instead 3. **Enable sleeping**: Bodies at rest stop computing 4. **Use collision groups**: Reduce collision checks 5. **Limit active bodies**: Too many dynamic bodies hurts performance 6. **Use instanced bodies**: For many identical objects 7. **Fixed timestep**: More stable than variable ```tsx // Performance-optimized setup {/* Use collision groups to limit checks */} ... ``` ## See Also - `r3f-fundamentals` - R3F basics and hooks - `r3f-interaction` - User input and controls - `r3f-animation` - Combining physics with animation