Phaser AE

Watermax

Free packv3.0.0-53DWorld

A simulated open sea for 3D games on WebGPU. Three wave cascades are evolved on the GPU every frame and shaded from the physics up, so the water refracts what lies beneath it, reflects what stands above it, foams where crests break and carries whatever you float on it.

This is a Free pack. Every phaser.io account can use it in their games.

Released October 14, 2026 · updated October 19, 2026

Watermax: a screenshot of its demo
A note about these graphics

Expansion packs don't come with graphics. This screenshot is one way of drawing what the pack does, made for its demo: none of the sprites, tiles or backgrounds in it are in the pack.

That's by design. A pack is a game's logic, mechanics and effects, and it works with whatever art you give it. Some packs draw effects, backdrops or 3D scenes of their own, but none carries a game's sprites.

So the look is yours. Make the art on phaser.io, bring your own, or let your agent draw it in code: the same pack can look like anything.

The pack

watermax

function watermax(opts: WatermaxOptions = {}): Pack<{ watermax: WatermaxApi }>

An FFT ocean with a physically based surface, foam, wakes, buoyancy, caustics, an ocean floor, spray, rain and an underwater post.

Functions

applyWaterPatch

function applyWaterPatch(target: WaterParams, patch: WaterParamsPatch): void

Merges a patch into live params, one group at a time.

assignCascadeBands

function assignCascadeBands(scales: readonly number[], resolution: number): CascadeBand[]

Assigns band edges to cascades given in input order, coarsest first (the caller orders them; there is no re-sort). Each seam is the coarser cascade's Nyquist over the cross-fade ratio, which maximises the finer grid's mode index at the handoff. When a finer cascade cannot reach its seam the gap is reported with console.warn rather than hidden — the wavenumbers in between are unrepresentable on either grid.

bakeDistortionNoise

function bakeDistortionNoise(size = NOISE_SIZE, period = NOISE_PERIOD): Uint8Array

Bakes the tileable gradient noise the underwater distortion scrolls, as r8 with 0.5 at zero.

bakeSource

function bakeSource(s: WakeSource, dt: number): BakedSource | null

Turn a WakeSource into the capsule the reference generator stamps. The reference sweeps a disc of radius from the previous position to the current one; here the disc becomes the hull's half-width and the segment is extended along the heading by the hull's extra half-length, so the capsule runs from last frame's stern to this frame's bow. A hull no longer than it is wide degenerates to the reference swept disc exactly.

The stamp is -depth * speed * SOURCE_GAIN / radius, so over the footprint-crossing dwell the added displacement totals about depth whatever the speed; a longer hull dwells longer, so the stamp is scaled by radius / (extraHalfLength + radius) to keep that trough depth. Returns null when the hull is stationary or swept further than TELEPORT_THRESHOLD (a teleport, not travel).

  • s - the source.

  • dt - the frame time in seconds the velocity is integrated over.

bakeVoronoi

function bakeVoronoi(size: number, cells: number, seed: number): Uint8Array<ArrayBuffer>

Bakes the caustic lookup: R = nearest-cell distance F1, G = 1 - (F2 - F1) the cell edges. Tiles seamlessly.

buildClipmap

function buildClipmap(levels: number, segments: number, baseSize: number, infinityExtent: number): ClipmapMesh

Builds the geometry clipmap: an inner square, square rings doubling in size, T-junction skirts between them, and a flat infinity ring out to infinityExtent. Vertices lie on regular grids so the mesh snaps to the coarsest spacing without swimming.

buildIWaveKernel

function buildIWaveKernel(P = KERNEL_HALF): IWaveKernel

Build the iWave kernel G(k,l) = [sum_n q_n^2 exp(-sigma q_n^2) J0(q_n r)] / G0 with r = sqrt(k^2 + l^2) and G0 the same sum at r = 0, so G(0,0) = 1. The numerator is computed once per distinct r squared.

Tessendorf recommends P = 6 as the minimum, but that truncates the operator's tail enough to run the longest waves about 60% fast; P = 10 (the default) holds true dispersion across the wake band within a few percent for a 21 by 21 stencil.

  • P - kernel half-size; default KERNEL_HALF.

cloneWaterParams

function cloneWaterParams(p: WaterParams): WaterParams

Deep copy of a preset for live editing.

createWakeSim

function createWakeSim(device: GPUDevice, opts: WakeSimOptions = {}): WakeSim

