This simulation elegantly distills complex wave mechanics into clean, low-level logic, proving that C remains the most transparent medium for physical visualization. It is a refreshing return to first principles that bridges the gap between abstract physics and tangible code.
Deep Dive
Prerequisite Knowledge
- No data available.
Where to go next
- No data available.
Deep Dive
Coding a Doppler Visualizer in CAdded:
Hi everybody. For today's episode, I thought I had a nice idea that is simulating the Doppler effect. For those of you who don't know what the Doppler effect is, let me just pull up an image.
It's the effect that frequency that audio frequency shifts for an object that moves away from you or towards you. So when you for example hear an emergency vehicle coming towards you, the siren has a higher pitched sound than when it's moving away from you. And that is because the sound waves emitted by the vehicle by the moving vehicle. They are compressed when the vehicle moves toward you and they are expanded when the vehicle moves away from you. So that's a quite interesting physical effect. I really find the Doppler effect um quite intuitive to understand because if the vehicle doesn't move then the the waves they expand in all they they travel in all directions equally.
But when it is moving then you see the sound waves emitted in the first position here they still are emitted as usual but then the future waves that are emitted at later points in time they are emitted at new positions at new origin positions and therefore this shifting frequency pattern here um is occurring It's quite a nice effect. I would like to simulate all of that in Rayip in C.
So, we're going to have an animation that visualizes this and um maybe even we're going to create a a vehicle that we can move around on the screen ourselves that visualizes the Doppler effect.
Anyways, that's the plan for today's episode. I haven't prepared this video, so no idea how long it's going to take.
No idea how we're going what the result is going to look like that we're going to end up with. I would say let's create a new directory Doppler Doppler simulation or simulator Doppler simulator and in it let's create a source file doppler C and we're definitely not going to need senate io I don't know for what we're going to need rail for actually we don't even need to do much mathematics because we're just going to draw circles that traverse that expand from an origin point. That's already it. So it might be a simple project. I don't know exactly. So let's initialize the window. And before we do that, let's pull up the rail header file where we can look up things. First thing that we have to do is always initial with a width and a height and a title.
So, let's keep it simple. Width, height, and title Doppler simulator.
There we go.
And width and height. I'm going to define them here. Define width 900 and define height 600.
Then we need a loop that keeps the window open. So while the window should not close, we are doing something. We draw and we set a target frame rate. set target FPS here of 60 frames per second. Okay. And I think now if we do a clear background here, clear background black, then what we get should be a black window that opens on the screen. And then yeah, we already have a window that the project that we can use to draw our animation, our simulation in so that uh what was the directory called that I'm in here? PWD, it's called Doppler simulator, right? So let's CD into that directory down here. CD Doppler simulator and compile this GCC output file is called Doppler and input file is called Doppler. C then the then we need to link Railip using -L ray lip and I will enable all warnings and extra warnings just to be sure that I'm not messing up things. And now I already get warnings.
So unused parameter RXC and RCV. Yes.
Right. We probably don't need the arc C and ARCV. They were just coming from the main method template that I used in Vim.
And now that's it. Okay. So now we have a main method that's simply opening a window. Let's com confirm that and run the executable. And here we go. We have a black window. Perfect. Hitting escape, we can close the window. And let's draw something. We want to draw a a vehicle, but let's keep it simple. It can be just a circle. Okay, the we definitely need to be able to draw circles in this episode. So, let's look for draw circle or circle.
Yeah, draw a circle and we can draw a circle at a given x position or x ycoordinate with a given radius of a given color. Then there are a couple of other circle related functions such as draw circle sector, draw circle sector lines, draw a circle gradient, draw circle V, which is essentially the same thing as draw a circle. It just takes as input a vector here.
So let's do draw circle V.
And yeah, I would say after the clear background function call, let's draw a circle.
We don't even need to draw circle V.
Let's keep it simple. So we draw a circle at coordinate 400 400. So X is 400, Y is 400. And the color is simply white. The colors, there are a couple of predefined colors in Ray. Just look for color here.
And there are some basic colors that you can use here. They are all predefined. I have just used this white here.
And now we should have a circle. I forgot the radius. Let's make the radius 50. And now we should have after compiling a circle that is white.
Perfect.
Okay. And that is going to be our vehicle that moves on the screen. Um actually what should we start with? Should we start visualizing the waves first or should we make the vehicle move first? I would say let's start let's start with the waves.
So from this vehicle here which is a simple circle we want waves to be emitted and waves are in that sense just like in this example here just more circles but they are circles they just the outlines of circles so probably we need to find a function for that and I guess there is one in Rayip it's draw circle circle.
[sighs] Draw circle sector lines. No, that's not it. Draw circle sector is not it either. Draw circle gradient. Draw a gradient fil circle. That's also quite nice.
But I would say draw circle lines is probably the one that we want. So let's try and see if this does the job. So we draw circle lines at the same coordinates as the car.
Let's create variables here. x= y = 400. I think this should work in in C such that I can pass that same variable to the circle and the car and then the radius of the circle is 100. So it's way bigger than the car itself. Now that yields a warning. Y is undeclared. Oh yeah. Oh, great. I think I can't do this, right? That that doesn't work. No, that doesn't work.
Okay, so let's do it cleanly. int x = 400 and int y = 400. And now this compiles. If we execute it, we have a bigger circle now. Ah, of course, because I called draw circle twice. Of course, we want to draw circle lines for the second call. So, let's compile this and execute. And now we have a circle with a line around it. This already looks quite nice.
Okay.
And now I think we can abstract the drawing of a car in a function. Let's create a function here. draw car at a coordinate int x or maybe even float x because we want to smoothly move the car in the future. So, it's better to deal with with floating point numbers and just move. We're just going to move the draw circle call into draw car here like this. And let's replace draw circle with draw car at X and Y. And let's make let's also make X and Y floating point numbers.
Then in Okay, wait. So we draw a car. This is just drawing the car. Then we draw.
Oh, now comes an interesting part because we are going to emit multiple lines from a car and we can't just do we can't just manually add more and more circles like this. Well, we can do it, but of course this is not extensible, right?
We somehow need to store the waves that are being emitted, the sound waves. We need to store them somehow because when the car moves, we still want the old sound waves to keep growing and growing and growing and expanding until they are too big to show on the screen.
So I would say let's properly formalize all of this. We first need a car strct car and all the car consists of is just an x and a y coordinate right because a car in our sense in our case is just a stupid it's just a point on the screen in this sense and this is a strct car right and then we want a strct sound wave wave and a soundwave really let's I don't know if whether to camel case this or lower case uh this looks more programming like so the soundwave also consists of an x and y coordinate and this is the origin point from which it originated but it also has a radius and technically in real physics it has a speed at which it's at which it propagates but it's fixed for all sound waves. So it's it can be stored in a global variable.
So the properties that are unique to every single soundwave are just the origin x and y and the radius at a given time.
Right? Right? And X and Y they are always fixed per soundwave and the radius grows over time per soundwave.
And that should be it. We can put that in type in a type def such that we don't have to type strruct manually over and over. But actually no, let's keep it like this. Okay, I think it's fine.
Now we draw a car at X and Y positions.
Let's improve that a bit by drawing the single car that we have. And the sing since we just have a single car that is the global it's it's globally just a single car that we will have on the screen. Let's put that on in a global variable. So the global variable is called strruct car car and then in our main method we're going to need to initialize the car init.
And here um in the initialize method.
Oh god for somehow for initialized methods I always use this lower case. I know the the underscore character. I think this looks nicer.
Maybe I should call draw car also draw underscore car. I somehow prefer prefer that when dealing with C. This way we can also differentiate between the rail lip methods which all use this camel casing where the first letter is also uppercase from the from my own methods here.
So we initialize a car. It's quite simple. How do we initialize a car? Uh void. Ah, no, we don't even need to put that in a dedicated function. We can just do car equals and then we position the car at the center of the screen. So, at width divided by two and height divided by two.
And if we compile this, of course, it doesn't work because uh I definitely forgot something.
I definitely interrupted myself in this go in the course of the investigation already and forgot what I was doing. So y is undeclared in line 19 and ah it's probably here right we draw a car and it's and we draw it at car dox and car doy and we don't draw a car we draw the car because we don't we only have a single car and then there's still a stale function call to the uppercased uh draw car here. Let's replace that with draw car. Remove the arguments. And now I think it might be good. No, not yet.
There's still an error in line 26, which is here.
And probably probably that's because I need to cast it to strct car otherwise the compiler doesn't know what the I'm what the object is that I'm referring to. Now it compiles and if I do Doppler then I render then we render a car at the center of the screen which is in our case just the circle and the sound waves. The sound waves are of course now misplaced.
Okay. So how do we do it now? Hm.
A car emits sound waves, right?
[sighs] And the sound waves, they are not properties of the car itself. They are properties of of the entire world we're dealing with.
So we can store all sound waves that have been emitted in an array here.
Let's call it waves. And that's an array of size I don't know we can do it very very big.
So number of or max max waves or technically we could also allocate memory dynamically but it's in my opinion it's over complicated then yeah let's just track the maximum number of waves and the current number of waves. So the maximum number of waves and let's make it generously high is is just I don't know 1,000. We'll never vis visualize more than that. And then let's store the current number of waves in an unsigned integer because it's never going to be it's never going to be signed. We never have a negative count of waves. So unsigned inturren waves equals zero and then the car emits waves. Here we draw a car before drawing because it's not a something that needs to happen inside the drawing loop.
Well, of course, we set the coordinate of the car outside of the drawing and outside of the drawing also we emit waves. Emit wave or wemit we emit a single wave or we call it well how do we call it?
There are two things that can happen.
the the car emits a new wave and the old waves that already exist on the screen.
They they expand.
They was a nice word that I used earlier that somehow came to my head, but I already forgot it again. [snorts] They traverse.
They [gasps] They move. Okay. They move. They grow.
Um they propagate. Right. Propagate.
Propagate. waves. So this propagates all previous waves. We don't have that method yet. And we emit new wave.
We emit a single new wave. We are going to propagate waves later. First let's emit a new wave.
So let's create a method here. Emit new wave here.
And how does emitting a new wave look like? This emitting a new wave just means we need to add a new element to the waves array. And we can do that by setting waves at some index equal to a new wave strct sound wave. And actually the Doppler effect, by the way, is not limited to sound waves. It happens also underwater, in fluids, everywhere in in solid materials. even I think well why wouldn't it occur in solid materials as well so it's not restricted to sound waves in that sense so I might also just call this structure wave here um but I mean the the Doppler effect is most visually um or intuitively understandable by using sound waves and let's keep it sound wave then but just to be precise it's not limited to sound waves It's it's a wavepecific phenomenon. It also happens with light by the way. That's how you know that the universe is expanding by the way because when you look at the stars in the sky then most of them almost all of them are redshifted. This means that the the frequency that we are seeing is expanded.
And this way we we know that the stars are moving away from us and only very few stars are blueshifted and that would mean that they are moving toward us because then the frequency is contracted just like in this example here. We are the observers. The source is the star.
It emits light and then the star is moving toward us and we see a higher light frequency.
And because all almost all the stars are moving away from us and almost all celestial objects are moving away from us, it is inferred from that that the universe is expanding.
Yeah. Okay. just just a fun fact for those of you who didn't know yet. I find it very s I find it super interesting actually. Um and let's continue programming. So I'm going to place a single stupid soundwave hardcoded at the center of the screen. Height divided by two. And let's give it a radius of 100.
And then I'm going to remove the draw lines calls down here.
We have emitted a new wave. And in this is just the pro this is just a call to programmatically emit a new wave. And then down here we also visually display all waves. So let's create a function draw waves.
And draw waves is not doing any physics.
It's just going to draw waves. Because right now if we compile this and we fix the errors of course ah right the errors just that I need to define this method first draw waves let's not do anything in here float x float y is unused why ah because the car is right because the car uses those as variables. here. So we don't need to set X and Y anymore and we don't need to use the variables X and Y anymore.
So now we have the car. It is visualized in the in the draw car method. We have also emitted a new wave by just hard coding this um this sample wave here. But it's not visualized yet. So let's draw a wave.
And we draw a wave by looping over all the waves.
Uh where what's the waves count again?
Ah it's ah it was current waves. That's the current waves count. So for all waves that currently exist, we draw a circle draw circle lines here using waves at index i. So we are using the loop to draw all the waves here. dox waves at index i doy waves at index i do r and the color is white and like just like this I think this is properly implemented already so drawing the waves is is stupidly simple because it's just using it's just calling the draw lines method in in ray lip And now there's a warning here. Warning comparison of integer expressions of different signedness int and unsigned int. And maybe this unsigned int here even though it's correct is it's maybe too we don't really need it. It's just an unnecessary thing to look at in the code. We can remove it or we can also make the counter variable here [sighs] an unsigned int. I prefer to keep it simple. So let's just remove the unsigned thing, but be aware that it's of course unsigned in practice.
Okay, now we have still only a car and no wave. Why is that? probably because we have added a wave to the waves array but we haven't incremented current waves which is the current counter and if it's zero then this loop is not going to enter. So now by incrementing it we enter this loop and draw a line. So and now we have a car with a wave around it. That's good.
Now the emitting of a new wave works.
Let's propagate the existing wave.
We can do that by yeah we first have to of course create the function. So void propagate waves and again we just need to use a for loop like in draw waves that iterates over all the existing waves. And we do we just need to access waves at index i, the radius because remember the origin point always stays the same, right? A wave just propagates starting from a center point and then grows. But the center point never shifts over time.
This is not happening. This not a this is not how waves work. [snorts] Um right. So we only need to change the radius over time. How do we do it? We increment the radius by by some speed. So wave wave speed wave speed multiplied by a delta time.
So how much time has spa passed and how fast is a wave? How much time has passed? We are going to pass this into the into the function. And we can find this out by the delta time of the frames. So inside this loop here, since we set a target frames a frame rate per second, we know approximately that a 60th of a second is that's the delta time between each iteration of the loop. So we could assume we so we could assume that a 60th of a second has passed. However, this is not reliable and Ray lip try um keeps track of the real time that has happened between the frames because if if you're on a very very slow machine for example a Raspberry Pi or even something slower than that then it might not even reach 60 frames per second even though it's such a simple software for example 30 frames per second or even only 10 frames per second and then the delta time will have a different value that is not a 60th and also So there's some overhead outside of the drawing that contributes to the delta time not being exactly a 60th even if 60 frames per second is reached.
So we propagate the waves here and we pass a delta time. What is the delta time? We can get it from get frame time which is a function in ray lip. get frame time here that returns the time in seconds for the last frame drawn and returns a float.
Um maybe it's simpler to look at it like this. float dt is get get frame time and then we pass dt into this.
If we compile this, of course it doesn't work because I first need to define the speed of a wave. So let's define wave speed. And a reasonable first value would be one. And then we will probably see that it moves way too slowly.
And then [snorts] we'll increase.
So let's execute.
And there's a small circle expanding in the top left corner. Why? Why top left corner?
So we see a move a a wave moving.
But it's not doing what I wanted to do.
Um where have I created the wave? The wave should be located at x is equal to width divided by two and y is equal to height divided by two. So I would expect it at the center of the screen.
Also I don't I already forgot again where the other line around the car is coming from. Where is this wave coming from? Was I drawing that somewhere?
Let's let's talk let's check all draw circle lines methods.
>> [sighs] >> There's one here. And then that's already it. Wait, what? Did I even compile this?
And where do I draw circles? Draw circle.
I draw one here.
Right. This is the radius of the car.
It's currently set to 50. How about we make it a bit smaller such that it is not too big on the screen. Then we draw circle lines here. And then that's already it. Wait, what? Then why is the wave growing in the top left corner? And where where's this line coming from?
I feel stupid because I know I remember I placed that line somewhere but I already forgot.
So in propagate waves we are looping over the waves. We are increasing the wave speed. Let's make that five just so we see the wave growing faster. There we go.
And this is not even a wave. This is a circle.
I have no idea what's going wrong here.
Ah, I am calling emit new wave in every single iteration of this loop. So maybe that's the issue.
Of course, I only want to emit a new wave. For example, well, actually actually I want to do it only once for now. For for a start, I want to emit one wave and then only keep growing and propagating that single wave and visualize that at first. And now for whatever reason um the logic is fixed and now we see a single wave growing on the screen. It's quite slow, but it's looking nice in my opinion.
Okay. And I would say we can increase the wave speed for example to 15 and then see that wave expanding here.
That's nice. So now we have a traveling soundwave.
And what else is there to do? There's something. Well, I don't know why the wave starts at already such a big radius. Did I do this? Did I implement this myself?
Right. I started the wave at radius 100.
So, it should be radius zero because a a wave is is always originating from a single point. Of course.
And now we have the car and we see a wave being emitted. Ah, that's nice.
And now comes the magic part. We have implemented the basic physics for a single wave.
And then we only need to add more waves that are emitted when the car moves. And then we need to make the car move in the first place. So, how about we make the car move first and only then add the other sound waves because I want to have this sudden surprise effect at the end of this episode where everything just ties together. So, we need to make the car move. Now, I know there is a sample code on ray lip for using arrow keys.
How about we just copy that?
I've discovered it in the episode that I did the last time I think where I had to do something with a key the space key.
Right. Right. And ah exactly. So this is the code we check is the key down. If yes, we increment the position of the car.
And there's some sample here. Yeah.
Okay.
So this is incrementing the position.
It's not incrementing the velocity of the car. It's fine. We actually we don't need any um car physics here, right? So incrementing the position is actually very fine. And we check is the key down, key right, key left, key up, key down.
Let's just copy that. That's fine for me. And let's put it here. Is the key down? If yes, we set car.x like this, right? We we do exactly this.
So we increment the car by 2.0F. It's not necessary in our case. We can increment it like this because it will always stay a float.
I don't know if there is there any benefit by the way to to making a floating point number this explicit by writing 2.0f here because the car the car's y x and y coordinates are already floating point numbers. So I don't know if if there's so much benefit is of course beneficial to make it explicit but I don't know if it's technically necessary or is this just for the programmer?
I don't know. So let's execute the code.
We should see a wave expanding now. Yes, it is. And if I press the arrow keys, the car is moving. Okay, that's nice.
So now we are updating the car's position here and all that's left now is to create or is to rework the emit new waves function here. So the first question for me is when do we want to emit a new wave?
We want to emit we want to emit a new wave.
Well, we don't we certainly don't want to emit a new wave every single frame.
So we can't call this method for every single frame in every single loop iteration. So instead, we could define, for example, that we want to emit a a new wave only every 600th frame. That would be every 10 seconds. That's way too way too long. Every 60th frame. That would work, but that would then depend on the frame rate.
A nicer solution would be to try to emit it every single second.
For that we can create a variable float seconds or let's call it interval. I think I did that in the last episode al also with the with the fractal generator with the silinsky triangle generator that we did where we where we increase the depth of a fractal of a fractal's recursion by um every full second. So there I use this concept for um where I create an interval variable.
Then I emit a new wave. Let's say here.
But I emit it only if the interval incremented by the delta time is still smaller.
I hope this syntax works. Is still smaller than 1 second. Let me just use this explicit floatingoint number notation here. Maybe it's useful here.
And then only then we emit a new wave and we reset the interval. Interval equals zero.
Okay.
And now we should see a new Oh no.
[laughter] So that doesn't work. Why? Why does this not work? Ah, of course, because I don't properly emit a new wave in the in the imit new wave function. I emit new waves.
I I do something stupid here. So, I create new waves in on index zero all the time. How it can properly be done is we set waves at index current waves.
After that we can increment uh and we set it to a new value that is equal to the car dox and the car doy and radius zero. So all waves of course start with radius zero.
Then [sighs] waves and we do that only if current waves is smaller than max waves. So we want to keep a limit here. We can't grow this array beyond bounds. Of course let's copy that in here. Else what happens if the array is full? Then we should remove waves.
Oh.
Oh no. We have to do some memory management but not but it's array memory management. Array based memory management maybe if [sighs] okay. So currently we are appending waves to the end of the array which is fine.
But once the array is full this is not so fine anymore because then where do we put the new waves? We either have to delete waves from the current array or wait.
Or we can just we can simply and stupidly just overwrite waves starting from index zero again.
Does that work?
So we know current waves is now the maximum waves value here because the likelihood that the waves that have already exp um escaped the drawing that it they have already escaped the windows surface and therefore are not visualized anymore is uh very very large. So therefore we can without problem just override them in place and we don't need to manage memory or shift the arrays back and add new waves and at the front. It's all not necessary. We can just override.
So we do if current waves is smaller than max waves or we simply do what about this guys?
If I'm doing something something stupid then just tell me. Um but I think current waves modulo max waves might work.
But then at some point if we increment current waves after every emission of a new wave then at some point current waves will overflow for integers. What's the maximum integer value here? Maximum integer in C. I know it's not fixed across all CPUs, but it should be this many waves.
So, for a long time, we're good.
But it won't it definitely won't work forever. So, I can't run my software and return in 1,000 years and expect it to still be working. So, maybe we have to fix it.
So current waves modulo max waves but I also can't reset it to zero because if I reset it to zero then the drawing waves won't draw the waves again.
So when we have reached max wave, how about when we reach max waves multiplied by two, then we reset to max waves.
What about this? Wait. If current waves equals max waves or is bigger than or and equals bigger than or equals max waves multiplied by by 10. I don't know. I think it doesn't really matter at all what the factor here is.
Then we just set reset current waves to max waves. The important part is just that that current waves is always uh wait the current waves must always be smaller than max waves actually.
But this is not wait no no no this won't work. waves at current waves modulo max waves. We write a new wave here.
If current waves is bigger than or equal to max waves, the defensive way to do it would be of course to to do this to just cap it. Then current waves is equal to max waves is bigger than or equal to max waves then current waves. Um but then if current waves is equal to max waves here then we modulo by max waves.
I don't like the plus+.
It's lowering my confidence that this that the syntax is correct. We definitely should only increment it at the end or maybe after assigning a new wave even though maybe it's the same um it's equivalent syntax but this is a bit clearer to me. So if current waves is bigger than or equal to max waves then we cap to this again and we should test it with a small max waves count to be sure that it works.
Let's make max waves three. And I think now it might be time to test. Well, it compiled without an error. Crazy. This is crazy. So today might be our lucky day. My lucky day. Our lucky day. I don't know.
And let's see how it behaves. Now, I would expect the car to still be able to move, of course. And then it will emit three waves, but as soon as the fourth wave is emitted, the outermost one disappears because we have capped the number of max of we have capped the number of waves.
Okay, so we have one wave. Where's the next one? Oh no. [sighs] Okay, so we are currently only emitting a single wave.
Why are we only emitting a single wave?
If interval incremented by dt is smaller than 1.0f.
So maybe this syntax here already doesn't work. I don't know. Interval is smaller than this. Let's make let's make it explicit. Somehow when I whenever I write code explicitly, then it works.
And when I try to somehow use unlever C syntax, then I mess everything up.
So let's try now wave one. And there's no other wave.
Why?
Interval is incremented by dt.
If interval is smaller than Oh, no. It must be bigger than it must be bigger than 1 second of course to emit a new wave.
And now wave number one. Wave number two. Ah okay. So we saw the capping happen but it's wrong.
Mhm.
And I know this is such a simple it's such a simple um problem problem here. It's it would be so simple to just shift the entire array and add new sound w sound waves at the beginning.
But somehow I have the feeling that this can be elegantly solved.
So current waves is incremented. No if current waves is equal to max waves then it will be modul by max waves and then the result will be zero. I think then we increment it and then it's capped to max waves again. That that's of course wrong because in the next iteration then we will overwrite wave number zero again.
[sighs] Yeah.
So this I don't like this approach but something like this should be possible. Waves current waves max waves.
What if I just do waves at current waves is equal to this? Then I increment the count of waves. If the current waves is bigger than or equal to max waves.
Yeah, come on.
We probably have to shift. That would be the only proper solution.
But but overwriting should be possible.
So that's what's bugging me right now.
Waves at index current waves is equal to this and then current waves is incremented.
This is fine.
So this is emitting new waves but at some point it's going to override be beyond max waves. Then we do current waves modulo max waves and this should still work. Let's just confirm this. But it will stop working according to my understanding once.
Yeah, of course this works. What? What the hell is this? Is the wave in the top left corner?
What's that?
[laughter] What the hell is going on here?
H I feel like it's all due to this due to this overriding here.
>> [laughter] >> Oh my god.
[snorts] Yeah. Okay. I give up. I I will override proper properly.
>> [snorts] >> So waves is equal to current waves mod.
No, we write a new wave into current waves into the slot current waves.
Starting at radius zero, then we increment. For the next iteration, we check is current waves bigger than or equal to max waves.
If yes, we need to shift back the array.
Shift back the array. So we create a new array here.
How do we do it?
Wave. Soundwave. We create a new array here strct soundwave which is a copy array.
So copy also of the same size. So it can accept max waves entries [snorts] and yeah that's already it.
And then we iterate over the current array which is waves.
I know which we go up to current waves up to the current waves count or no we go up to max waves because we know the limit has been has been reached and then we do copy at index I is equal to waves at index i + 1. And this of course only works if i is smaller than max waves minus one. [clears throat] And this copies all waves into the new into the copy array. And then we uh and then we somehow copy back. No, shift back the array.
And maybe we need yet another loop here.
where we do where we just copy let's keep things stupid and simple. So we set waves at index i starting from index i equals 1 here to copy at index i.
So first we copy from waves.
We start.
No, we need to start from waves at index i. And we copy into copy i + 1.
Or no, we just create we just create a copy. We don't need to do any index shifting here. So we just need to create a copy.
And then we copy back the copy into waves but at shifted values. So we set copy at index i plus no waves at index i + 1 equal to copy at index i. And here we have to check is i.
No. If I is smaller than max waves minus one, then we do this. And this should result in a shifted array where the first slot is not yet occupied or it's occupied by some value that we can just overwrite and the rest is shifted by one.
And then we can do wave at current waves.
No, we can just write the new wave into zero then.
[snorts] And actually we can always wait current waves bigger than or equal to max waves. We shift back the array and then we write.
Actually, we need to shift back or we can shift back the array every single time like this.
shift back the array to free up index zero.
And then we write waves at index zero and then we increment current waves.
Do we even need to take to track the current waves then?
I think so. because we still need current waves for the drawing because the drawing needs to be aware of which waves are available and also for the propagate waves which uses current waves.
So if current waves is smaller than max waves then we increment it.
And now maybe it works. Let's check.
Let's compile. Of course, compilation doesn't work because I forgot a semicolon in this line.
We compile. Compilation works. Let's execute this. And I'm still puzzled. Why the hell are we emitting waves in the top left corner?
>> [sighs] >> It seems to have something to do with overriding waves at index zero here. And maybe [sighs] do I need to somehow No, no, no. I I don't I really don't understand it.
This can only be some weird memory behavior.
Because errors in my logic are totally exclud excluded. It's impossible that I made a logic mistake here.
Waves at index zero equal this.
Let's just double check the shifting logic. We iterate over waves and copy it. We can just we can also just create a copy um using memcopy by the way. Mem copy and we copy how many items do we copy? We copy um max waves items. Wait, let's open the documentation of me copy uh man me copy. So we can copy an entire destin an entire memory area here from source to destination where and size t n bytes.
Yeah, let's but it's no benefit at all.
But instead of me copy, well, the manual the manual copying is fine in and of itself, but it might make sense to to overwrite to to zero out using mems set the copy array first before copying into it. This is just def desperate um trying out stuff now. So we use mems set to set C elements each of size N on the array as C elements. So how many elements do we set to zero max waves elements on each of size size of sound wave.
That might do it. And then we need to include string.h string.h because mems set is defined here in this header.
Okay. I there was some compilation error. Let's just check it. Soundwave undeclared, right? It must be size of strct soundwave. Let's compile this. This works. Let's execute it.
And we still have a soundwave in the top left corner.
And it has something to do with this line. I'm sure that this is the case.
Waves at index zero.
waves at index zero. How about we do or what happens if we zero out the entire waves array before we copy back from the copy waves?
Let's just check this.
No, that's not it.
So let's remove that line again. Then where the hell why the hell do waves originate at coordinate zero?
Propagate waves.
In propagate waves, we only change the radius of a wave. We don't change the coordinates.
Do we ever set the coordinates, by the way? Wait.
Yes, we do set the coordinates using car do.x and car doy here.
Car do.x and car doy are floats.
Soundwave x and soundwave.y are floats as well.
This is ridiculous, guys.
And where do we draw the waves? Maybe there's an error.
There we use waves X and waves Y as well.
So what happens with waves at index zero in the next iteration? So here we set a new wave. This is fine.
Then if the current waves count is not yet yet maxed out, we increment it. Else we skip. Then in the next iteration we enter here. We create a copy that we first zero out.
Yes, we zero it out with the what the hell am I doing here? Wait.
The mems set function fills the first n bytes of the memory area pointed to by s with the constant byte c. This should be a constant by c. What was I doing here?
This must be by zero of course. Then how many bytes do we fill? Max ah max wave multiplied by size of sound wave construct sound wave of course.
So this is now properly zero zeroing out the array.
Now still this hasn't fixed the error but this is not too bad.
Um, and now we copy into this zeroed out copy here from the waves I from the waves. We copy every single entry from there.
And we should Oh no. [snorts] And then starting from index zero, of course, we must copy back.
But this doesn't change anything yet.
Oh, it does. Yeah. No, this this worked.
So it was a copying error in the in the copying back from the original from the original um copy from the cop from the copy of the copy of the copy. It was a copying copy copying thing. Okay. So it was this that was the error here. Index I should be zero. Of course, [sighs and gasps] that happens when you mix up different uh when you try to when you try to restructure your code and you forget old things that you have done. So now what we are seeing is a car or some object emitting sound waves. It's now quite small. We can increase the wave speed to let's say 25.
We can now since we have confirmed that that the waves are deleted, right? Because you just saw that only three waves were generated and then the waves deleted themselves.
Now we confirm that or I can confirm that it works. The memory management works. And now let's make let's set a maximum of 1,000 waves such that we always have waves on the screen or maybe 50 50. It would be nice to see a boundary of the waves and the waves should and it would also make sense to define a car speed. And currently, let's make let's make it 1.5F.
Um, just to be a little bit slower because I felt like it was a a bit too too fast right now.
Car speed like this. Like this. Like this.
And now we have, if everything works out guys, a fully capable Doppler effect simulator. So now we have a couple of waves. And if we move the car, you should be able to observe the Doppler effect. Let's have a look. Ah, I know that's way too fast. The car moves way too fast in relation to the waves. And also it should be pos possible to to change the wave emission frequency.
So let's let's set frequency or yeah wave emission frequency here.
Frequency.
And currently it's one wave per second.
How about we do wave emission frequency? Let's make it 4 [snorts] hertz.
In Hertz.
And to be sure it's in hertz, we have to do a division here. One point. We can do it like this.
Okay.
And now I'm very excited to see the result, guys. Oh, there's an error. Ah, of course I can't put a comment like this here. Uh, we're in C. So, let's do this.
Okay.
And now let's see. Now we are emitting a lot of waves. You're probably seeing some aliasing on the screen right now.
And if we move the car, well, the car is way too way too fast. And the waves are also too slow. So, let's make the waves much faster. The frequency can stay the same because we have increased the speed. Then the gap should also be bigger between individual waves. And the car should be way slower. For example, 0.3 such that we really see the compression of the waves.
Now we are emitting waves and I'm moving the car now. And now you should be able to see the Doppler effect, right? You can see it on the screen. Oh, that looks so great. You can see that the waves on the right hand side of the car are compressed while the waves on the left hand side of the car they are expanded.
So that's the shift in frequency that you observe in nature as well. And you can also see the boundary of the waves on the screen right now because we have coded we have set a limit of maximum of the maximum number of waves that we create. And this is now the working Doppler effect. The real Doppler effect in action guys. That's so awesome.
Holy crap. Let's just adapt the wave speed even more such that is maybe even more impressive.
And now the car is a bit too slow. We can also increase the frequency to let's say 6 hertz. Make the car a bit faster.
Let's say 1.0F.
Ah, this I love these results. They are genuinely heart opening for me. I love it. So now here's the Doppler effect in action. I move the car. Yeah, that's perfect, guys. This is so awesome.
Th this is perfect.
And I can even move it downwards like this and upwards like this. And this is a real simulation of the physics of the Doppler effect. That's why you hear high frequencies of when when when sounds are moving or when objects emitting sounds are moving toward you. And that's why you hear lower frequencies when objects are moving away from you. That's such a great project. And by the way, the sonic boom is exactly this this huge pile of sound waves here that you see when the movement of the object emitting sound lines up exactly with the with the wave speed because then you see this huge wall of waves here and this is extremely loud in reality. This is the sonic boom effect.
Oh, that's so awesome. I love it.
Crazy.
This looks crazy.
Let's move outside of the screen for a second and move in again. And here's the rolling sonic boom. Awesome.
Crazy. So guys, if you want to learn C like we do, like we cod it on this channel and apply it all the time, then have a look at the nononsense C programming course. It teaches you everything that I know about C and everything that about how I use C in practice to create real and useful and fun projects.
And it skips all the boring theory and enables you to create real nice projects like this one yourself. [cough] [clears throat] So have a look at it. I highly recommend it and I hope you guys enjoyed this video. If you did, then see you next time. Bye-bye.
It's It's much better than I expected.
Awesome.
Related Videos
Is dark matter real? - Why can't we find it? - physicist explains | Don Lincoln and Lex Fridman
LexClips
1K views•2026-05-30
Nobody Expected This Lava Reaction 🤯 #faits #facts
TendzDora
28K views•2026-05-30
Saptarshi Basu - Spectacular Voyage of Droplets: A Multiscale Journey to Extreme Flow Conditions
DAlembert-SU-CNRS
152 views•2026-06-02
A 6.0 Just Hit Hawaii — And It Came From The Wrong Place
TerraWatchHQ
115 views•2026-06-03
The Split-Second Mistake That Made Bouncing Bettys So Deadly
NoMansLandChannel
253 views•2026-06-02
The Silent Memory of Glass
UnchartedScienceworld
146 views•2026-05-30
The Difference In Charged And Neutral Particles
heavybrainspace
959 views•2026-05-29
A380 vs Every Vehicles Crash Test Challenge | Which One Win?
BeamLap
163 views•2026-05-29











