Parkour momentum controller
Same rooftop course, same jump presses.
Left: every move is its own state, and speed resets at each one.
Right: speed carries through the vault, jump, landing and wall climb.
Finish: 5.30 s vs 3.93 s.
The problem
A movement system can have every move and still feel stiff. If each state sets its own speed, every transition is a small stop: land, pause, run again. Topic via @unity3dvfx. I built both versions on the rapier.js character controller to see where the time goes.
How it works
Both runners use the same capsule, 1.6 m tall, moved by rapier's KinematicCharacterController at 120 Hz. It handles walls, floors and snapping to the ground. Speed, states and transitions are my code.
- One horizontal speed for every state. It accelerates toward 7 m/s at 16 m/s² on the ground and 4 m/s² in the air. No state sets it from scratch; only the vault, landing and mantle trim it.
- Coyote time: a jump still counts for 0.12 s after the feet leave the edge.
- Input buffer: a jump pressed up to 0.15 s early fires when it becomes possible.
- Vault: a ray at knee height finds a wall within 0.9 m. If its top is under 1.1 m, a short curve carries the capsule over at 95% of its speed.
- Wall climb: running into a high wall turns 85% of the run speed into climb speed. A 0.3 s mantle puts it on top.
- Landing: the body crouches in proportion to fall speed. At 10.3 m/s that's 0.30 m, and it keeps 97% of its speed.
What I hit
My first wall run started 1.07 m from the wall, so the capsule climbed thin air. Now it keeps running until the ray reports contact, then converts the speed. Contact comes 0.16 s later.
The late jump was pressed 0.07 s after the capsule centre passed the edge, but 0.09 s after the controller last reported ground. Coyote time counts from that grounded flag, so the window has to cover the gap. 0.12 s did.
My build
The left runner snaps to 7 m/s on the ground and 4.5 m/s in the air. It pauses 0.25 s on every landing and stops dead at the low wall before a fixed 0.7 s vault. Its late jump is ignored, so it drops off the edge. At the high wall it grabs the ledge, climbs at 1.6 m/s, then pulls up for 0.4 s.
The right runner's biggest loss is the mantle, which exits at 5.19 m/s. The jump pressed in the air just before touchdown went through the buffer and started the wall run.
Trade-offs
- Transitions stop costing time, and the run reads as one motion
- Coyote time and the buffer forgive inputs a few frames off
- The vault and mantle curves are scripted, so they can clip odd geometry
- Every number is tuned to one capsule and one gravity (22 m/s²)
- More states share one speed, so a bug in one leaks into the others
When to use it
Third-person movement where flow matters: parkour, chases, traversal-heavy levels.
Source and credits
Topic credit: @unity3dvfx (third-person parkour movement demo).
Collision and movement: rapier.js by Dimforge (@dimforge), https://github.com/dimforge/rapier.js, Apache-2.0 license, source read at commit 9f638c5384c282a8abd22515973ac7c82ccfbc43 (newest CHANGELOG entry 0.19.3); run as the npm package rapier3d-compat 0.19.3. Used: KinematicCharacterController and world ray casts.
Rendering: three.js by @mrdoob, https://github.com/mrdoob/three.js, MIT license (npm 0.170.0).
Code: rapier.js and three.js are used unchanged through their public APIs; no source files copied or modified. Both movement controllers (speed, coyote time, input buffer, vault and mantle curves, wall run-up, landing crouch), the course, the mannequin and the renderer are my own. Everything in the video was rendered by me; no footage, game, character or level from the original post. No external assets. No IA model.