Animated Ocean Waves

I have been trying to create different kinds of animated ocean waves. Here is one example.

The basic element is a plane divided into grids. The program uses shader extensions to add routines to vertically displace the points (vertices) within the plane and to color the waves based on their height. The vertical displacement for each vertex is computed using the sum of several sine waves of different amplitudes and directions. This incorporates some shortcuts:

  • Instead of using a Gerstner computation to make the waves more “pointy”, I simply reduced the number of segments in the plane to 15.
  • Rather than use a huge number of sine waves and segments to create a turbulent surface, I used a normal map to create a rough surface. (Although you can probably find a better normal map .)
  • For fun, I am also scrolling the normal map at about 1/2 the speed of the waves.
  • To simulate “foam”, waves above a certain height are colored a brighter color. This doesn’t really look that great, but is left in for reference.

This particular collection of sine waves has created waves which seem to morph. Feel free to experiment with different combinations.

The planes can be tiled seamlessly (this example has 4 adjacent planes). I have created programs with almost 100 tiled planes and the frame rate remains at 60 fps.

I want to thank prisoner849 for all his help on working out the mechanics.


Please help me to see it…
Thank You for the attention

SyntaxError: expected expression, got ‘<’

1 Like

Alright, I am baffled. I assume that you are using an iPhone or iPad?

The Pen and your link to the Pen work fine on my PC. But I can’t get the Pen or a Webpage with this Pen to work on my iPhone. As you can see from the Pen, I have now added my standard head meta to the HTML section and have added my iPhone commands to the CSS. I have also tried to turn the Pen into an html on my website that works with iPhone. But no luck on that - although fixing that should just be a matter of time.

The link to the version on my website is here.

Any thoughts?

Just Firefox in Win 8.1…
And could not open in Your website link, with Firefox and Chrome.

I have discovered that CodePen does not work well with IOS - possibly because of thing like CodePen using iframes. You can see the code, but not the examples. Supposedly if I pay extra, I can make the project viewable in DeBug mode which does not use iframes.

I will let you know when I get my website version working. I have created many three.js examples which work fine with IOS, including this version which includes a different wave pattern, the same normal map and also diffuse textures. (I think the glow from the normal map looks like foam from certain angles.)

Let me know if you have trouble accessing my website. You should be able to view all of my three.js demos on my Aviation Webpage.

Thank You for the attention! :slightly_smiling_face:

The link above is now working. I replaced OrbitControls with my own viewer. You can use the touchscreen to orbit the waves. And, although this has never worked for me in the past, you can use the pinch gesture to move in and out.

For some reason, my iPhone will not reload the website to show changes I have made. I made the display brighter and put limits on camera movement up and down. Perhaps it wants to fit with the dark mood of the Halloween season.

OK… now I can see it and I liked …
Thank You again for the dedication! :wink:


The basic example uses a sum of random sine waves to create the wave height. There is a primary wave and several secondary waves which modify the same of the primary wave.
Because you are using waves that begin and end at 0 along the X and Z axes of the plane, you can tile grids together. For reference, here is the original version posted on my website.

One of the challenges with tiling is to avoid something that looks like it is tiled together. This generally means that there shouldn’t be repeating values, within your field of view. In our example, can add variety to your grids by creating additional textures and reversing the X or Z values of secondary waves within each texture. You can then create a group of grids that have slightly different height values. To make the edges align, the grids need to be stacked correctly. For example, grids where you have reversed the X multiplier have to be stacked in the Z direction and vice versa. Here is an example that shows what is happening. Not that all the edges still align perfectly.

Of course, you don’t want to do this with your primary wave. That would look strange. But you can do this with your secondary waves to create variation. Here is a modified version where the secondary waves are flipped. While this may not be the best illustration, if you look closely, you will see that there is greater variation in the waves. (You might see this better if you open the original version and the modified version in separate browser tabs and flip between the two.)

Creating this modified version required me to create 4 textures with different shader extensions. But the extensions are very small and should not take up much room. Also, you can use a single routine if you put your variables in arrays.

This 2X2 group of grids, along with variations to the normal maps should help avoid the dreaded appearance of tiling and add some variety to your waves. (You can also create matching normal maps by flipping X and Y values. However, you will have to convert the original normal map back to a bump map, then flip the X and Y values of that bump map to create 3 additional bump maps and then convert each of those additional bump maps to normal maps.)

You could also create a 3X3 group by setting the X or Z values to zero for the middle grids. However, this does not seem like a great solution because the center grid will lose a lot of modifiers because both the X and Z values will be zero.

I have discovered that recent versions of three.js modules do not work with Apple platforms because of changes to the importmap. So I revised the above to refer to an older version of three.js (v136) which uses an older version of the importmap that is compatible.

The odd thing is that I revised to program to prevent you from dropping below the horizon. This fixed version shows up on my PC, but the iphone sill shows the older version where you can drop below the horizon. I have deleted my iphone web cache and tabs. But it still loads the older non-existent version.

Thank You once again, for more information.

PS.: Observing that I only work with Windows OS.

And I just realized that the reason the limiter which prevented you from going below the waves was not being picked up by the iPhone was that I made the change only to mouse input and not to the touchscreen input :roll_eyes: I have now fixed that.
So thanks to your questions, I am now back to making programs that are compatible with IOS - at least for now.

Here is a little Pirate diorama, created using the method shown in the CodePen example abpve. The ship is pitched and rolled using a sine wave function that agrees with the sine wave function for the primary (pitch) and secondary (roll) waves.

From a design standpoint, this approach to creating waves may be limited to games that have a “cartoon-like” style. That style works fine for most 3D games. Even if you want to create an ultra-realistic game, you can use a cartoon-like style for your initial demo versions.