Create a wake field on device. The kernel is built and factorised once here (a few hundred milliseconds of Bessel quadrature at P = 10); everything else is rebuilt as configure demands.

createWaveSim

function createWaveSim(device: GPUDevice, opts: WaveSimOptions = {}): WaveSim

Creates the simulation on device. Compiles the pipelines and allocates every cascade up front; nothing runs until update. Throws RangeError on invalid options.

crestTransmission

function crestTransmission(c: WaterColorPhysical, pathLength: number): [number, number, number]

Sunlight transmission through a wave crest of pathLength metres, integrated to RGB.

deriveCascadeScales

function deriveCascadeScales(maxScale: number, resolution: number, count: number): number[]

The tile size of cascade index for one shared resolution: see deriveCascadeScale.

foldWaveTime

function foldWaveTime(time: number): number

Folds an absolute time in seconds into [0, WAVE_TIME_PERIOD_SECONDS); negative times wrap the other way.

hexRgb

function hexRgb(css: string): [number, number, number]

Parses a css hex colour into linear-ish 0..1 floats, the way the engine treats colours.

hullPoseFromHeights

function hullPoseFromHeights(bow: number, stern: number, port: number, starboard: number, length: number, width: number, offset = 0): { y: number; pitch: number; roll: number }

The pose a hull takes from the surface heights at its bow, stern, port and starboard samples: y their mean plus offset, pitch nose-down positive (negative when the bow is higher) and roll positive when port is higher, both radians, as world-3d's handles take them.

hullSamples

function hullSamples(at: { x: number; z: number; rot: number }, length: number, width: number): Array<[number, number]>

The four places a hull samples the water, as [x, z] world points: bow, stern, port and starboard, in that order.

inScatterReflectance

function inScatterReflectance(c: WaterColorPhysical): [number, number, number]

Deep-water in-scatter reflectance (Morel), integrated to RGB.

jonswapSpectrum

function jonswapSpectrum(x: number, y: number, p: SpectrumCellParams): { kx: number; ky: number; variance: number }

The banded per-mode variance Ψ(k,θ)·Δk²·window of cell (x, y) — everything the init shader computes before the random draw. Returns the wave vector too. The DC cell is floored to |k| = 1e-4, as the shader does.

separableKernel

function separableKernel(kernel: IWaveKernel, rank = SEPARABLE_RANK): SeparableKernel

Rank-r separable approximation of the kernel. The kernel matrix is symmetric and radial, so it is numerically near-rank-2 (at P = 10 the leading eigenvalues are about 1.97, 0.34, 0.026): the top eigenpairs reproduce the operator to about 1% at rank 2, turning 441 taps per texel into 84. Because the matrix is symmetric each term's row and column factor is the same vector, applied horizontally then vertically.

  • kernel - the dense kernel from buildIWaveKernel.

  • rank - number of terms; default SEPARABLE_RANK.

snapOmega

function snapOmega(omega: number): number

Snaps a natural angular frequency (rad/s) to the nearest multiple of WAVE_TIME_OMEGA_STEP. The perturbation is at most π / period ≈ 3.84e-4 rad/s, under 0.2% for any visible wave; wavelengths are untouched.

transmittanceLUT

function transmittanceLUT(c: WaterColorPhysical, size: number, lengthScale: number): Float32Array

Broadband RGB transmittance over a non-linear path-length axis: entry i is path -lengthScale * ln(1 - i / size).

Constants

COLOR_LUT_LENGTH_SCALE

const COLOR_LUT_LENGTH_SCALE: 20

Path length in metres at which the LUT axis reaches 1 - 1/e.

COLOR_LUT_SIZE

const COLOR_LUT_SIZE: 256

The transmittance LUT size and its length scale, shared with the shader.

CREST_PATH

const CREST_PATH: 3

Crest path length in metres for the transmission colour.

DEFAULT_WAKE_OPTIONS

const DEFAULT_WAKE_OPTIONS: Readonly<Required<WakeSimOptions>>

Every option's default.

DEFAULT_WAVE_OPTIONS

const DEFAULT_WAVE_OPTIONS: ResolvedWaveOptions

The defaults: the sunset reference set with three cascades of 1024, 96 and 9 metres at 256.

STORM

const STORM: WaterParams

A grey storm sea: strong wind, steep chop, heavy whitecaps.

