# How do I simulate a doppler effect in a game?

I am trying to simulate the doppler effect in a game (a car racing game). I am not using a specific sound library that simulate the effect, I only have a callback function where I mix the data.

I already figured out how to change frequency of a sample in the mixer function.

What I dont know is how much the frequency should change depending player and emitter position and velocity.

Here is what i have in the game :

//player
vec3 p.pos;
vec3 p.vel;

//emitter
vec3 e.pos;
vec3 e.vel;


1) According to wikipedia, the relation between emitted frequency and observed frequency is given by :

float f = (c + vr) / (c + vs) * fo


where c is a constant, the velocity in medium (typically a big number) vs and vr are source and receiver velocities relative to medium.

so i guess :

float vr = p.vel.length; //player speed
float vs = e.vel.length; //emitter speed


but i think its wrong, it wont produce any change in frequency, for eg: if vr = 0 (player dont move) and emitter have constant speed, then vr and vs wont change (while they should).

maybe should i calculate velocity of player relatively to velocity of emitter ?

like this :

relative_speed = distance(p.pos + p.vel, e.pos + e.vel) -
distance(p.pos, e.pos);


then how vr and vs should be feed ?

2) wikipedia also give another formula to simulate the effect of a vehicle that vehicle passes by the observer :

vr = vs * cos(theta);

//theta is angle between observer and emitter
//theta = atan2(e.pos.y-p.pos.y, e.pos.x-p.pos.x); ?


however, this formula suppose the receiver dont move, which is not the case here. if player and emitter move at same speed (or small difference), there should be no doppler effect. this function is also specific to one case, i suppose the final formula should be the same no mater the situation.

EDIT: i'm trying to find correct formula, using SkimFlux post :

vr,r = vr.vel * cos(shortest_angle_between ( vr.vel , vs.pos - vr.pos));
vs,r = vs.vel * cos(shortest_angle_between ( vs.vel , vr.pos - vs.pos));

//is there a easier/faster way to find them out ?
//note: vr.vel and vs.vel are vectors, the green and red arrows on SkimFlux picture.


EDIT2 :

For those interested, here is final formula :

vec2 dist = vs.pos - vr.pos;

vr,r = dotproduct(vr.vel, dist) / length(dist)
vs,r = dotproduct(vs.vel, dist) / length(dist)


NOTE : it use vector projection, described here :

then vr,s and vs,r should be injected in first wikipedia formula :

I tested it and it works sucessfully, providing great results.

• You can adapt the formula that assumes the receiver isn't moving by replacing the actual movement of the source with its movement relative to the receiver. – yoozer8 Feb 9 '12 at 14:35
• – bobobobo Sep 10 '13 at 13:27

1) Assumes that both objects are moving on the same line - (this is explained in the wikipedia page you linked) your conclusion is correct, in this situation, with constant velocities, the frequency shift is constant. For the frequency shift to change, the relative velocities need to change, hence formula 2), for the situation where Vs is constant but not colinear with the S-R axis.
Formula 2) is misleading however: Vr should be read as Vs,r, that is, the radial/relative component of the source velocity.
Edit: this should help you figure out the velocities, you need to use the Vs,r and Vr,r quantities with formula 1: