External Publication
Visit Post

Jolt Vehicle Tuning Guide

jMonkeyEngine Hub April 22, 2026
Source

Here is a claude written summary on our tuning guide for a jolt car. In the end i got a quite fun to drive car.

Tried to get Jolt’s VehicleConstraint to simulate a 2020 Porsche 718 Cayman GT4 (982) well enough that published 0-100 km/h, 0-150, and 100-0 figures match within ~10%. Sharing what moved the needle (and what didn’t), in case it saves someone else the iteration cost.

Stack: jolt-jni 4.0.1 (Java binding for Jolt Physics), jMonkeyEngine 3. Single-car RWD that also needs to do AWD cleanly for cross-drive-train comparisons.

Where Jolt’s defaults come up short

Jolt’s VehicleConstraintTest.cpp is a great starting point but the sample car is tuned as a generic sedan. For a sports car, several things need deliberate tuning:

  1. Anti-roll bars: sample ships 1 000 N·m/rad. Sports-car realistic is 20 000–40 000. But at 60 Hz with 1 collision substep, 20 000 pushed the roll mode past the integrator’s stability margin — the car would flip on tiny bumps. Fix: 10 000 N·m/rad + PhysicsSystem.update(dt, 2, …) (2 substeps). Solver stable, corners feel sporty.
  2. Suspension damping: default 0.5 ratio lets the chassis rock for 4-5 cycles after any bump. ARBs then couple the bounces across each axle so it becomes self-sustaining. Fix: near-critical 0.85–0.90 damping ratio per wheel. Wobble dies in one cycle.
  3. Max pitch/roll cutoff: past this angle Jolt disables wheel torque (car becomes a brick). Default 60° strands the car on any side-impact. Fix: 90°. Driver can throttle/steer to self-right even when fully on the side.
  4. Auto-sleep: dynamic bodies sleep after idle. Once asleep, wheel-torque impulses don’t wake them and the driver inputs silently do nothing. Fix: BodyInterface.activateBody(chassisId) whenever any non-zero input arrives.

Friction model is where realism lives

Jolt’s default WheelSettingsWv longitudinal friction curve runs (0,0)(0.06,1.2)(0.2,1.0) and then extrapolates negatively past slip=0.2. A hard RWD launch puts the slip at 3+ instantly (engine hits redline while wheels crawl), grip collapses toward zero, the car crawls.

Custom curves that fixed it:

// Longitudinal — keeps grip across a wide slip band (0.00, 0.0) (0.06, 1.5) (0.20, 1.4) (0.50, 1.2) (1.00, 1.0) (3.00, 0.7) (10.0, 0.55) (100.0, 0.45)

// Lateral — Pacejka shape, drops past 20° so drift becomes possible (0°, 0.0) (3°, 1.3) (20°, 1.0) (40°, 0.7) (90°, 0.4)

Friction circle — the one Jolt doesn’t give you

Jolt’s solver clamps longitudinal and lateral impulses independently (rectangular region). That means a rear tire burning out in pure wheelspin still keeps full lateral grip — no power-oversteer even with a stiff RWD layout.

Real tires share a grip budget across both directions (the friction circle). jolt-jni exposes CustomTireMaxImpulseCallback which receives slip ratios for both axes per wheel. Scaling each axis’ max impulse based on the other axis’ slip saturation closes the gap:

// Max impulse on this axis = baseline × sqrt(1 − n²) // where n = clip(|other_slip| / other_peak_slip, 0, 0.9)

The 0.9 cap leaves a 44% grip floor so launch wheelspin (absurd longitudinal slip) can’t zero out lateral grip and make the car float sideways. With this: throttle-on mid-corner breaks the rears out as expected.

Engine / transmission — where my 20% error lives

Jolt’s automatic transmission + clutch model burns real power:

  • Clutch counter-torque saturates at roughly the engine’s max torque. During launch the engine pegs at redline with 6000 rpm of slip to the wheels; adding clutch strength past ~100 does nothing further.
  • Each gear shift costs ~`switchTime + clutchReleaseTime + switchLatency`. With Jolt defaults (0.25 + 0.15 + 0.50 = 0.9 s) you lose ~2 s over 0-100. Shortening to 0.10 + 0.05 + 0.05 (DCT-fast) recovers most of it.
  • Engine flywheel inertia defaults to 0.5 kg·m² (truck); sports-engine realistic is ~0.15.
  • Default mNormalizedTorque curve drops off sharply outside a narrow RPM band. A flatter curve (>85 % peak torque from 3500-7000 rpm) keeps the car pulling between shifts.

