Sampling an engine gives you a perfect recording of one engine, at one rpm, at one throttle opening, in one gear, on one day. Everything a driver actually does happens between those points. This is how you generate the sound instead — the signal chain, the parts that are harder than they look, and what fits inside an audio callback on a phone.
ON THIS PAGE
The standard game-audio approach records an engine at a series of steady rpm steps under load and off load, then cross-fades between the nearest loops while pitch-shifting each to the current rpm. It works, it is cheap at runtime, and it has three failure modes that everyone recognises without being able to name:
A synthesizer has none of these problems because it has no recordings to interpolate between. It has a model, and the model is evaluated at whatever state the engine is actually in.
Nearly every procedural engine synthesizer, whatever the implementation details, is the same four stages:
The rpm feeding stage one is not a slider. It comes from a small driveline simulation — a torque curve, rotating inertia, the current gear ratio, drag and rolling resistance — integrated each block. That is what makes the sound respond to load rather than to your thumb, and it is why the engine bogs when you dump the clutch at 1,200 rpm.
Keep a running crank angle in degrees, wrapped at 720 for a four-stroke:
theta += 360 × (rpm / 60) × dt then wrap into [0, 720)
Each cylinder owns a fire angle. When the accumulator steps past a cylinder’s angle in this block, that cylinder fires. Everything discussed in the other articles on this site enters the model right here, as data rather than as code: a crossplane V8 is a table of fire angles that are unevenly distributed per bank; a Subaru flat-four is a table that puts two same-bank cylinders 180° apart and then waits 540°. Change the table, get a different engine — no new assets.
Misfire and overrun fall out of the same place. On a closed throttle, mark some proportion of scheduled events as failing to ignite; the unburned charge that reaches the hot exhaust becomes a delayed, noisier event instead of a clean pulse. That is the crackle, and it emerges from the model rather than being a sample triggered on lift-off.
The loud part of a firing event is the blowdown: the exhaust valve cracks open while cylinder pressure is still high, and gas leaves at sonic velocity. Acoustically that is a sharp rise followed by a decay lasting a few milliseconds, plus broadband turbulence noise.
A workable excitation is an exponentially decaying burst whose amplitude scales with peak cylinder pressure (so it tracks throttle and load), with a noise component mixed in proportional to flow rate. The decay time should scale with the exhaust valve open duration in crank degrees, which means it shortens in real time as rpm rises — a detail that matters, because it is part of why an engine sounds harder as it revs rather than merely higher.
This stage is where an engine stops sounding like a machine gun and starts sounding like a car.
A pipe open at one end and driven at the other is a quarter-wave resonator: it has modes at odd multiples of c/4L, where L is the length and c is the speed of sound in the gas. Note that c in hot exhaust gas is roughly 500–600 m/s rather than the 343 m/s of room air, which is why exhaust tuning shifts as the system warms up.
The cheap and standard way to implement this is a digital waveguide: a delay line carrying the wave down the pipe, a reflection at the open end with an inverted, low-pass-filtered coefficient (the end radiates the high frequencies away and sends the lows back), and a second delay line carrying the reflection home. One delay line per runner, meeting at a scattering junction that represents the collector.
Everything interesting comes free once this exists. Runners of different lengths give different delays, so the Subaru rumble appears without being modelled explicitly. Lengthening the pipe lowers the resonances. Removing the muffler removes a filter stage and the engine gets louder and harsher. A turbocharger becomes an energy sink and a low-pass in the exhaust path, and turbocharged engines come out sounding flat, as they should.
Engine Sim is this class of synthesizer, running live on the device: per-cylinder ignition, misfire, uneven firing intervals between banks and exhaust resonance, all computed rather than played back. Twenty-one engines, and an engine designer if you want to build the firing table yourself.
An engine modelled only as firing pulses through a pipe sounds synthetic, and the reason is that a real engine bay produces a lot of noise that has nothing to do with combustion:
These are cheap to generate (filtered noise, a few oscillators) and they contribute a disproportionate amount of realism, mostly because their absence is conspicuous.
Aliasing. The excitation pulse is nearly a click, so it is rich in harmonics far above the Nyquist limit. Generate it naively and everything above 24 kHz folds back down into the audible band as inharmonic garbage that moves the wrong way as the engine revs. This is the single most common reason an engine synth sounds cheap. The fixes are the usual ones: band-limited pulse generation, or oversampling the excitation stage and decimating with a proper filter. It gets worse the higher the engine revs, so a 19,000 rpm V10 is the stress case.
Sub-sample event timing. At 48 kHz a sample lasts about 21 microseconds. A V8 at 7,000 rpm fires every 2.1 milliseconds, and firing events essentially never land on a sample boundary. Round each event to the nearest sample and you introduce a timing jitter of up to half a sample, which is not audible at idle and is very audible at high rpm — it reads as roughness or a dirty edge to the note. Events have to be scheduled at fractional sample positions, with the excitation interpolated accordingly.
The real-time contract. The audio callback runs on a high-priority thread with a hard deadline — a 256-frame buffer at 48 kHz is 5.3 ms, and missing it produces a click that no amount of DSP quality compensates for. Inside that callback there can be no allocation, no locks, no file I/O, and no unbounded work. Parameters arriving from the UI or the physics thread must be passed lock-free and then smoothed, or every throttle change becomes a zipper noise. And on a phone all of it has to fit in a power budget that the user experiences as battery life and case temperature.