vidar.games

Parkour momentum controller

2026-11-06 · Discuss on X

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.

  1. 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.
  2. Coyote time: a jump still counts for 0.12 s after the feet leave the edge.
  3. Input buffer: a jump pressed up to 0.15 s early fires when it becomes possible.
  4. 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.
  5. 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.
  6. 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

When to use it

Third-person movement where flow matters: parkour, chases, traversal-heavy levels.

New breakdowns go out on X first: @vidar_0208. If this one helped, you can buy me a coffee.

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.