Reference
API reference
Everything exported from src/index.js and build/index.js. Units are metres, seconds and radians unless noted; preset angles are degrees. The types are in build/index.d.ts.
import {
RiverSystem, River, Lake, Waterfall, RiverPath, Floaters,
PRESETS, getPresetParams, QUALITY_LEVELS,
} from 'naturegl-river';#RiverSystem
RiverSystem.create(options): Promise<RiverSystem>static asyncBuilds the system: the render target with its depth texture, the overlay scene, the optional sky dome (added to options.scene) and the water map. new RiverSystem(options) does the same synchronously.
| Option | Type | Default | |
|---|---|---|---|
renderer | THREE.WebGLRenderer | — | Required. WebGL2 |
scene | THREE.Scene | — | Required. Your world; only the sky dome is added to it |
camera | THREE.PerspectiveCamera | — | Required |
quality | 'low', 'medium', 'high', 'ultra' | 'high' | See Quality levels |
preset | string or preset object | 'clear' | A complete preset; see Presets |
builtInSky | boolean | true | Add the analytic sky dome. It is removed while setSky() is active |
manageFog | boolean | true | Create and update a FogExp2 on scene from the preset |
manageExposure | boolean | true | Set renderer.toneMappingExposure from the preset |
manageToneMapping | boolean | true | Switch NoToneMapping to ACESFilmicToneMapping |
It throws if renderer, scene or camera is missing.
#Frame
water.update(dt): voidmethodCall once per frame, before render(). It advances time (dt in seconds, clamped to 0 – 0.1), eases the displayed values toward params, rebuilds meshes after authoring or quality changes, re-renders the water map when bodies change, applies lighting, fog and exposure, picks the nearest obstacles and moves the floaters.
water.render(output?): voidmethodDraws the whole frame: scene into the HalfFloat render target with a DepthTexture, then the overlay (composite of that target, tone mapped with depth restored, then lakes, rivers, waterfall sheets, mist and spray). Use it instead of renderer.render(scene, camera). Pass a WebGLRenderTarget as output to draw somewhere other than the screen, for example for your own post-processing.
water.resize(): voidmethodMatches the internal target to renderer.getDrawingBufferSize() × the tier's rtScale. Call it after renderer.setSize() or setPixelRatio(). render() also checks the size on every frame.
water.dispose(): voidmethodFrees every GPU resource, removes the sky dome and the library's meshes, and clears the floaters.
#Authoring
water.addRiver(options): RivermethodAdds a ribbon along a spline. The mesh is built on the next update() or render().
| Option | Default | |
|---|---|---|
path | — | Vector3[], at least 2 points. Centripetal Catmull-Rom. y is the water level |
width / widths | 6 | Full width in metres, constant or one per path point |
depth / depths | 1.5 | Depth at the centre, constant or per point. Used by carveTerrain |
flowSpeed | 1.5 | Centre-line surface speed at the mean width (m/s) |
conserveFlow | true | Speed ∝ 1 / width (constant discharge) |
margin | 0.4 | Ribbon extension past each bank, as a fraction of the half width |
extend | 0.3 × mean half width | Metres past the path ends, number or [start, end] |
fade | 'auto' | [in, out] metres of alpha fade. 'auto' fades ends inside a lake |
bankSlope | 0.32 | Carve: bank rise per metre |
bankWidth | 1.2 | Carve: blend width beyond the bank, in half widths |
endBlend | mean half width | Carve: how fast the channel fades out past the path ends (m) |
normalScale, foamScale | 1, 1 | Per-body multipliers |
name | river-<id> | Mesh name |
See Rivers and lakes.
water.addLake(options): LakemethodAdds calm, flat water inside a polygon or a circle. Give polygon, or center and radius; otherwise it throws.
| Option | Default | |
|---|---|---|
polygon | — | Vector2[] (x, z) or Vector3[] (y ignored), at least 3 points |
center, radius | — | A circle instead |
level | center.y, or 0 | Water level |
depth | 2.5 | Basin depth |
shelf | radius / 2, or 5 for polygons | Distance from the shore to full depth |
drift | Vector2(0.03, 0.05) | Slow surface drift (m/s); floaters feel it too |
margin | 1.5 | Mesh extension past the shore (m) |
bankSlope, bankWidth | 0.3, 6 | Carve |
calm | 1 | Suppresses the standing-wave detail, 0 – 1 |
normalScale, foamScale | 0.55, 0.6 | |
name | lake-<id> |
water.addWaterfall(options): WaterfallmethodAdds a cascade from a lip to a pool.
| Option | Default | |
|---|---|---|
top | — | Centre of the lip; y is the upstream level |
bottom | — | Where the curtain meets the pool; y is the pool level |
width | — | Curtain width |
flow | 1 | Streak speed, spray power, plunge foam |
direction | bottom − top in xz | Vector2 fall direction, for near-vertical drops |
plungeDepth | min(2.2, 0.6 + 0.3 × drop) | Carve depth of the pool |
plungeRadius | 0.55 × width | Carve radius and foam radius |
clipUpstream | true | Discard water at top.y beyond the lip line |
spray, mist | true, true | Particles |
name | waterfall-<id> |
Up to 4 waterfalls feed lip clips, plunge foam, ripple rings and splash zones. See Waterfalls.
water.addObstacle(position, radius, strength = 1): ObstaclemethodAdds a foam ring, bow pillow and downstream wake. position is a Vector3 or { x, z }, and radius is measured at the waterline. Returns { position, radius, strength }; edit it in place to move the obstacle. Each frame the nearest maxObstacles to the camera are used.
water.removeObstacle(obstacle): voidmethodRemoves an obstacle returned by addObstacle().
water.remove(body): voidmethodRemoves a River, Lake or Waterfall and disposes its geometry.
water.carveTerrain(heightFn): (x, z) => numbermethodReturns heightFn with every current body's bed, basin and plunge pool blended in, and low banks raised to the water. Where bodies overlap the lowest result wins. It's a snapshot: call it again after adding or removing bodies. See Terrain and materials.
water.patchMaterial(material): materialmethodInjects the waterline wet band, splash zones, underwater tint, sun attenuation, caustics and caustic reflections into a MeshStandardMaterial or MeshPhysicalMaterial. Lambert and Phong get the colour and caustic parts. Chains an existing onBeforeCompile; patching the same material twice does nothing. Returns the material.
#Queries
water.sample(x, z): WaterSample | nullmethodThe highest water surface at (x, z): { height, flow: Vector2, speed, edge, inside, body }. flow and speed include the global water.flow multiplier. null where there is no water. See Flow, rocks and floaters.
water.sampleHeight(x, z): number | nullmethodThe water level, or null.
water.sampleFlow(x, z, target?): Vector2 | nullmethodThe surface velocity in m/s, copied into target (a new Vector2 if omitted), or null.
water.isInWater(x, z): booleanmethodtrue inside a river's banks or a lake's shore.
water.isUnderwater(camera?): booleanmethodtrue when the camera (default: the system's) is below a water surface. Nothing is rendered differently.
water.getFlowMultiplier(): numbermethodThe eased water.flow value.
#Sky and lights
water.setSky(sky | null): voidmethodUses an external sky. Every field is optional and read by reference each frame: sun: { direction, color?, intensity? }, envMap (equirect), envIntensity, cloudShadow: { texture, matrix }, fogColor, skyColor. While set, the built-in dome is removed and bound lights aren't driven. null restores the built-in sky. See Sky and lights.
water.bindLights(directional, hemisphere?, distance = 120): voidmethodLets the preset drive a DirectionalLight (colour, intensity, and position at target.position + sunDirection × distance) and a HemisphereLight. Either can be null. Only active with the built-in sky.
#Presets and quality
water.loadPreset(preset, { instant? }): voidmethodReplaces params with a preset name or a complete preset object. Lighting eases over about 1.5 s and water over about 0.3 s; instant: true snaps. Throws on an unknown name.
water.getParams(): RiverPresetmethodA deep copy of the current settings, in preset format.
water.setParam(path, value): voidmethodSets one value by dotted path, for example setParam('water.clarity', 1.4) or setParam('light.sunColor', '#ffd9a0').
water.setQualityLevel(level): voidmethodSwitches tier: recompiles the water shaders and patched materials, rebuilds meshes, resizes the water map, and recreates the render target when MSAA changes. Unknown levels and the current level are ignored.
#Properties
| Property | |
|---|---|
params | The settings you set, in preset format. Live: edit it directly |
lighting | Current { sunDirection, sunColor, sunIntensity, hemiSky, hemiGround, hemiIntensity, fogColor, fogDensity, exposure } |
floaters | The Floaters instance |
qualityLevel, quality | The tier name and a copy of its settings |
rivers, lakes, waterfalls, obstacles | The authored bodies, read-only by convention |
time | Seconds accumulated by update() |
externalSky | The object passed to setSky(), or null |
renderer, scene, camera | As passed to create() |
#Floaters
water.floaters.add(obj, options?): objmethodFloats anything with a position (and optionally a rotation) from its current xz.
| Option | Default | |
|---|---|---|
radius | 0.3 | |
drag | 0.9 | Fraction of the surface velocity it reaches |
bob | 0.012 | Bob amplitude (m) |
draft | 0 | Height offset from the surface (m) |
spin | 0.4 | Yaw rate while drifting |
align | false | Yaw along the velocity instead of spinning |
onExit | — | (obj) => void, called when it leaves the water |
water.floaters.remove(obj): voidmethodStops floating obj.
water.floaters.clear(): voidmethodRemoves every floater.
#Bodies
River, Lake and Waterfall are returned by the add* calls. You rarely construct them yourself.
| Member | |
|---|---|
type | 'river', 'lake' or 'waterfall' |
name, id | |
mesh | River and lake surface mesh (in the overlay scene), null until built |
group | Waterfall sheet and particle group |
sample(x, z) | This body's own sample, without the global flow multiplier: { height, flowX, flowZ, speed, inside, edge } or null |
carve(x, z) | [profileHeight, weight] or null |
River.path | Its RiverPath |
Lake.signedDistance(x, z) | Distance to the shore, negative inside |
Waterfall.plunge, .drop, .direction | The landing point, the height of the drop and the fall direction |
#RiverPath
new RiverPath(points, options?)classA centripetal Catmull-Rom centre line, resampled by arc length every 0.5 m, with per-sample level, half width, depth and tangent, and a uniform grid for fast nearest-point queries. options takes width, widths, depth, depths, and step (resample spacing, default 0.5 m).
path.query(x, z, out?): PathSample | nullmethodThe nearest point on the centre line: { s, lateral, dist, overshoot, level, halfWidth, depth, tx, tz, slope }. s is metres along the path, lateral the signed distance across, and overshoot the distance past either end.
Also: length, meanHalfWidth, maxHalfWidth, count and the sample arrays x, y, z, tx, tz, s, hw, depth.
#Presets and quality exports
PRESETS: Record<string, RiverPreset>constEvery built-in preset by name: clear, morning, golden, overcast, glacial, muddy, swamp, dusk, night.
getPresetParams(name): RiverPresetfunctionA deep clone of a built-in preset. Throws on an unknown name.
QUALITY_LEVELS: Record<QualityLevel, QualitySettings>const{ rtScale, samples, ssrSteps, normalLayers, maxObstacles, ribbonStep, crossSegments, spray, mist, waterMap, causticLayers } per tier. See Quality levels.