Animated Ocean Waves


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?

Do you know how to make the color of the ocean more navy blue?

I have tried changing the water color, but that does not seem to have much effect. I suspect that the reflection of the sky color is over-riding the color of the ocean.

That is one of the characteristics of the ocean. If you look out horizontally from a lower altitude, you see more of a reflection of the sky color. But as your altitude increases, you are looking more downwards and the natural color of the water becomes more dominant. This transition is subtle because both the ocean and the sky are blue.

It would be interesting to see what would happen if we were able to dynamically change the reflectiveness of the ocean based on viewing angle or altitude. We would not want to eliminate the reflectiveness entirely because with this method defines the waves using reflections - eliminating all reflections would cause the waves to disappear.

The fragment shader is where the colours are decided.
And fragment shaders, especially the water example, are not something I understand very well.
But the sky colour is reflected in the fragment shader.
The variable is mirrorSampler
So, you could make your sky darker as your altitude increases.

1 Like

Yes, that is one improvement I wish that they would implement - providing us with more documentation about how their “add ons” work. Not just from a user perspective, but from a programmer perspective.

Here are three variations to my Pirate Ship demo:

  • Standard Ocean - Uses MeshStandardMaterial. This is the standard version with both a normal and perlin map which create height.
  • Cartoon Ocean - .Uses MeshToonMaterial. For some reason, I like this one the best. Instead of height, the normal map creates colors. And there is no reflection of the sun.
  • Mirror Ocean - A mirror-like surface that reflects the sky and has all kinds of colors. If pushed, this would probably be the first to drop below 60 fps.
  • Gerstner Ocean - A version of Ocean modified by Sean Wasere to include Gerstner Waves (the original Ocean looks like it has waves, but is flat). I have not yet added his subroutine which should float the boat correctly.

The ship now has a waving flag, created using the same principles as the waves. I will add animated sails. (This should end up as a sine wave demo.)

While these look okay here, they do not work so well with the flight simulation due to the tiling effect which is mitigated somewhat by increasing the grid size. Also, the visual impact of using physical waves is mostly lost at atititude.

1 Like

Looks cool :+1: , but my brain refuses to believe that waves move in one direction and the flag waves in another one (btw, about a waving flag: Three.js + noisejs, can be done with 2d noise in shaders ) :slight_smile:

Do you have a favorite?

Ship motion can be deceiving. My interpretation is that the waves (or, more correctly, ocean swells) are not moving towards you, but that you are sailing with them and overtaking them. I moved the normal map to try to convey this motion through the swells.

Regarding noise, I wanted to generate a noise that would have the same value for each xz position so that the routine for computing normals works correctly. Does the 2d noise do that? (Also, I would want a perlin noise generator which would create smooth changes.)

I like the gerstner version. Its progressing really well.
The reflection is really good that the normals look correct.

But, there is something wrong with the green on the bow, and the red on the stern. Port and starboard on boats is the same as it is on aeroplanes. :slight_smile:

I’m thinking about replacing them with a “skeleton crew” :skull:

1 Like