Engineering a 240Hz Deterministic Physics Engine in Pure Swift for iOS 17
PHYSICS & SIMULATIONSeptember 10, 20268 min read

Engineering a 240Hz Deterministic Physics Engine in Pure Swift for iOS 17

When building Velocity Unleashed, one of our uncompromising engineering constraints was ensuring that driving at 285+ MPH felt visceral, precise, and completely free of physics stutter or collision tunneling.

Most mobile games rely on standard variable delta time ($dt$) tied directly to the display frame rate (30, 60, or 120 FPS). But at supersonic racing speeds, variable $dt$ introduces non-deterministic tire slip, erratic barrier bounces, and floating-point divergence between devices.

Here is how we architected a deterministic 240Hz fixed-step physics engine in pure Swift 5.9 leveraging SIMD vector math on Apple Silicon.

Why 60Hz Variable Time Fails at 285 MPH

  • At 285 MPH (127.4 m/s), a vehicle travels over 2.12 meters per frame at 60 FPS. If a physics step only evaluates once every 16.6ms:
  • A car can tunnel completely through thin track barriers or rumble strips before collision detection registers
  • High-frequency suspension dampers and anti-roll bars oscillate out of control, causing vehicles to launch violently into the air
  • Multiplayer ghost replays and leaderboards fail to reproduce identically across different hardware

To solve this, we decoupled rendering from simulation using a 240Hz fixed-clock time accumulator (FixedStepClock.swift). At 240Hz, the simulation steps forward every 4.166 milliseconds, reducing travel distance to just 0.53 meters per tick.

---

1. The Fixed-Step Accumulator (`FixedStepClock.swift`)

The game loop captures elapsed real time from the display CADisplayLink / SceneKit renderer and feeds it into an accumulator buffer. The engine consumes fixed $dt = 1/240$ slices until the accumulated time is depleted.

public final class FixedStepClock {
    public static let targetFrequency: Double = 240.0
    public static let fixedDeltaTime: Float = 1.0 / Float(targetFrequency) // 0.0041667s
    
    private var accumulatedTime: Double = 0.0
    private let maxAccumulatedTime: Double = 0.1 // Clamps spiral of death
    
    public func advance(realDeltaTime: Double, tick: (Float) -> Void) -> Float {
        accumulatedTime += min(realDeltaTime, maxAccumulatedTime)
        
        while accumulatedTime >= Double(Self.fixedDeltaTime) {
            tick(Self.fixedDeltaTime)
            accumulatedTime -= Double(Self.fixedDeltaTime)
        }
        
        // Alpha interpolation factor for sub-frame smooth rendering
        return Float(accumulatedTime / Double(Self.fixedDeltaTime))
    }
}

Alpha State Interpolation Even when the physics runs at 240Hz, rendering on an iPhone ProMotion display occurs at 120Hz or 60Hz. To eliminate micro-jitter between physical ticks and visual frames, we perform linear state interpolation (alpha in [0, 1]):

visualPosition = simd_mix(previousPhysicsPosition, currentPhysicsPosition, alpha)
visualRotation = simd_slerp(previousPhysicsRotation, currentPhysicsRotation, alpha)

This guarantees butter-smooth rendering without sacrificing physical determinism.

---

2. Pacejka Magic Formula Tire Dynamics (`VehicleDynamics.swift`)

Real racing dynamics live or die by the tire contact patch. Rather than using simplified arcade raycast grip, we implemented a modified Pacejka Magic Formula curve:

$$F_y = D \cdot \sin(C \cdot \arctan(B\alpha - E(B\alpha - \arctan(B\alpha))))$$

  • Where:
  • alpha: Lateral tire slip angle
  • B: Stiffness factor (governs initial steering bite on turn-in)
  • C: Shape factor (determines the peak transition)
  • D: Peak friction coefficient multiplied by normal load (Fz)
  • E: Curvature factor (controls how grip falls off into a drift)
Grip Force (Fy)
     ▲          Peak Grip (Turn-in)
     │               ╭──╮
Peak ┼──────────────╭╯  ╰─────────── Dynamic Slide Zone
     │             ╭╯               (Controllable Counter-Steer)
     │            ╭╯
     │           ╭╯
     │          ╭╯
     0 ─────────┴─────────────────────► Slip Angle (α)