Net: roughly 15-20 % of acceleration disappears into model losses vs. real-world figures. I carry a single SIM_TORQUE_COMPENSATION = 1.40f multiplier in VehicleFactory so PorscheGT4Spec records the real 420 N·m (not 500+) and the published 0-100 time is still hit. Any future car spec enters factory-sheet torque and gets the compensation for free.

Aerodynamics — Jolt doesn’t model it

Drag and downforce added as body forces per frame:

F_drag = −½·ρ·Cd·A·|v|·v // ρ = 1.225 kg/m³ F_down = −½·ρ·(Cl·A)·|v|²·ŷ // pushes chassis into ground

Downforce raises each wheel’s suspensionImpulse → higher tire max grip automatically via the friction callback. At 150 km/h, a GT4 sees ~800 N of drag and ~425 N of downforce (3 % of weight).

Spec per-car: dragCoefficient, frontalArea, downforceCoefficient. New cars (BMW M, Ferrari) just plug their factory values.

Collision tester: cast-cylinder, not ray

VehicleCollisionTesterRay (the obvious default) pops visibly on kerbs and crowned surfaces. VehicleCollisionTesterCastCylinder casts the actual wheel shape and gives correct contact on edges. Trivial swap, noticeably better over rough geometry.

Runtime driver assists

TCS and ABS are simple slip-threshold gates in the driver-input path:

  • TCS trims throttle when any driven wheel’s longitudinal slip exceeds 6 m/s. Linear fade to zero at 12 m/s. Invisible on clean launches, kicks in on throttle-on-corner or low-µ surfaces.
  • ABS pulses brake to 60 % when any wheel’s slip exceeds 12 m/s under braking.

Both toggleable at runtime — matches how real PSM/PDK systems behave: invisible under clean inputs, save you when the driver asks for more than the tire has.

Verification loop

Built a JUnit test that spawns the car on a flat plate and measures 0-N km/h + 100-0 braking. It’s been the single highest-value dev-loop asset: tune something, re-run the test, see whether it moved the needle. Mostly saves guessing which of 15 interacting parameters is responsible.

Final numbers against real 982 GT4:

┌────────────────┬─────────────────┬────────────────┐ │ │ Sim │ Real │ ├────────────────┼─────────────────┼────────────────┤ │ RWD 0-100 km/h │ 4.77 s │ 4.4 s │ ├────────────────┼─────────────────┼────────────────┤ │ RWD 0-150 km/h │ 9.35 s │ ~9.3 s │ ├────────────────┼─────────────────┼────────────────┤ │ AWD 0-100 km/h │ 3.70 s │ — │ ├────────────────┼─────────────────┼────────────────┤ │ 100-0 km/h │ 2.60 s / 36.4 m │ ~2.6 s / ~32 m │ └────────────────┴─────────────────┴────────────────┘

Things I’d warn others about

  1. Don’t guess parameters. Write an acceleration/braking test before tuning. You’ll change 15 interacting knobs; without a benchmark you have no idea what’s helping.
  2. Jolt clamps rectangularly, not as a friction circle. If your sim “can’t drift even with wheelspin”, that’s why. The TireMaxImpulseCallback fix is ~40 lines.
  3. Vehicle respawn leaks a constraint + step listener if you only remove the body. They keep running against a destroyed chassis and cause mystery behaviour on the next spawn. Expose a dispose() that removes constraint → step listener → body in order.
  4. Auto-sleep quietly breaks things. Wake the chassis on any non-zero input.
  5. Wheel inertia isn’t exposed in jolt-jni 4.0.1 (Jolt has it, binding doesn’t). If you care, file a PR or wrap around it.
  6. Aerodynamics isn’t in Jolt; add it yourself and expect to tune it against a torque-compensation multiplier — the two interact because both eat the same “excess-power-over-load” headroom.

Happy to share code snippets for any of the specific pieces — drop a reply.

Discussion in the ATmosphere

Loading comments...