How Engine Simulator Synthesizes Authentic V8 Rumble from Physics, Not Samples
Hook
Most racing games fake engine sounds by playing pre-recorded audio samples at different pitches. Engine Simulator calculates the actual pressure waves from combustion events, piston motion, and exhaust harmonics—and you can hear the difference.
Context
Traditional engine audio in games and simulators relies on sample-based playback: record a real engine at various RPM ranges, then crossfade and pitch-shift the samples during gameplay. This approach sounds decent but breaks down under scrutiny. Rev limiter behavior sounds unnatural, unique engine configurations (like a flat-plane crank V8 versus a cross-plane) require entirely new recordings, and backfires or anti-lag systems need special-case handling.
Ange Yaghi built Engine Simulator to explore a fundamentally different approach: what if you simulated the actual physics of combustion, piston motion, and exhaust flow, then converted those mechanical vibrations directly into audio? Originally created as a demonstration for his YouTube channel, the project models engines from first principles—individual cylinders firing in sequence, pistons compressing air-fuel mixture, connecting rods translating linear motion to crankshaft rotation. The result is procedurally generated audio that captures the character of different engine configurations without a single pre-recorded sample. A Ferrari V12 sounds different from a Subaru flat-four because the simulation genuinely models different geometries and firing orders, not because someone loaded different WAV files.
Technical Insight
Engine Simulator’s architecture splits into three primary systems: a custom DSL for defining engine configurations, a physics simulation loop that models thermodynamics and kinematics, and an audio synthesis pipeline that converts mechanical state into sound waves.
The DSL uses Flex for lexical analysis and Bison for parsing, allowing users to define engines declaratively. Here’s a simplified example of how you might specify a flat-plane crank V8:
import "engine_sim.mr"
public node ferrari_v8 {
alias output __out: engine;
engine engine(
name: "Ferrari F154",
starter_torque: 150 * units.lb_ft,
redline: 8000 * units.rpm
)
crankshaft c0(
throw: 1.50 * units.inch,
flywheel_mass: 15 * units.lb,
rods: rod_journals
)
rod_journal rj0(angle: 0.0 * units.deg)
rod_journal rj1(angle: 90.0 * units.deg)
rod_journal rj2(angle: 180.0 * units.deg)
rod_journal rj3(angle: 270.0 * units.deg)
cylinder_bank b0(angle: 90.0 * units.deg)
cylinder_bank b1(angle: -90.0 * units.deg)
}
This declarative syntax gets parsed into an internal representation that the physics engine consumes. Notice the flat-plane crank geometry: rod journals at 90-degree intervals, fundamentally different from an American V8’s cross-plane crank (which would use 0-90-270-180 degree offsets). This geometry alone produces the distinctive high-pitched scream of Ferrari engines versus the burble of a Corvette.
The physics simulation runs in a tight loop, advancing time in small steps (typically sub-millisecond) and calculating forces on every component. Each cylinder models intake, compression, combustion, and exhaust strokes. The combustion model isn’t scientifically rigorous—it uses simplified equations prioritizing computational efficiency and audio character over predictive accuracy. When a cylinder fires, the simulation calculates expansion force on the piston, translates that through the connecting rod to the crankshaft (accounting for rod angle and inertia), and updates crankshaft angular velocity.
The audio synthesis is where things get fascinating. The simulation tracks vibrations at key points: crankshaft torsional oscillations, exhaust pressure pulses, intake runner harmonics. These mechanical vibrations get converted to audio samples using convolution. The exhaust system acts as a resonator—a straight pipe emphasizes high frequencies, while a chambered muffler dampens them. The code applies digital filters to simulate this:
void ExhaustSystem::process(float *buffer, int samples) {
for (int i = 0; i < samples; ++i) {
// Get exhaust pressure from all cylinders
float pressure = 0.0f;
for (auto &cylinder : cylinders) {
if (cylinder.isExhausting()) {
pressure += cylinder.getExhaustPressure();
}
}
// Apply exhaust resonance filter
float filtered = m_filter.process(pressure);
// Add intake noise (venturi effect)
filtered += m_intakeNoise * 0.1f;
buffer[i] = filtered;
}
}
The output buffer feeds directly into SDL2’s audio callback, generating sound in real time. The simulation can’t afford to drop frames—audio stuttering is immediately noticeable—so the physics loop dynamically adjusts time steps to maintain consistent audio output even on slower hardware.
One clever optimization: the simulation uses fixed-point arithmetic for certain calculations to reduce floating-point error accumulation over long sessions. Engine speeds involve massive numbers (8000 RPM means 133 revolutions per second), and small errors compound quickly. The crankshaft angle calculation uses a high-precision fixed-point representation that resets periodically to avoid drift.
The real-time dyno feature deserves mention. While the engine runs, the simulator calculates instantaneous torque and power output, displaying curves that update live. This isn’t just eye candy—it reveals how engine configuration affects power delivery. Changing cam timing or exhaust length produces immediate, audible differences and corresponding dyno changes. You can hold the engine at a specific RPM and listen to its steady-state harmonics, something impossible with sample-based systems.
Gotcha
The GitHub repository you’ll find is explicitly deprecated. Yaghi built this as a demonstration and moved on, leaving the community to fork it into the “Community Edition.” This means documentation is scattered, build instructions go stale, and you’ll need to hunt through Issues and Discord servers for solutions. The original codebase is rough—expect to encounter poorly commented sections, magic numbers, and subsystems that were “good enough” for a demo but frustrating for extension.
The build process is Windows-centric and dependency-heavy. You need Visual Studio, SDL2, Boost, Flex, and Bison properly configured. Linux and macOS users have managed to compile it through Herculean effort, but there’s no official support. Expect to spend a day getting it building before you write a single line of code. The README is candid about this: the project prioritizes audio authenticity over portability or scientific accuracy. It’s explicitly not a tool for actual engine design—the thermodynamic models are simplified, ignoring factors like knock, detonation timing variance, and realistic fuel chemistry. Using this to predict horsepower for a real engine build would be misguided.
Verdict
Use if: you’re building procedural audio for a racing game and want engines that sound correct across all RPM ranges without gigabytes of samples; you’re an automotive enthusiast who wants to understand why a flat-six sounds different from an inline-six; you’re creating YouTube content about engines and need accurate audio for visualizations; you want to learn how physics simulations translate to real-time audio synthesis. Skip if: you need cross-platform support out of the box; you want a polished application rather than a codebase to hack on; you’re looking for engineering-grade simulation for actual engine design work; you’re uncomfortable navigating C++ build systems and community forks. For active development, seek out the Community Edition fork rather than the original repository.