If you want to create more realistic looking waves using vertex displacement, you probably need to switch to a program that uses a multi-stage fast fourier transformation (FFT). (You can also use ray-tracing, but the waves do not look as nice at low viewing angles.) One drawback with the FFT method is that it requires a lot of segments (around 512) in a single relatively small plane. So you also need to also create a lower-resolution version for more distant planes while avoiding the appearance of tiling (such as by using cascading perlin textures).

Since I am looking for an approach that is in-between those two, I will continue to experiment with variations.

(Note that the vertex displacement method used here can also be used to create terrain. I will be exploring those possibilities later. Ideally, I will be able to create a version that uses heightmaps to create the terrain.)

A few “crewmen” have been added.
The pirate flag is now animated using the same sine function used to animate the waves and rock the boat. I should be able to use the same approach to add some animation to the sails - e.g. making them billow more when running with the wind.


Hi @phil_crowther , have you seen this example? It uses the threejs ocean example and adds workable waves - Three.js TypeScript Tutorials by Sean Bradley :

Edit. just noticed it uses gersntner method. So ignore if not interested, haha

1 Like

Yes, in another thread, I discussed that method with Sean. Essentially he modifies the three.js Ocean to include actual waves. (The three.js Ocean uses “trickery” to make you think you are seeing waves. But if you look at where the water intersects with the floating cubes, the water is flat.)

He provided several helpful examples, all of which include boxes floating on the waves:

  1. Basic modification of ocean which (like ocean) uses three.js Sky.
  2. Variation of the above which uses standard skybox.
  3. Variation of the above which has fog.
  4. Variation of the above which adds motion to the example.

Those are all very useful examples, especially useful if you are creating something where you are on the water. The standard three.js Ocean is great for static displays on calm water. Sean has provided an option where the water is rougher and there are visible waves. You can use that method on some fairly large displays - the standard three.js Ocean is projected on a plane measuring 10 km by 10k km (about 6 miles square).

My challenge is that I am trying to create something much bigger (about 100 miles square). While you can theoretically “tile” the above, there is a huge performance hit. The version that I am showcasing can be easily tiled without affecting performance. And it does not need to be as great looking as Seans’s version. But I am hoping to come up with a version that looks better and which does not look tiled when you are up in the air a few kilometers.

1 Like

I updated my last variation, where the water is centred under the moving helicopter.
Land on a square

The water now reaches roughly the boundary of the camera far.
And it doesn’t appear to be repeating.
Instead of using a planeGeometry with thousands of vertices to be displaced,

I use something similar to a circleGeometry instead.
This can be much larger since the detail is concentrated in the middle, which is closer to the camera.

So, this results in a much larger water, with many less vertices than using 1 or more plane geometry’s that would cover the same area.
And I don’t have the problem of visible edges.


Thanks! It does look good and have a good frame rate. I will it this a try

If you are interested, I came up with the following guidelines for waves based on standard information for wind-driven deep-water waves:

Amp Speed Number
(m) (mps) (n/km)
7.50 14.84 5.00
4.25 11.92 7.00
2.00 8.90 13.00
0.75 5.94 30.00

The amplitude is the distance from sea level to wave top (1/2 the height which is wave top to wave bottom). Note that bigger waves move faster and there are not many per km.

These parameters are probably most helpful if you are trying to create a “wave field” of waves of a certain height coming from a certain direction.

Okay, I have inserted this version into a simplified simulation. I hope that you are able to view it here.

As you indicated, there is no tiling and the edge extends to the horizon, even at high altitudes. What is the size of the geometry in units (meters)?

However, it does not seem to like me speeding along above the waves. The faster and lower I go, the more it bounces and bucks. I assume that the program is having a hard time coordinating large offsets with wave motion. A solution might be to increase the wavelengths for larger waves per the above table. I can give that a try.

Your scene hierarchy is different than mine, so this works better

To change the circle size, use this value.

phiSegemets = 1024 gives circle size,

phiSegments = 512 gives circle size

512 is actually good enough for my use case for lower altitude.

To reduce the flapping of the ocean, you try to adjust

I didn’t manage to get a perfect result.
My demo works better most likely since the helicopter is going much slower than your aircraft.

I think there isn’t a one solution for every use case. You may need to experiment with how the circle geometry distributes the vertices.
If I slow your plane right down, and bring it close to the ocean, the water reacts better to me.

My solution is probably buggy, I don’t really know, since I only delve deep enough into it so it looks good enough for my use case.

To make it easier to understand where the vertices are, you can use wireframe

Below screen grab is using

const thetaSegments = 256;
const phiSegments = 1024;


Those changes helped quite a bit. Also, as suspected, changing the waves also helped. Reducing the steepness of the waves down to 0.1 and the wavelength down to 10 seemed to eliminate all jitters. I will update the program with these changes so you can see.

Those minimizations may seem self-defeating because it is minimizing the visible waves. However, I think your switch to a circular geometry alone has vastly increased the usefulness of the basic ocean program and is worth considering as a permanent improvement.

Regarding waves, I have found that non-reflective waves are not particularly visible from the air - not event relatively high waves with a visible texture on them. So what I have been considering is creating “pop up” waves. This is not a wavefield with rows of waves, but a variation where waves randomly pop up when several small waves from several directions meet at the same time. These kinds of waves are fairly common and create noticeable shadow and/or reflections depending on the direction of the sun. Adding foam would be a nice touch, but I have not figured out how to do that. I could use something like a large transparent texture, but I think that might have an impact on frame rates. Any thoughts on this?