Course outline
Syncing Physics and Rendering
In one line
The physics world and the three.js scene are two separate copies of the world. After every step, copy each body's position and rotation into its mesh, and never move the mesh by hand: the body owns the transform. In React Three Fiber, @react-three/rapier does this for you.
Think of it like this
A puppet and its shadow on the wall. You move the puppet; the shadow follows. If you paint a new shadow on the wall instead, it no longer matches the puppet, and the next time the puppet moves, your painting is gone.
Words you'll meet
New words in this topic, in plain English. Come back here whenever one feels fuzzy.
- sync
- Copying each body's position and rotation into its mesh after a physics step.
- owner
- The one place allowed to change an object's transform; for a dynamic body, the physics engine.
- kinematic body
- A body your code moves by setting its next position; it pushes dynamic bodies but isn't pushed back.
- teleport
- Moving a body instantly with
setTranslation, without a path the engine can check.
Step by step
01Compare three ways to sync
Run the first example. Each line slides the same crate for 90 steps and measures how far the mesh is from its body, and how far each has turned.
02Keep one owner
For each object, decide: body-owned (dynamic: copy body to mesh) or code-owned (kinematic: your code sets the next position; the engine follows). Never both.
03Let the library do it
The second example is the same idea in R3F: two crates fall onto a fixed slab; onSleep fires when each one comes to rest.
04Read the warnings
Both warnings come from libraries, not your code. Note them, check the release notes when you upgrade, and don't hide them.
Try it yourself
- 1
Draw the colliders
In the second example, write
<Physics debug>. Coloured lines trace the edges of each collider, drawn on top of the meshes: the quickest way to see a collider that doesn't match its mesh. - 2
Slow the world
In the second example, write
<Physics gravity={[0, -1.62, 0]}>. The crates drop like on the Moon, noticeably slower, and the two "fell asleep" lines still appear (measured: both within 9 s).
Code & diagrams
Console
Run the scene to see its console.Expected console output
copy position and rotation: mesh 0.00 m from the body, turned 29° (body 29°)
copy position only: mesh 0.00 m from the body, turned 0° (body 29°)
mesh moved by hand too: mesh 0.50 m from the body, turned 29° (body 29°)@react-three/rapier 2.2.0 (built against Rapier 0.19.2) running on the course's Rapier 0.21.0: that mismatch is why the init warning appears. `onSleep` fires once per crate when it comes to rest.
Console
Run the scene to see its console.Expected console output
THREE.Clock: This module has been deprecated. Please use THREE.Timer instead.
using deprecated parameters for the initialization function; pass a single object instead
the crate that started at y 1 fell asleep
the crate that started at y 2 fell asleepBreak it on purpose
Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.
Break #1
Teleport into a wall
Zero gravity, a fixed wall 0.2 m thick at x = 1 (faces at 0.9 and 1.1), and a 0.4 m dynamic box. Teleport the box with setTranslation to x = 0.9 (half inside the wall), x = 0.8, or x = 1.0 (its centre in the middle of the wall), and step.
Myth vs fact
Myth
You can move physics objects by setting mesh.position.
Fact
The mesh is a picture of the body. The next sync puts it back, and the body never moved. Move bodies, then copy.
Pro corner
Extra depth for experienced readers. New to this? Skip it for now and come back later.
- ▸
For many bodies, copy poses into an
InstancedMeshwithsetMatrixAtinstead of a mesh per body: one draw call for a thousand crates (Topic 17.2). - ▸
With interpolation (Topic 16.2), keep the previous pose of each body and draw
lerp(previous, current, alpha);@react-three/rapier's<Physics>has aninterpolateprop for the same reason.
Remember this
- 1
The problem this solves: physics moves bodies, but you see meshes. If the two get out of step, the picture lies: a crate looks upright while its body lies on its side, or a mesh moved by code walks through a wall that its body cannot pass.
- 2
Copy both, every step.
mesh.position.copy(body.translation())andmesh.quaternion.copy(body.rotation()). The first example slides a crate down a slope: copying both keeps the mesh exactly on the body; copying only the position leaves the mesh at 0° while the body has turned 29°. Rapier gives positions and rotations as plain{ x, y, z }and{ x, y, z, w }objects, which three.js'scopyaccepts. - 3
Who owns the transform. For dynamic bodies, the body owns it; the mesh only shows it. Moving the mesh by hand is undone at the next copy and, until then, the picture is wrong (the example's third line: 0.50 m away). To move a dynamic body, apply a force or impulse, or set its velocity. To place it once (a reset),
body.setTranslation()andsetRotation(). For things your code moves (a platform), use a kinematic body withsetNextKinematicTranslation(): it pushes dynamic bodies out of its way. - 4
Teleports.
setTranslationjumps without a path, so it can put a body inside another. Measured: a 0.4 m box teleported half inside a fixed wall is pushed back out over many steps, not at once (about 10 steps to get out, and then it overshoots with the speed it gained), and one teleported to the middle of a 0.2 m wall pops out of the far side. Teleport only into free space, as the build's reset does (back to the start). - 5
In React Three Fiber.
@react-three/rapierwraps Rapier in components:<Physics>creates and steps the world (with its own fixed-step loop), and<RigidBody>makes a body whose collider is generated from its child meshes (colliders="cuboid") and copies the pose to them each frame. The second example prints two real warnings: R3F 9.8.2'sTHREE.Clockdeprecation (Topic 11.1), and Rapier's ownusing deprecated parameters for the initialization function; pass a single object instead, because this version of the library callsinit()the older way.
Explain it without notes
Who should own an object's transform in a physics scene, and what goes wrong otherwise?
How do you move a dynamic body, a kinematic body and a fixed one?
Practice
Write syncAll(pairs) that copies every body's pose into its mesh.
Make a moving platform: a kinematic body that slides back and forth 1 m, carrying a crate.
Done when you can
You copy position and rotation after each step
You keep one owner per object
You move dynamic bodies with forces or velocities, not by moving meshes
You can use @react-three/rapier and read its warnings