How we built it
DriveCoach is built on a custom wgpu → Metal rendering pipeline via pygfx, bypassing the deprecated OpenGL stack
entirely for native Apple Silicon GPU throughput at 60 FPS with a fully procedural city, cockpit geometry, and 16
autonomous NPC agents.
The force feedback subsystem required a ground-up implementation of the HID++ 2.0 protocol over IOHIDDeviceSetReport — reverse-engineered from the Linux kernel driver (hid-logitech-hidpp.c) — to drive the G920's motor on macOS where no official SDK, DirectInput layer, or ff-memless abstraction exists.
Vehicle dynamics are modeled using a kinematic bicycle model with friction-circle grip limiting, longitudinal/lateral
force coupling, dynamic load transfer across the axle, and a six-speed engine with realistic torque curves. Force
feedback is derived from self-aligning torque (SAT) with pneumatic trail collapse at the grip limit, combined with
viscous damping — eliminating the feedback oscillations endemic to centering-spring implementations. Audio is fully
synthesized in real-time via phase-continuous harmonic oscillators and bandpass-modulated noise generators, with zero
pre-recorded assets.
Challenges we ran into
The G920 on macOS is completely undocumented. macOS surfaces the device as a _GCLogitechRacingWheel through
GameController.framework but provides no FFB output path. We discovered the device answers HID++ long reports (0x11)
on the very-long report ID (0x12) — a non-standard behavior that breaks every off-the-shelf HID library. The full
handshake sequence requires dynamic feature-index discovery via GET_FEATURE(0x8123), hardware effect slot allocation
via DOWNLOAD_EFFECT, and a 100 Hz output loop to satisfy the G920's 200ms hardware watchdog before the motor zeros
out.
The deeper challenge was FFB fidelity. Virtually all consumer-grade FFB implementations substitute a
position-proportional centering spring for real steering physics — which is both inaccurate and unstable
(motor-position feedback loop → oscillation). Replacing this with a SAT model derived from live lateral tire force,
with pneumatic trail collapse scaling as the friction circle saturates, required careful calibration against real
vehicle data.
Accomplishments that we're proud of
- The only known open-source implementation of G920 force feedback on macOS that correctly handles the HID++ 2.0
handshake, non-standard report ID mapping, and hardware watchdog timing. - A physically correct SAT-based FFB model that replicates the grip-limit warning felt in real hydraulic power
steering — not a spring approximation. - A pure-pursuit ghost coach that haptically guides learners through correct steering geometry using the same
path-following algorithm deployed in autonomous vehicle research.
- Real-time DMV-criteria scoring mapped directly to CVC section violations, producing actionable post-session
diagnostics rather than gamified point totals.
What we learned
Proprioceptive feedback dramatically accelerates skill acquisition. Testers intuitively understood understeer the
moment they felt pneumatic trail collapse through the wheel — no verbal explanation required. This validates the core
hypothesis: the haptic channel encodes vehicle state information that neither visual displays nor verbal instruction
can efficiently transmit. We also learned that correct physics and correct FFB modeling are inseparable — perceptual
realism emerges from physical accuracy, not from tuning subjective "feel."
What's next for MotorAxN
Expanding the procedural route engine with adverse-condition rendering (precipitation, reduced-visibility scenarios,
night driving with dynamic headlight simulation), instructor telemetry dashboards for driving school integration, and
multiplayer ghost-lap infrastructure for peer benchmarking. The long-term target: positioning DriveCoach as the
standard pre-licensure training protocol for the 3.5 million Americans who take a driver's test annually — bringing
simulator-grade haptic training out of professional motorsport and into mainstream driver education.
Log in or sign up for Devpost to join the conversation.