Realistic Oceans Using Mesh Deformation!

Yeah I don’t think the rendering system touches it.

This is absolutely wonderful, but i’ve been having a few issues with it, it’s probably just me being bad at scripting, but is there a way to have 2 different waves alternate when a event fires, like, in my game have I have a script that fires an event wich triggers a bunch of scripts and starts a storm. when that event is fired, I wanted the waves to become bigger and faster, but I cant seem to get it to work. Thanks in advance

For this, you can use the Wave:UpdateSettings(waveSettings) method (which I actually forgot to document, my bad). You provide a new waveSettings as the only parameter and it will update the wave with the settings. In your case you would just update wave height and maybe frequency. You would update all wave’s wave settings. Hope I helped!


oh yeah, thanks ill go and try it right away.

you may want to add that to the documentation aswell.


How did you get the bones to be all parented to the plane?

The bones from my mesh are like this and I don’t know how to parent them all to the plane without glitching out the mesh.
Nvm I solved this.

Blender isn’t totally my thing, so I’m not sure if I would be of much help.

Check out this tutorial here and if you still have the problem, try asking it there.

1 Like

Question, very late

Have you gotten around to make this work completely or not? I´m looking for a solution to bone collission within oceans/seas which are animated

That will be very difficult to do, as Roblox has not added support for collision detection with skinned meshes.

Collision detection isn’t necessarily needed, it can be calculated without it

Theres multiple videos on it just look up “gerstner waves roblox”

Take the object you’d want as a sort of “boyant/boyancy block”

Great tutorial imo, thanks a ton!

1 Like

I did some research thru APIs and the ¨Skinned MeshParts are live!¨ post and came across this API:

¨ TransformedWorldCFrame¨
Describes the combined CFrame offset of the bone and the current animation offset in world space.

This could help with collision, possibly

Another way I´ve seen before is create triangles on the gerstner waves, which follow the same wave path to detect collision way easier (but more performance impact probably)

Along with that I came across this example:

This is probably the closest we can get to collision detection for skinned meshes, for now.

That’s some good research right there. However, unless there is an official implementation, I don’t plan on updating the module with collisions. I don’t believe it will exactly be performant in real situations, but it’s always worth a shot!

1 Like

Is it possible to detect if a player/camera is inside the water?

1 Like

Is there any way to condense your expansive and useful module into a single function that takes a vector 3 and returns where that vector 3 should be at that returned time?

-- call a function and pass the origin point before the translation (and perhaps the settings?)
-- expected return is the vector3 post-translation 
x, y, z

This has to be possible and simple (but math and coding aren’t my areas of expertise)
^ Any arbitrary bone can be translated by the gerstner function you have, as long as it exists in the workspace, therefore any arbitrary point should have its own translation. This is obvious, I think.

I tried a couple methods and all failed, however, the method that made the most headway was:
Putting all necessary functions in a serverscript for the individual floating part
Taking the part’s position as the vector3 to be translated
Placing the translated vector3 as the bodyposition’s position

The one problem I faced was that I could not correctly isolate and carry over your perlin noise function.

Yeah sure I can add that real quick

1 Like

Lemme know if it works. Download the updated module:


-- Added per request of @Desired_Knowledge
function Wave:GerstnerPoint(point: Vector3)
	local WorldPos = point
	local Settings = self._settings
	local Direction = Settings.Direction

	if Direction == EmptyVector2 then
		-- Use Perlin Noise
		Direction =,WorldPos.Z/NoiseModifier,1),math_noise(WorldPos.X/NoiseModifier,WorldPos.Z/NoiseModifier,0))
		Direction = GetDirection(Settings,WorldPos)
	local transform = newCFrame(Gerstner(WorldPos,Settings.WaveLength,Direction,Settings.Steepness,Settings.Gravity,self._time))
	return transform, point+transform

Just realized I never hit reply lol

1 Like

It works properly, but its not synced with the waves. It doesn’t seem to be the same wave at all.

Now, I did make edits to the module to have the plane anchor to the HumanoidRootPart and keep the points where they should be, had the plane never moved. I move the plane, and subtract the humanoidrootpart’s x/z position from the worldposition of the bones. In this way, the water is basically infinite. There must be some issue in the way I did that though.

I’m trying to have an infinite ocean that uses only 1 plane and also has waves that are pretty synced from client to server to clients. I asked about the point method so I could add swimming and floating physics, as well as a check for if your camera is under water.

If that’s an edit you can walk me through, or make altogether (as long as it isn’t asking too much), that would be extremely appreciated and helpful. I can commission you, otherwise.

The problem with Gerstner waves is that there is no mathematically correct way to get the waveheight at a given xz-position.

The best you can do is use approximations. What I found by researching the problem is that you can achieve a pretty good result by plugging the wave formula in twice. As in you first calculate one offset of the position, then pass that new position in the formula again to extract the height.

How would I do that using these above methods?

Something like this:

function getHeight(XZPos)
	local w = computeDisplacement(XZPos)
	local correctedXZPos = + w.X, XZPos.Y + w.Z)
	return computeDisplacement(correctedXZPos).Y

computeDisplacement() is a function that takes in an XZ-coordinate and returns the offset on that point (Vector3).