Repository navigation
Physics and movement integrate the same real time at any frame rate - #2315
Merged
Merged
Conversation
The Rapier backend stepped once per call with fixedDt as the timestep, so 1s of play ran 0.5s at 30fps and 2.4s at 144fps; it now accumulates like PhysicsWorld. PhysicsWorld bounds-floor friction was a 60Hz multiplicative brake six times stronger than contact friction; it is now Coulomb. Player jump and character-controller gravity integrate trapezoidally, so apex is v^2/2g instead of frame-rate dependent. Gates: backend conformance (same distance at 30/60/144fps), Rapier fixedDt accumulation, movement apex across four rates, resting body penetration/velocity/drift, floor friction parity.
Noisemaker111
enabled auto-merge
October 24, 2026 02:14
…olerance At 60 Hz the fall and rise land on the same tick, so the strict comparison sat on its own boundary. Sample at 240 Hz where the 1.6x fall gravity resolves (0.1667s fall vs 0.1875s rise) and allow one 240 Hz tick of rounding; the failure message prints both numbers.
This was referenced Oct 24, 2026
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Physics and movement integrate the same real time at any frame rate.
Three separate causes, each measured with a headless scenario before it was named.
Causes and before/after
packages/rapier/src/rapierBackend.ts:125setworld.timestep = fixedDt ?? dtand stepped once per call, contradicting the contract inphysicsBackend.ts:151. A ball withfixedDt: 1/60, 2 s of real time:y = 15.075at 30 fps (half the simulated time) vs0.199at 144 fps (2.4×). Now 30 and 60 fps agree exactly and 144 fps is within one fixed step.PhysicsWorldfloor friction was a 60 Hz multiplicative brake —physicsWorld.ts:779didvelX *= 1 - frictionevery substep (0.6^60/s), six times stronger than contact friction. A crate at 4 m/s travelled 0.165 m on the world floor and 0.970 m on a floor collider with the samefriction: 0.4. Now 0.967 m and 0.970 m.movementModel.ts:372andcharacterController.ts:268. The same jump peaked at 0.933 / 0.992 / 1.026 m at 30 / 60 / 144 fps, an 11% band from frame rate alone (analytic 1.050). Now 1.05000 / 1.05000 / 1.05012 m.Checked and cleared, so they are not part of this change: the heightfield walker keeps feet exactly on the terrain (clearance 0.00000 over 3 s at every rate, zero drift standing still),
PhysicsWorldwas already frame-rate invariant, and the gravity sign mapping is correct.Gates, each proven red on the old code first
physicsBackendConformance.ts) — red on the old Rapier step, green on both backends now.fixedDtaccumulation (createRapierBackend.test.ts) — old code gave 5.0 m instead of 10 m; red, now green.movementProbe.test.ts) — old spread 0.093 m; red, now green.physicsWorld.test.ts).bun run verify:fast11/11. Core physics + movement 682 pass, rapier 11 pass, typechecks clean.Open, not fixed here
Adoption
Games get these from a release.
simulation.hzstill defaults to"variable", so game-layer per-tick math still runs on frame dt: character travel differed 0.37% and the force-model vehicle 2.8% over 3 s between 30 and 144 fps. Harbor Heat, Scrap Signal and Drift Foundry should declaresimulation: { hz: 60 }. Not changed here.Jump apex at 60 fps is 6% higher than before;
jumpVelocity7.1 → 6.9 keeps the old height if a game wants it.