SUNSET

const SUNSET: WaterParams

Golden-hour sea under a low sun: the sunset look.

TROPICAL

const TROPICAL: WaterParams

Clear tropical water: physical Oceanic IB colour, gentle wind, bright foam.

WATER_PRESETS

const WATER_PRESETS: Readonly<Record<WaterPresetName, WaterParams>>

The bundled presets by name.

WATER_QUALITY

const WATER_QUALITY: Readonly<Record<'low' | 'medium' | 'high' | 'ultra', WaterQuality>>

The built-in tiers, one per cost band.

Types

ClipmapMesh

interface ClipmapMesh

The clipmap as vertex positions (x, z pairs, y is 0) and a triangle index list.

  • positions: Float32Array — Interleaved x, z per vertex.
  • indices: Uint32Array — Triangle indices.
  • totalSize: number — Size of the outermost level in world units.
  • coarsestSpacing: number — Vertex spacing of the coarsest level, the snap grid.

Floater

interface Floater

A floated object's controller.

  • readonly target: { x: number; y: number; z: number; rot: number; pitch: number; roll: number } — The object being floated.
  • readonly spec: FloatSpec — The footprint; edit it live.
  • kill(): void — Stop floating it.
  • readonly dead: boolean — True after kill.

FloatSpec

interface FloatSpec

What a floated object needs: its footprint on the water.

  • length: number — Hull length, bow to stern along the object's forward (local +z, as world-3d's yaw points it), metres.
  • width: number — Hull beam, port to starboard, metres.
  • offset?: number — How far below the deck the waterline sits, metres: the object's y is the surface plus this. Default 0.
  • damping?: number — Smoothing 0..1 applied per frame to the height and tilt; 1 snaps. Default 0.25.
  • wake?: boolean — Cast a wake as it moves. Default true.
  • wakeStrength?: number — Wake strength 0..1. Default 0.6.

MaskHandle

interface MaskHandle

A registered hull mask.

  • remove(): void — Stop masking; the water closes back over the hull next frame.

ResolvedWaveOptions

type ResolvedWaveOptions = Readonly<Required<Omit<WaveSimOptions, 'cascades'>> & { cascades: readonly number[] }>

WaveSimOptions with every field present.

SprayEmitter

interface SprayEmitter

A registered spray source.

  • readonly index: number — Slot 0..MAX_SPRAY_EMITTERS-1.
  • readonly params: SprayEmitterParams — Live; edit in place.
  • enabled: boolean — Whether this emitter's probes can fire; a disabled emitter keeps its slot. Default true.
  • setProbes(points: ReadonlyArray<readonly [number, number, number]>): void — Probe points in the emitter's LOCAL space; more than MAX_SPRAY_PROBES are dropped.
  • setWorld(m: Float32Array): void — Column-major 4x4 local-to-world matrix (m[12..14] = translation). Call every frame the object moves.
  • remove(): void — Frees this emitter's slot for reuse; later calls on it do nothing, and the emitter is dropped from the system's emitters list.

SprayEmitterParams

interface SprayEmitterParams

Per-emitter tunables; every burst freezes a copy at spawn, so edits reach only later bursts.

  • bottomFadeStart: number — Bottom-fade start, 0..1 of the billboard height; alpha is zero at and below it.
  • bottomFadeStop: number — Bottom-fade stop, 0..1 of the billboard height; alpha is full at and above it.
  • duration: number — Burst lifetime in seconds.
  • fadeOutTime: number — Alpha tail in seconds, measured back from death; the flipbook completes before it starts.
  • opacity: number — Opacity multiplier, 0..1.
  • respawnTime: number — Extra cooldown in seconds after a burst's life before its probe may fire again.
  • size: number — Billboard side in metres.
  • spawnJitterTime: number — Most random delay in seconds between the trigger and the visible spawn.
  • stretchX: number — Width multiplier.
  • stretchY: number — Height multiplier.
  • submersionDepth: number — Metres below the displaced surface the billboard's bottom edge sits, hiding the seam.
  • velocityHeightFactor: number — Height multiplier per m/s of impact speed over the threshold, capped at 2x; 0 disables.
  • velocityScaleFactor: number — Uniform scale per m/s of impact speed over the threshold, capped at 2x; 0 disables.
  • velocityThreshold: number — Least vertical closing speed in m/s at the crossing for a probe to fire.

