More
Changelog
Every release of naturegl-water. The package follows semantic versioning.
#1.1.0
2026-09-24 These fixes came out of the NatureGL Island integration.
#Fixed
setSky(skySystem)threw a TypeError with NatureGL Sky, whoseambientis{ color, skyColor, groundColor, intensity }with nosky. The ambient is now read from a Color,ambient.sky,ambient.colororskyColor × intensity, falling back to the horizon × 0.6.water.setSky(sky)with a realSkySystemworks as documented.- External-sky moonlight was a hard-coded constant, about 25× NatureGL Sky's moon. With a sky whose
moonhascolorandintensity, the night key light ismoon.color × moon.intensity × moonIntensityScale(× the optionalmoonTint), faded in below the horizon. The constant remains the fallback. - Shore-wave foam rings over wide shelves. Offshore trains keep their own k·a, reach
shoreSteepnessonly as they break, and fold into the whitecap Jacobian only past a breaking index of about 0.6. Trains are limited to water shallower than about 2–4× the breaker depth. On a flat shelf they are depth-limited bores (H < 0.44 d). Swash foam is limited to about 6–18 m from the waterline. Breaker foam starts at a breaking index of 0.62 (was 0.75). Read more. - Bull's-eye rings round small islets.
Bathymetrybakes a shore-train weight from the area of the nearest land mass (shoreTrainMinRadius, default 40 m), and a slow phase wobble breaks up perfect contour crests. bindLights()with an external sky used the built-in sky's hemisphere ground colour. It now uses the externalambient.groundorgroundColor × intensity.
#Added
- Param
shoreSteepness(default 0.92, eased). BathymetryDesc.shoreTrainMinRadius,Bathymetry.trainTexture(RG: train weight, breaking allowance),sample().trainand.breaking, and the uniformtShoreWinWAVES_GLSL.SetSkyOptions.moonIntensityScale,.moonTintand.syncExposure(default true:render()tone-maps withsky.displayExposure × params.exposurewhen the sky provides it, as NatureGL Sky 1.1 does), and the read-onlywater.exposure.water.renderHDR()→{ texture, depthTexture }: world, water and overlay in linear HDR with world and water depth, for external post chains such as NatureGL Sky'sSkyPostPass.composite().render()is unchanged. Read more.BuiltinSky.bindLights(…, { groundColor }).- Examples:
examples/with-sky/(a realSkySystemwith day, night and the sky post pass, built when the siblingnaturegl-sky/build/exists) andexamples/lagoon/(a shore-wave stress test).
#Changed
- Shore waves look calmer offshore on every coast. Raise
shoreWavesorshoreFoamif you relied on the old offshore foam.
#1.0.0
2026-09-24 The first packaged release, ported from the single-file index.html ("WATER FREE" ocean and Dive reef).
#Library
- The
WaterSystemfacade:create,update,render,resize,loadPreset,setParams,getParams,setQualityLevel,setSky,bindLights,setBathymetry,sampleHeight,sampleNormal,isUnderwater,addWake,dispose,profiling/gpuTimingsandstats. - A GPU FFT ocean (Tessendorf): JONSWAP directional spectrum, 2–3 rotated non-harmonic cascades in one Float32 atlas (2·log2 N Stockham passes), mip-mapped HalfFloat outputs, and an asynchronous read-back of the swell cascade for CPU sampling and buoyancy. The default wave source on high and ultra.
- A JONSWAP-sampled Gerstner spectrum (24–56 waves) on a camera-centred polar grid for low, medium and the fallback. Long waves' normals and Jacobian are summed per vertex, the short band per pixel, and sub-pixel waves become LEAN roughness.
- The depth pipeline: a HalfFloat world target with a DepthTexture (MSAA on high and ultra), Beer–Lambert thickness, in-scatter, refraction with forward-scattering blur, screen-space reflections, GGX glitter with a clamped peak, crest SSS, bloom and ACES.
- A baked bathymetry (EDT shore distance and direction): shoaling, breakers (Green's law, breaking index), swash foam, and depth-buffer contact foam.
- A persistent camera-centred foam field, with mip-mapped foam lace and micro-normal textures (Toksvig).
- Buoyancy (single-probe spring and tilt, multi-probe least-squares plane), wakes (contact foam, Kelvin V, persistent trails) and a caustics material patch.
- Underwater: fog and absorption, volumetric light shafts, Snell's window with the refracted scene, a TIR mirror, and a near-plane waterline split with a meniscus.
BuiltinSky(the ported analytic sky), theSkyLikecomposition contract withsetSky(), and the overlay layer for effects drawn after the water.- 7 presets (Tropical, Sunset, Dusk, Storm, Foggy, Moonlit, Arctic), one file each, and 4 quality tiers.
#Demo
- The index.html island (terrain, basalt headland, palms with wind sway, jungle), buoys, Kenney Pirate Kit ships, dock, wreck and props (extracted to
.glbfiles), and the reef planted on the island shelf. Dive now goes under the same ocean. - The original UI (presets, Wind / Wave / Time / Cloud, Dive), plus quality buttons, Copy preset JSON and the
window.__procontract.
#Tooling
npm test, a headless real-GPU smoke test.scripts/shot.mjsandscripts/perf.mjsdev helpers. Examples: basic (Vite), CDN (import map) and external sky.
#Performance
Apple M5, headless Chromium with ANGLE/Metal, 1280×720, full demo scene, uncapped: the original page ~200 fps, medium ~220, high ~140 (120 with vsync), ultra ~107. See Quality and performance.