# OpenSpec Change: enterprise-gaming-vehicle-physics - **Change ID**: enterprise-gaming-vehicle-physics - **Package**: `@suaveplan/vehicle-physics` - **Category**: gaming/rigs - **Target path**: `gaming/rigs/vehicle-physics` - **Status**: PROPOSED - **Date**: 2026-04-24 - **Reference spec**: `/mnt/s/web/suaveplan/enterprise/vehicle-physics.md` --- ## Summary Deliver `@suaveplan/vehicle-physics` on the genesis branch from scratch at enterprise quality. The package provides engine-agnostic vehicle physics using Pacejka Magic Formula tire model, spring-damper suspension per wheel, multi-wheel drivetrain (FWD/RWD/AWD), differential, braking, and aerodynamic drag. All math is pure TypeScript with no runtime deps — physics backends plug in via interface. Genesis adds Zod config schemas, structured errors, injectable clock, OTel telemetry, and property tests verifying tire force symmetry and suspension convergence. --- ## Current-state audit (genesis) Package does not exist on genesis yet — fresh build. --- ## Available workspace deps on genesis | Package | Role | |---|---| | `@suaveplan/error` (`workspace:^`) | `VehiclePhysicsError`; `VEHICLE.*` codes | | `@suaveplan/types` (`workspace:^`) | `Clock` | | `@suaveplan/events` (`workspace:^`) | `vehicle:collision`, `vehicle:flip`, `vehicle:stall` events | | `zod` (`catalog:forms`) | `VehicleConfig` schema | | `@opentelemetry/api` (`catalog:observability`) | Step duration histogram | --- ## Public API surface ```ts function createVehicle(config: VehicleConfig): Vehicle interface VehicleConfig { mass: number wheelBase: number trackWidth: number centerOfMassY: number engine: EngineConfig transmission: TransmissionConfig wheels: WheelConfig[] aerodynamics?: AeroConfig backend?: VehiclePhysicsBackend clock?: Clock } interface EngineConfig { maxTorque: number // Nm maxRpm: number torqueCurve?: [rpm: number, torque: number][] idleRpm?: number } interface TransmissionConfig { type: 'FWD' | 'RWD' | 'AWD' gearRatios: number[] finalDrive: number differentialSlip?: number } interface WheelConfig { position: Vec3 radius: number suspensionRestLength: number suspensionStiffness: number suspensionDamping: number tireGripLateral: number tireGripLongitudinal: number } interface Vehicle { update(dt: number, input: VehicleInput): void teleport(position: Vec3, rotation: Quat): void applyImpulse(impulse: Vec3): void flip(): void readonly position: Vec3 readonly rotation: Quat readonly velocity: Vec3 readonly speed: number // m/s readonly rpm: number readonly gear: number readonly wheelsGrounded: boolean[] } interface VehicleInput { throttle: number // 0–1 brake: number // 0–1 steering: number // -1 to 1 handbrake: boolean clutch?: number } ``` ### Error codes ```ts type VehicleErrorCode = | 'VEHICLE_INVALID_CONFIG' | 'VEHICLE_BACKEND_NOT_SET' | 'VEHICLE_UPDATE_FAILED' | 'VEHICLE_INVALID_GEAR' ``` --- ## New additions for genesis 1. **Pacejka tire model** — lateral and longitudinal force from slip angle / slip ratio; configurable B/C/D/E coefficients. 2. **Automatic gear shifting** — RPM-based shift logic with configurable up/down thresholds. 3. **Aerodynamic drag + downforce** — `0.5 * rho * Cd * A * v²` drag; downforce scales with speed². 4. **Suspension raycast** — `VehiclePhysicsBackend.castRay()` for each wheel per tick; ground contact from hit distance. --- ## File layout ```text gaming/rigs/vehicle-physics/ package.json tsconfig.json vitest.config.ts src/ index.ts error-codes.ts constants/index.ts types/common.ts types/common.md core/ vehicle/ vehicle.ts vehicle.md vehicle.types.ts vehicle.test.ts index.ts models/ pacejka/ pacejka.ts pacejka.md pacejka.test.ts index.ts suspension/ suspension.ts suspension.md suspension.test.ts index.ts drivetrain/ drivetrain.ts drivetrain.md drivetrain.test.ts index.ts backends/ null-backend/ null-backend.ts null-backend.md null-backend.test.ts index.ts tests/ property.test.ts ``` --- ## Impact **Downstream**: `@suaveplan/r3f-vehicle` wires this to Rapier. Prerequisites: `@suaveplan/error`, `@suaveplan/types`, `@suaveplan/events`.