SprayHandle

interface SprayHandle

A bow-spray emitter riding a world-3d object.

  • readonly params: SprayEmitterParams — The emitter's live parameters.
  • enabled: boolean — Pause the bursts without dropping the emitter.
  • probes(points: ReadonlyArray<readonly [number, number, number]>): void — Replace the probes.
  • remove(): void — Drop the emitter; its droplets finish their lives.

SpraySpec

interface SpraySpec

What spray() takes: where on the hull the water is sensed, and any emitter fields to override the preset's.

  • probes?: ReadonlyArray<readonly [number, number, number]> — Probe points in the object's local space (before its scale). Omitted: derived from its mesh — bow, stern and pairs down each side at the origin's height, where a floated hull meets the water.
  • params?: Partial<SprayEmitterParams> — Fields that override params.spray for this emitter.

WakeSim

interface WakeSim

A live wake field on one GPU device.

  • readonly field: GPUTextureView — The rg16float field texture: r = wake height in metres (already clamped to MAX_WAKE_HEIGHT), g = wake foam 0..1. Sample at uv = (worldXZ - origin) / worldSize + 0.5 with WakeSim.sampler; the rim is sponged to zero so clamp-to-edge never shows a seam. Re-read after configure changes the resolution, since the texture is rebuilt.
  • readonly sampler: GPUSampler — Clamp-to-edge, linear sampler for WakeSim.field.
  • readonly origin: { x: number; z: number } — Field centre in world xz, snapped to texel multiples so the field never swims; the contents shift with it. Updated by each update.
  • readonly worldSize: number — Field extent, metres per side.
  • readonly resolution: number — Field resolution, texels per side.
  • configure(opts: WakeSimOptions): void — Apply new options; unspecified fields keep their current values.
  • update(encoder: GPUCommandEncoder, dt: number, centre: { x: number; z: number }, sources: ReadonlyArray<WakeSource>): void — Encode this frame's step: recentre on centre, inject sources (at most MAX_WAKE_SOURCES; extra ones are dropped), advance the solver by dt seconds (clamped to the stability limit), and update the foam. Call once per command encoder, before the encoder is submitted: the uniforms are written through the queue and a second call before submit would overwrite them.
  • destroy(): void — Release every GPU resource. The sim stays usable: a later update or field read rebuilds a blank field.

WakeSimOptions

interface WakeSimOptions

Construction and reconfiguration options for createWakeSim.

  • resolution?: number — Field resolution in texels per side; default 512. Changing it rebuilds the field.
  • worldSize?: number — Field extent in world metres per side; default 100 (the reference quality-tier default). Changing it clears the field.
  • friction?: number — Velocity-damping friction, at least 0; default 0.25. Required for stability and sets the trail length.
  • foamPersistence?: number — Persistent foam decay per frame, 0..1; default 0.99. Closer to 1 keeps the foam trail longer.
  • foamStrength?: number — Foam injection rate for the hull track and breaking crests; default 0.8.
  • foamBreakThreshold?: number — Surface steepness (height gradient magnitude) at which crest foam begins; default 0.
  • gravity?: number — Gravity in m/s^2 the dispersion follows; default 9.81. Pass the ocean's value so the wake speed matches the swell.

WakeSource

interface WakeSource

One hull disturbing the field this frame.

  • x: number — World x of the hull centre, metres.
  • z: number — World z of the hull centre, metres.
  • vx: number — World velocity x, m/s.
  • vz: number — World velocity z, m/s.
  • length: number — Hull half-length in metres, along the heading.
  • width: number — Hull half-width in metres; the footprint radius of the swept capsule.
  • yaw: number — Heading yaw in radians, 0 along +x, measured toward +z.
  • strength: number — 0..1 strength: how deep the hull sits, as a fraction of DEPTH_PER_WIDTH * width metres.

WaterColorCustom

interface WaterColorCustom

Artist-authored colour: per-channel Beer-Lambert absorption, the transmitted crest colour, and the body colour.

  • mode: 'custom' — Colour mode.
  • absorptionColor: string — Per-channel absorption coefficient, as a css colour; red dies fastest in clear ocean.
  • transmissionColor: string — Colour of light transmitted through a wave crest, css.
  • waterColor: string — Intrinsic in-scattered body colour, css.

WaterColorPhysical

interface WaterColorPhysical

