WebGPU · three.js · React Three Fiber

threejs-water: Water from the beach to orbit.

An ocean that is one draw call at every altitude, rivers that flow downhill, and lakes that fill to their spill level. Drop it on a threejs-biomes world, flat or planet, or use it on its own for a plain sea.

screenshot · water.html open ↗
Waves rolling onto a sandy coast at a river mouth
1draw call for the sea
16Gerstner waves, max
~330rings, beach to horizon
3kinds of water
Getting started

Fill the sea with one call.

Give it your camera, scene, renderer and world. The ocean takes its sea level and planet radius from the world, the rivers stream in around the camera, and both update themselves whenever the scene renders.

three.jsthreejs-water
import { World } from 'threejs-biomes'
import { Ocean, Rivers } from 'threejs-water'

const world = new World({ seed: 'coast' })

const ocean = await Ocean.create({
  camera, scene, renderer, // WebGPURenderer
  world, sun,
  waves: { amplitude: 0.8, direction: [0, 1] },
  whitecaps: 0.6,
})
const rivers = await Rivers.create({ camera, scene, renderer, world, sun, maxDistance: 2000 })

// Buoyancy: depth below the waves and the surface normal.
const { depth, normal } = ocean.waterAt(boat.position.x, boat.position.y, boat.position.z)
React Three Fiberthreejs-water/react
import { Water } from 'threejs-water/react'

function Coast({ world, sun }) {
  return (
    <Water
      world={world}
      sun={sun}
      ocean={{ waves: { amplitude: 0.8 }, deepColor: '#06283a' }}
      rivers={{ maxDistance: 2000, flowSpeed: 1.4 }}
    />
  )
}
Install npm i threejs-water threejs-biomes three Full API reference → React components →
What's in the box

Every kind of water.

A sea that covers everything below sea level, rivers along the valleys, and lakes where the ground holds water. Every feature below is live in the demo.

01

One ocean, every altitude

From an ant on the beach to orbit.

The sea is a camera-centred grid of concentric rings: dense under the camera, sparse at the horizon, rescaled with the camera's height. On a planet it is bent onto the sea sphere in the vertex shader and clamped at the horizon, and wave phases are computed in float64, so the water stays smooth millions of metres from the origin. One mesh, one draw call, at every altitude.

Waves on a beach at a river mouthon the beach
A 120 km planet and its ocean seen from orbitfrom orbit
02

Gerstner waves

A wind sea you can tune.

Up to 16 Gerstner waves summed around a wind direction, with tunable amplitude, wavelength range, spread, choppiness and speed. Per-pixel wave normals and drifting ripples add detail, and fade out before they alias.

  • amplitude
  • wavelength
  • direction
  • spread
  • choppiness
  • speed
Tall, choppy waves with whitecaps
03

Light through water

Shallow to deep, clear to dark.

Depth-based absorption fades from shallowColor to deepColor, the bottom refracts through the surface, and Fresnel reflections pick up the sky or an environment map. A GGX glint follows the sun.

Clear shallow water over sand turning blue offshore
04

Foam

Shorelines, whitecaps and rapids.

Foam gathers where the water gets shallow at the shore, on the sharpest wave crests (whitecaps), and in river rapids where the flow gets fast. One foam setting scales all three.

White foam along the shoreline
05

Rivers that flow

Streamed along the world's rivers.

River surfaces are built in camera-centred tiles under a per-frame time budget. Flow follows the slope of the surface, so flow-mapped ripples and rapids foam move downstream, faster where it is steep.

A river winding through green land to the sea
06

Real drainage

Lakes at their spill level, rivers by catchment.

computeHydrology fills depressions (Priority-Flood+ε), routes flow downhill (D8), accumulates catchment area, and returns rivers that widen downstream and lakes at their spill level. riverModifiers carves the rivers into the world and Lakes draws them.

Carved rivers and lakes from the bounded hydrology
07

Queries for gameplay

Buoyancy, swimming and splashes.

ocean.waterAt(x, y, z) returns the wavy surface, depth and normal under a point. waterAt(world, x, z) says whether it is sea or river, with level, depth and flow velocity. Pure functions, no GPU readback.

The demo with its control panel and live water readoutthree.js demo

Go jump in.

Fly from the beach to orbit in the live demos, or go straight to the API reference.