Dynamic Weight Transfer As the driver brakes hard into a sharp chicane, inertia transfers mass forward across the suspension geometry:

let longitudinalAccel = (currentSpeed - previousSpeed) / dt
let pitchTransfer = (carMass * longitudinalAccel * centerOfGravityHeight) / wheelbase
let frontAxleLoad = (staticFrontLoad + pitchTransfer).clamped(min: 200, max: carMass * 0.85)
let rearAxleLoad = (staticRearLoad - pitchTransfer).clamped(min: 150, max: carMass * 0.85)

This allows drivers to execute authentic trail braking — loading up the front tires to rotate the car into the apex before applying power.

---

3. Ground-Effect Aerodynamics & Slipstream Wakes

Modern open-wheel cars generate massive downforce through underfloor venturi tunnels. In Velocity Unleashed, downforce is calculated quadratically:

$$F_{\text{downforce}} = \frac{1}{2} \rho \cdot v^2 \cdot C_L \cdot A$$

  • Ground Effect Compression: As the car compresses on high-speed banked turns, ride height drops, multiplying $C_L$ by up to 1.45x.
  • High-Speed Stability: At 250+ MPH, total downforce exceeds 1,250 kg — more than the car's weight — giving incredible grip in high-speed sweepers.

The 3D Slipstream Vortex Cone Behind each AI and player car, we compute a real-time turbulent wake cone:

let distanceToLeader = simd_length(leaderPos - followerPos)

if forwardDot > 0.92 && distanceToLeader < 45.0 { let wakeIntensity = (1.0 - (distanceToLeader / 45.0)) * forwardDot dragCoefficient *= (1.0 - (wakeIntensity * 0.38)) // 38% drag reduction drsRechargeRate += wakeIntensity * 1.5 // Charges nitro/KERS } ```

Tucking behind an opponent cuts aerodynamic drag by up to 38%, creating massive slingshot overtaking opportunities down the main straights.

---

4. Sub-Millisecond SIMD Execution on Apple Silicon

A physics engine running 240 times per second must execute in under 1.0 millisecond per tick to prevent frame drops.

  1. 1To achieve this in Swift 5.9:
  2. 2Zero Heap Allocations in Tick Loop: All vehicle states, raycasts, and force accumulators use stack-allocated Swift structs with no reference-type overhead.
  3. 3Apple SIMD Accelerated Vectors: We use `simd_float3`, `simd_quatf`, and `simd_float4x4` directly, compiling down to hardware NEON vector instructions.
  4. 4No Dynamic Dispatch: Physics methods are marked `final` and marked for aggressive compiler inlining (`@inline(__always)`).

Benchmarked Execution Time On an Apple A17 Pro (iPhone 15 Pro), our entire 240Hz physics update for a 6-car race grid completes in just **0.28 milliseconds** per step:

  • Raycast Ground Probing: 0.08 ms at 240 Hz
  • Tire Slip & Pacejka Model: 0.06 ms at 240 Hz
  • Aerodynamics & Aero Wakes: 0.04 ms at 240 Hz
  • Collision & Barrier Resolvers: 0.07 ms at 240 Hz
  • State Integration & Clock: 0.03 ms at 240 Hz
  • Total Simulation Step: 0.28 ms (Comfortably under 4.16ms budget)

---

Conclusion: Crafting Console-Grade Mobile Racing

By uniting a deterministic 240Hz fixed-step clock, true Pacejka tire friction, ground-effect aerodynamics, and hardware SIMD vectors, Velocity Unleashed delivers high-fidelity handling that rewards skill and racecraft.

Experience the physics for yourself: Download Velocity Unleashed free on the App Store →

0
LinkedIn
Sharing to LinkedIn?Rich 1200x630 Card
Copy a formatted technical post with relevant tags, or share directly.
PADDOCK CONVERSATIONS

Community Responses0

Leave a Response / Technical Question

Share feedback on this dev log, ask the engineering team a question, or discuss game mechanics with other racers.

Be respectful to fellow racers.

No comments yet.

Be the first to share your thoughts on this dev log!