Physical colour from constituent concentrations, integrated over the visible spectrum.

  • mode: 'physical' — Colour mode.
  • algae: number — Phytoplankton concentration; greens productive water.
  • silt: number — Mineral suspended sediment; brightens and mutes turbid water.
  • stain: number — Coloured dissolved organic matter; browns stained water.

WaterFloorParams

interface WaterFloorParams

The ocean floor: a camera-following plane below the surface, displaced by noise, sand blending to rock with height.

  • enabled: boolean — Draw it at all. Default true.
  • depth: number — Metres below the water's rest level.
  • meshResolution: number — Grid cells along each edge of the plane; the plane spans the clipmap.
  • tileSize: number — Metres of world one repeat of the textures covers, before textureScale.
  • displacementScale: number — Metres per unit of the terrain noise: larger is broader features.
  • displacementStrength: number — Metres of vertical displacement the noise adds.
  • lacunarity: number — Noise frequency multiplier per octave (two octaves).
  • persistence: number — Noise amplitude multiplier per octave.
  • normalScale: number — How strongly the texture normal maps tilt the terrain normal.
  • blendThreshold: number — Normalised terrain height (0 low, 1 high) where sand gives way to rock.
  • blendSoftness: number — Width of that transition.
  • textureDisplacementStrength: number — Metres of extra displacement from the textures' own relief.
  • textureScale: number — Repeats of the textures per tileSize.
  • causticDepthAttenuation: number — How fast the caustics on the floor fade with depth (0 none).

WaterFoamLayer

interface WaterFoamLayer

One foam texture layer.

  • enabled: boolean — Whether this layer draws.
  • opacity: number — Master opacity 0..1.
  • color: string — Foam tint, css.
  • size: number — Texture size in world units.
  • texture: 1 | 2 | 3 — Which bundled foam texture: 1, 2 or 3.

WaterFoamParams

interface WaterFoamParams

The three foam sources.

  • surface: WaterFoamLayer & { coverage: number } — Texture-thresholded foam everywhere.
  • waves: WaterFoamLayer & { windStretch: number; persistence: { decayTime: number; windwardStrength: number; crestStrength: number } } — Persistent wave-crest foam from the energy field.
  • shoreline: WaterFoamLayer & { coverage: number; range: number } — Foam in shallow water against geometry.

WaterFogParams

interface WaterFogParams

Atmospheric fog on the water surface, for when no sky pack is fogging the scene.

  • color: string — Near fog colour, css.
  • enabled: boolean — Whether the surface fogs itself.
  • fadeStart: number — Distance where fog begins.
  • fadeEnd: number — Distance where fog is total.
  • fadePower: number — Curve power; 1 = linear.
  • skyBlendDistance: number — Distance over which the fog colour reaches the sky colour.

WatermaxApi

interface WatermaxApi

game.watermax.

  • readonly params: WaterParams — The live parameters. Edit fields in place; the water follows next frame.
  • apply(patch: WaterParamsPatch): void — Merge a partial set of parameters.
  • preset(name: WaterPresetName): void — Replace everything with a bundled preset.
  • readonly presets: ReadonlyArray<WaterPresetName> — The bundled preset names.
  • level: number — Still water level, world y.
  • probe(x: number, z: number): WaterProbe — Track a point's height on the GPU. Up to 128 points are read back each frame, one or two frames late.
  • heightAt(x: number, z: number): number — Height of the surface at a point, from the nearest probe, or the still level when nothing is close.
  • readonly submerged: boolean — True when the camera is under the surface.
  • float(target: { x: number; y: number; z: number; rot: number; pitch: number; roll: number }, spec: FloatSpec): Floater — Float a world-3d object on the waves: four probes under its hull set its height, pitch and roll, and it casts a wake. A handle's yaw is its rot.
  • wake(source: WakeSource): void — Push a wake source for this frame only, for something that is not floated. Its yaw is the wake's own heading, 0 along +x towards +z: an engine yaw rot is rot + Math.PI / 2.
  • quality(tier: WaterQualityName): void — Switch quality tier.
  • readonly qualityLevel: WaterQualityName — The active tier.
  • windDirection: number — Wind direction in radians, the spectrum's and the foam's.
  • mask(target: { x: number; y: number; z: number; rot: number; pitch: number; roll: number; sx: number; sy: number; sz: number }): MaskHandle — Cut the water out of a hull: the object's mesh is drawn into a screen-space mask each frame and the surface behind its nearest face is discarded, so a deck stays dry however low the boat rides. Up to 64.
  • spray(target: { x: number; y: number; z: number; rot: number; pitch: number; roll: number; sx: number; sy: number; sz: number }, spec?: SpraySpec): SprayHandle — Bow spray off a world-3d object: probes at its waterline watch the surface, and where one is driven through the water fast enough a burst of droplet billboards is thrown up, the spray system. Up to 16 emitters, 32 probes each.
  • readonly stats: { readonly cascades: number; readonly vertices: number; readonly triangles: number; readonly probes: number; readonly sprayEmitters: number; readonly rainStreaks: number; readonly masked: number } — Counts for a profiler.

