Screen-Space Water Ripples with World-Space Player Movement in Three.js

TL;DR:
Extended Mirko Kunze’s CheapWater to support player movement while keeping ripples in sync with world positions. Ripples are positioned in world-space and rendered to a separate normal scene, which the water shader samples.

Live Demo on CodePen →


The Original CheapWater

CheapWater is brilliant for its simplicity and performance:

  • Ripples render straight into screen space.

  • Water shader samples using gl_FragCoord / resolution.

  • Extremely efficient, looks great as long as the camera doesn’t move in world space.

CheapWater keeps ripples aligned correctly with the world as long as the normal map and main scene are rendered with the same camera, even if the camera moves or rotates.


The Adaptation

I integrated CheapWater with a moving player and camera setup suitable for games:

  • World-Space Ripple Placement – Ripples remain anchored in world coordinates.

  • Separate Normal Scene – Rendered to a texture that the water shader samples.

  • Player / Camera Movement – Main camera moves freely and follows the player.

  • Ripple texture behaves like a top-down snapshot of world-space ripples.

Result: Efficient, screen-sized ripple textures that remain anchored to world positions, fully compatible with a moving player camera.

Full implementation available here: Screen-Space Water Ripples with World-Space Player Movement in Three.js


Performance Considerations

This approach uses two render passes like the original:

  1. Render ripple normal map to off-screen texture.

  2. Render main scene using ripple texture.

Why it’s fast:

  • Simple geometry: just quads with basic materials.

  • No lighting: additive/subtractive blending only.

  • Small texture: screen resolution, not world-scale.

  • Minimal overhead: typically <0.1ms even with hundreds of ripples.


Demo Features

  • WASD / Arrow key movement on XZ plane.

  • Camera follows player smoothly with OrbitControls.

  • Ripples generated by player movement and falling drops.

  • Animated demo ball for visual comparison.

  • Real-time performance stats.


Future Improvement

  • Use instancing for improved performance.

Discussion

Has anyone else tackled screen-space to world-space ripple mapping in Three.js or similar engines? Would love to hear alternative approaches or performance tips.


Demo: Screen-Space Water Ripples with World-Space Player Movement in Three.js
License: MIT

Hope this helps someone out there!

9 Likes

Very cool.

Hi red-reddington!
I appreciate the credits! :slight_smile:
Can you elaborate on the sliding problem? You can also move the camera in the original demo with the right mouse button, I don’t see any sliding issues…

There shouldn’t be any desynchronization going on, did you make sure that you render both the normal scene and the color scene with the same camera?

renderer.setRenderTarget(normalTarget)
renderer.render(normalScene, camera)

renderer.setRenderTarget(null)
renderer.render(colorScene, camera)

@mqnc , you’re right; your demo works perfectly because both the normal and color passes use the same camera each frame.

I’ve clarified this in my post. My contribution was integrating the ripple system with a moving player camera, suitable for game-building.

Thanks again for the excellent technique!

Excuse my ignorance.. but is there a worldspace heightmap for the ripples and then the normals are computed in screen space each frame?

Like.. you could use 2 layers of “this thing” to render both a shore with dynamic footprints in the sand, and on top, the water layer with ripples that are also generated by the feet/ankles as they walk?

And the main difference between the 2 would be that the sand footsteps heightmap doesn’t propagate/fade out, and remains relatively static, whereas the water layer does ?

… And for that matter you could potentially combine it into one heightmap, with a “water deepness” map controlling the ratio between propagation/fade and static behavior? Or just based on some function of worldspace geometry height… ?

2 Likes

@manthrax that is, actually, a great idea. the technique lends itself to a variety of usecases. Sand, mud, snow, … might have to experiment with this further. Thanks for the suggestion!

2 Likes