Finding the height of any point on a 2d plane perturbed by a sine wave

AKA: Making water waves

I wanted to make a special effect for some software that I’m writing. It involved simulating waves in water inside the vertex and fragment shader in an OpenGL program.

While I found a lot of posts on the subject, none of them worked from the ground up to explain what was going on with the math (mostly trig) in the calculations. Since I work a lot better when I can see examples visually, I wanted to share my thoughts while I was going about the process of understanding what I was reading in my head.

There wasn’t a whole lot as far as code because much of it is hidden behind vector operations like the dot product, but really digging down I found the methods interesting nonetheless. I hope my brain dump can make other people excited about some of the stuff behind 3d.


There are many ways to look at a sine wave. Most people know it as the uppity downity regular pattern associated with waves of all kinds. I like to look at it depicted as a continuous revolution around a circle being drawn by a marker.

Sine wave amplitude being drawn from the Y component of a rotation around a circle.

One sine wave is completed each time we go all the way around the circle. This means that as we input between 0 and 2*PI radians into the sin() function, we complete one whole wave. Once we go over 2*PI radians, the function starts over. Another way of saying this is that the domain of sine is between -1 and 1: No matter what goes in, something between those numbers comes out.

Lets say we want to create one of these waves “traveling” in some two dimensional direction given a change in time. We then want to render that traveling wave on a computer screen. We also want to be able to control the frequency of the wave. How many peaks and troughs we’ll see given a single span of 1 unit.

As shown above, we know to complete a full wave we need to input 2pi to the sin function. Therefore, we should multiply our desired frequency by 2pi.

float frequency = 2*pi * desiredFrequency;

You can think of this mutiplication as creating a situation where we pass in the same X value of 2, but more waves have been generated by that point.

Screen Shot 2015-03-06 at 6.34.41 PM

Multiplying by frequency

Since the sine wave is going in a certain direction, the last thing we need to know to draw a point on our plane is how much the position of the point is contributing to amount traveled along the wave.

We can figure this out by obtaining the dot product between the direction the sine wave is traveling, and the location of the point which is itself a vector rooted at the origin.

Screen Shot 2015-03-06 at 6.43.41 PM


Remember that the dot product tells us the cosine of the angle between point vector from the origin and the wave direction. Cosine according to SOH CAH TOA is the ratio of the adjacent side of the triangle vs the hypotenuse. This is exactly the ratio we need to determine how much of the magnitude of the point contributes itself in the direction of the wave.

Putting this together in code, we get:

float theta = dot(waveDirection, vec2(x, y));

Theta is then how much x, y point/vector has moved us along the wave.

Now, finally, we can calculate the height of the wave at the given X,Y point.

//inputs are:
//vec2 waveDirection: The direction the wave is traveling
//float freq: The number of complete waves per unit distance
//float amplitudeMul: The amplitude multiplier for this wave
//float time: input time to create wave movement
//float x: X coordinate of the point we're drawing
//float y: Y coordinate of the point we're drawing

float wave(vec2 waveDirection, float freq, float amplitudeMul, 
    float time, float x, float y) {

    float frequency = 2*pi * freq;
    float theta = dot(waveDirection, vec2(x, y));
    return amplitudeMul * sin(theta * frequency + time);

Happy waves!