WatermaxOptions

interface WatermaxOptions

Manifest options for watermax. All optional.

  • preset?: WaterPresetName — Which bundled look to start from. Default 'sunset'.
  • quality?: WaterQualityName — Quality tier: cascades, clipmap density, foam and wake field sizes, SSR. Default 'medium'.
  • level?: number — Still water level, world y. Default 0.
  • seed?: number — Deterministic seed for the spectrum phases. Default 1.
  • foamUrls?: string[] — Foam texture URLs, three of them. Default 'assets/watermax/foam1.jpg' and so on.
  • caustics?: boolean — Publish the caustic pattern for render-3d. Default true.
  • causticFade?: number — How deep below the surface the seabed caustics fade out, metres. Default 25.
  • causticGain?: number — Extra multiplier on the published caustic strength; the engine's seabed tap is brighter than the floor material was tuned for, so the default halves it. Default 0.5.
  • underwater?: boolean — Render the underwater post pass. Default true.
  • floorUrls?: { sand: string; sandNormal: string; rock: string; rockNormal: string } — Ocean floor textures: sand colour, sand normal, rock colour, rock normal. Default 'assets/watermax/sand.jpg' and so on.
  • sprayUrls?: string[] — Spray flipbooks, up to eight 7×7 sheets. Default 'assets/watermax/splash1.jpg' … 'splash8.jpg'.

WaterParams

interface WaterParams

The whole ocean, as a preset stores it.

  • waves: WaterWaveParams — The spectrum.
  • color: WaterColorCustom | WaterColorPhysical — Body colour.
  • foam: WaterFoamParams — Foam layers.
  • fresnel: { iorRatio: number; refractionStrength: number } — Refraction and the interface.
  • sss: { enabled: boolean; intensity: number; power: number } — Subsurface scattering at backlit crests.
  • sparkle: { enabled: boolean; intensity: number; power: number; minDistance: number; fadeDistance: number } — Sun glints.
  • ssr: { enabled: boolean; strength: number } — Screen-space reflections of geometry.
  • caustics: { enabled: boolean; intensity: number; scale: number; waveDistortion: number } — Seabed caustics, published for render-3d.
  • fog: WaterFogParams — Surface fog.
  • underwater: { tintColor: string; distortionEnabled: boolean; distortionIntensity: number; distortionScale: number; distortionSpeed: number; sunShafts: boolean; sunShaftIntensity: number; absorption: number } — Under the surface.
  • wake: { friction: number; foamPersistence: number; foamStrength: number; foamBreakThreshold: number } — Boat wakes.
  • clipmap: { baseSize: number; levels: number } — The clipmap.
  • environment: number — Environment reflection multiplier.
  • sunScale: number — The sun's strength on the water, a multiplier on the lighting record's intensity.
  • floor: WaterFloorParams — The ocean floor: a displaced sand-and-rock plane that follows the camera below the surface.
  • rain: WaterRainParams — Rain: falling streaks and the analytic ripple rings they leave on the surface.
  • spray: WaterSprayParams — Bow spray: the defaults every emitter starts from.

WaterParamsPatch

type WaterParamsPatch = Omit<DeepPartial<WaterParams>, 'color'> & { color?: WaterColorCustom | WaterColorPhysical }

A recursive partial of WaterParams; color is replaced whole because its shape depends on the mode.

WaterPresetName

type WaterPresetName = 'sunset' | 'tropical' | 'storm'

Preset names.

WaterProbe

interface WaterProbe

A point on the sea the GPU keeps a height for.

  • x: number — World x of the probe; move it freely.
  • z: number — World z of the probe.
  • readonly y: number — Surface height at the probe from the last readback, world y.
  • readonly normal: { x: number; y: number; z: number } — Surface normal at the probe from the last readback.
  • kill(): void — Stop tracking it.
  • readonly dead: boolean — True after kill.

WaterQuality

interface WaterQuality

Quality tiers for the water.

  • segments: number — Clipmap vertices per side per level.
  • cascades: number — FFT cascade count.
  • resolution: number — FFT resolution per cascade.
  • foamFieldResolution: number — Foam field texels per side.
  • foamFieldWorldSize: number — Foam field extent in metres.
  • wakeResolution: number — Wake field texels per side; 0 disables the wake.
  • wakeWorldSize: number — Wake field extent in metres.
  • ssr: boolean — Screen-space reflections on.
  • ssrSteps: number — SSR march cap.
  • ssrMaxDistance: number — SSR reach in metres.
  • shaftDiv: number — Sun shaft pass divisor.

WaterQualityName

type WaterQualityName = 'low' | 'medium' | 'high' | 'ultra'

Quality tier names.

WaterRainParams

interface WaterRainParams

Rain over the sea: streaks in the air and ripple rings on the surface, flat, one record.

  • enabled: boolean — Whether rain is drawn (streaks and ripple rings); when false the streak intensity is forced to 0.
  • color: string — Streak colour.
  • intensity: number — How much of the streak pool falls, 0..1.
  • fadeDistance: number — Metres from the camera where streaks are fully faded.
  • streakLength: number — Streak length in metres.
  • streakWidth: number — Streak width in metres.
  • speed: number — Fall speed multiplier.
  • opacity: number — Streak opacity.
  • rippleDecay: number — How quickly rings fade, 0.1..5.
  • rippleDensity: number — Rings per area, 0..1.
  • rippleFadeEnd: number — Metres from the camera where rings are fully faded.
  • rippleSize: number — Ring cell size in metres, 0.1..1.
  • rippleStrength: number — How much the rings tilt the surface normal, 0..1.

WaterSprayParams

interface WaterSprayParams

Bow spray defaults: what every emitter is born with unless spray() overrides a field.

  • enabled: boolean — Master switch for all bow spray; when false no emitter fires, whatever its own enabled says.
  • bottomFadeStart: number — Bottom-fade start, 0..1 of the billboard height; alpha is zero at and below it.
  • bottomFadeStop: number — Bottom-fade stop, 0..1 of the billboard height; alpha is full at and above it.
  • duration: number — Seconds a droplet lives.
  • fadeOutTime: number — Seconds of the end of that life spent fading.
  • opacity: number — Opacity multiplier, 0..1.
  • respawnTime: number — Seconds a probe rests after a burst.
  • size: number — Billboard height in metres.
  • spawnJitterTime: number — Seconds of random delay before a burst.
  • stretchX: number — Billboard width multiplier.
  • stretchY: number — Billboard height multiplier.
  • submersionDepth: number — Metres the billboard's foot sits below the surface.
  • velocityHeightFactor: number — Height multiplier per m/s of impact speed over the threshold, capped at 2x; 0 disables.
  • velocityScaleFactor: number — Uniform scale per m/s of impact speed over the threshold, capped at 2x; 0 disables.
  • velocityThreshold: number — Impact speed (m/s) a probe must cross the surface at to burst.

WaterWaveParams

interface WaterWaveParams

The FFT ocean's spectrum knobs.

  • amplitude: number — Overall height multiplier. Default 1.
  • animationSpeed: number — Time multiplier on the wave motion. Default 1.
  • windSpeed: number — Wind speed in metres per second; energy and steepness at the peak wavelength.
  • windDirection: number — Wind direction in radians, 0 = +x.
  • choppiness: number — Horizontal displacement multiplier: sharper crests.
  • peakWavelength: number — Dominant wavelength in metres, the JONSWAP spectral peak.
  • spectralSharpness: number — Directional spread multiplier; above 1 narrows waves toward the wind.
  • standingWaveRatio: number — 0 = travelling waves, 1 = standing.
  • maxScale: number — Largest cascade tile in metres; the finer tiles derive from it.

WaveCascade

interface WaveCascade

One cascade's outputs. Sample both textures at uv = worldXZ / scale + 0.5 with the sim's repeat sampler.

  • readonly scale: number — Tile size in metres; the textures repeat every scale metres of world xz.
  • readonly displacement: GPUTextureView — rgba16float, res×res, no mips. xyz = world displacement in metres of the grid point at that texel (x, y = height, z — y already negated for sharp crests, x/z already multiplied by choppiness), w = 1.
  • readonly normal: GPUTextureView — rgba16float, res×res, full mip chain. rgb = worldNormal · 0.5 + 0.5 (y up); a is the directional eigenvalue: 1 − clamp(1 − λmin, 0, 1) · edgeFactor where λmin is the smaller eigenvalue of the horizontal Jacobian and edgeFactor gates it to wind-facing slopes. 1 is unfolded; 1 − a is foam.

WaveSim

interface WaveSim

A running FFT ocean. Create with createWaveSim.

  • readonly cascades: ReadonlyArray<WaveCascade> — The cascades, coarsest first. Replaced (new views) after a configure that changes resolution or cascade count.
  • readonly sampler: GPUSampler — Repeat both ways, linear mag/min, linear mipmaps, anisotropy 8.
  • readonly resolution: number — Grid resolution of every cascade.
  • readonly options: ResolvedWaveOptions — The options in force.
  • readonly sharedFft: boolean — True when the FFT runs in workgroup memory on this device; false means the global-memory fallback.
  • configure(opts: WaveSimOptions): void — Applies new options. Spectrum-shaping changes re-run the spectrum init on the next update; a resolution or cascade-count change rebuilds every GPU resource (and cascades). Same values: no work. Throws RangeError on invalid values, like resolveWaveOptions.
  • update(encoder: GPUCommandEncoder, time: number): void — Encodes this frame: (init if dirty) → time evolution → FFT → normalise → normals → texture copies → mips, then the height sampler if points were requested. time in seconds; animationSpeed and the WAVE_TIME_PERIOD_SECONDS fold are applied inside. Also queues the uniform writes.
  • requestHeights(points: Float32Array): void — Queues up to MAX_SAMPLE_POINTS world xz points (x0, z0, x1, z1, …) for GPU sampling in the next update. Extra points are dropped; a second call before update replaces the first.
  • readHeights(): Float32Array | null — The heights (y, metres) of the most recently landed request, one per point in order, or null before any lands.
  • readSamples(): Float32Array | null — The full sample of the most recently landed request: 4 floats per point, height, nx, ny, nz, or null.
  • afterSubmit(): void — Call after queue.submit each frame: starts the async readback of any sample copied this frame. Never throws.
  • destroy(): void — Releases every GPU resource; every later call is a no-op.

WaveSimOptions

interface WaveSimOptions

Configures a WaveSim. Every field is optional; see DEFAULT_WAVE_OPTIONS for the defaults.

  • resolution?: number — Grid resolution per cascade: a power of two in 128..512. Default 256. Throws RangeError otherwise.
  • cascades?: number[] — Cascade tile sizes in metres, coarsest first, 1..4 of them. Default: deriveCascadeScales(1024, resolution, 3), i.e. 1024, 96, 9 at 256.
  • windSpeed?: number — Wind speed in m/s; scales the JONSWAP energy at the peak wavelength. Default 2.5.
  • windDirection?: number — Wind direction in radians (0 blows along +x). Default 3.9618974020271276.
  • choppiness?: number — Horizontal displacement multiplier; above ~1.5 crests fold and foam. Default 1.51.
  • peakWavelength?: number — Dominant wavelength in metres — the spectral peak. Default 20.
  • spectralSharpness?: number — Directional-spread multiplier: 1 physical, above narrows toward the wind. Default 1.46.
  • standingWaveRatio?: number — 0 travelling waves … 1 standing waves. Default 0.23.
  • amplitude?: number — Global height and displacement multiplier. Default 1.
  • animationSpeed?: number — Multiplier on the time passed to update. Default 1.
  • seed?: number — Deterministic seed of the Gaussian spectrum; cascade i uses seed + i. Default 1.
  • gravity?: number — Gravity in m/s². Default 9.81.
  • jonswapGamma?: number — JONSWAP peak-enhancement γ. Default 3.3.
  • foamWindBias?: number — 0 folds foam on both faces equally, 1 only on the wind-facing leading edge. Default 1.
  • foamLeadingEdgeScale?: number — Sharpness of the leading-edge gate (the smoothstep width is 0.1 × this). Default 2.