| name | threejs-spectral-ocean |
| description | Synthesize broad-band offshore seas with spectral FFT cascades in Three.js WebGPU/TSL. Use for homogeneous directional wind sea or swell, transported foam history, bounded CPU surface queries, or offshore forcing of a separate coastal model. |
Spectral Ocean
Build a dimensioned random sea from directional variance density to one
displaced, shaded surface. The spectral domain is a horizontally homogeneous
periodic patch with one mean depth and one dispersion relation per cascade.
1. Gate the representation
Choose from the required observable, not the desired look:
| Observable | Owner |
|---|
| A few authored modes or exact cheap CPU queries | Parametric waves in $threejs-water-optics |
| Bounded disturbances or explicit walls | Bounded heightfield in $threejs-water-optics |
| Broad stochastic directional sea | This spectral FFT skill |
| Depth-varying refraction, diffraction, breaking, run-up, or wet/dry fronts | Coastal branch in $threejs-water-optics; this skill may supply offshore forcing |
| Overturning, entrained air, jets, or three-dimensional vortices | External free-surface solver |
Select FFT cascades only when the visible wavelength band contains enough
independent modes that direct summation is more expensive or visibly
repetitive. Record the visible wavelength interval, footprint, tolerated
phase/slope error, periodic repeat distance, interaction needs, and measured
storage budget.
Complete when: one row owns every required water observable, and every
rejected row has a stated validity or cost reason.
2. Freeze the spectral convention
Use metres, seconds, radians per metre, and radians per second. Declare:
- stable horizontal physics frame and water datum;
- patch length, resolution,
Delta k, represented radial band, and guard band
for every cascade;
- inverse-transform sign, normalization, and centered-index correction;
- gravity, mean depth, surface tension, density, and uniform current;
- spectrum family, direction convention, seed, and cascade power windows;
- one coefficient/state clock and one render-surface owner.
The required inverse convention is
f[j] = sum_n F[n] exp(+i 2 pi n dot j / N).
If the implementation uses a normalized inverse, rescale coefficient
initialization rather than tuning the sea afterward. Current is a separate
water velocity and Doppler term; atmospheric wind never becomes water current.
For wind sea, record the sampled wind vector, reference height, averaging
window and footprint, profile/drag calibration, stability treatment, fetch,
forcing duration or wave age, spreading model, and represented source terms.
Retarget a statistical state continuously; gusts may affect spray but do not
reseed mature waves.
Load only the reference sections for
environment forcing,
wavevector and transform convention,
and dispersion and dimensional spectrum
while freezing this contract.
Complete when: every coefficient and clock has unambiguous units, the same
transform convention appears in initialization and validation, and replaying
one forcing snapshot reproduces the same coefficients and phase.
3. Build deterministic cascades
Load the reference sections for
cascade power and deterministic coefficients,
displacement and derivative spectra,
packing and inverse FFT,
and the transform gate.
When implementing or changing the transform, import or adapt the
FFT convention oracle. Feed every
makeConventionFixtures() spectrum through the implementation, then pass its
measureTransform() metrics and caller-declared gates to applyTolerances()
before loading the production spectrum.
For directional angular-frequency variance density S_omega,
P(k_x,k_z) = S_omega(omega(k),theta) |d omega/dk| / |k|,
[P] = m^4.
Include Delta k_x Delta k_z in discrete coefficient variance. Power windows
obey sum_c w_c(k)=1 over the represented band; smooth overlap uses
sqrt(w_c) on amplitude. Generate normals from
(seed,cascade,index_x,index_z) so masks and quality changes do not perturb
surviving coefficients.
Evolve a Hermitian height field, construct derivatives in wavevector space,
then transform height, horizontal displacement, slopes, and displacement
derivatives. Treat DC and self-conjugate Nyquist cells explicitly before any
division.
Complete when: directional normalization, power-window sum, target versus
realized variance, coefficient determinism, Hermitian partner error, DC, both
axis bins, an oblique bin, a conjugate pair, every Nyquist parity case, CPU-DFT
error, Parseval error, and post-IFFT imaginary leakage all pass declared
gates.
4. Assemble one exact surface
Load only exact displaced geometry
for the composite tangent, velocity, sampling, and fold rules.
Sum all cascade fields before nonlinear geometry. For
P(q) = (q_x + chi D_x, h, q_z + chi D_z),
derive both parametric tangents from the summed displacement and set the normal
to normalize(cross(P_qz,P_qx)). Compute the full horizontal Jacobian,
including the mixed derivative. A nonpositive determinant is a fold, not a
normal-map defect.
Choose the sign of chi with the one-mode criterion: under the positive
inverse convention, h=a cos(kx) and positive choppiness must produce
X=x-chi a sin(kx), compressing the crest.
Geometry must resolve the represented displacement band. Filter or omit modes
that its footprint cannot resolve, and report their height/slope contribution.
One mesh/material owns every displayed location.
Complete when: the one-mode travel and displacement signs are correct;
analytic tangents and normals match finite differences; combined variance and
minimum Jacobian are measured; folds are counted; and position, normal,
velocity, foam, and temporal consumers read the same immutable state pair.
5. Add only represented optional state
Foam
When foam is enabled, load only the
foam section.
Build one source from combined-cascade compression, Jacobian evolution,
curvature, or a calibrated breaking proxy, then evolve one bounded history.
Declare Lagrangian parameter storage, Eulerian advection, or conservative
density transport. Source, transport, reaction, diffusion, and display
coverage remain separately inspectable.
Queries
When CPU queries are enabled, load only the
CPU queries section.
Retain a deterministic coefficient subset on CPU. Expose parameter-coordinate
sampling separately from physics-horizontal sampling. Choppy Eulerian queries
invert X(q)=x; the omitted-mode bound, inversion residual, numeric error, and
GPU probe discrepancy stay separate. The frame path performs no synchronous
map readback.
Optics
When $threejs-water-optics is installed and available, invoke it for Fresnel,
refraction, Beer-Lambert, and receiver-caustic rules. Surface geometry,
derivatives, foam, and optics then share one cause and one final output
transform. When it is unavailable, report missing optics owner and keep
optics outside this implementation and its claims.
Complete when: each enabled option has an owner, validity interval,
support/filter, reset rule, and failure diagnostic; disabled options allocate
no state and make no claim.
6. Couple a coast only when required
When the sea feeds a depth-varying or wet/dry owner, load only the
coastal donor
section and its phase-resolved, phase-averaged, and ownership subsections.
Before either solver runs, record the spectral donor, coastal consumer, and
forcing, reaction, render-surface, and foam owners; SI units; common physics
frame/origin and water datum; clock, sample instant or transfer interval,
cadence, and sample phase; band support, filter, and quadrature; validity,
staleness, and error; and donor/receiver state and resource versions. Keep
atmospheric forcing, mean current, wave transfer, and coastal reaction as
separate signals. Reset dependent boundary, query, foam, geometry, and temporal
state after a clock discontinuity, rebase, incompatible version,
representation change, or ownership change. Invoke $threejs-choose-skills if
the composed route still lacks one owner per observable.
Choose exactly one:
- Phase resolved: transfer elevation and depth-integrated discharge with
common phase, intrinsic frequency, sample instant, band identity, and error.
Prescribe only the incoming characteristic; retain the coastal outgoing one.
- Phase averaged: transfer dimensioned wave action/energy, intrinsic
frequency, group velocity, current, direction, quadrature, and band identity.
The receiver separately owns local display phase.
The FFT remains the offshore donor. It does not gain refraction, diffraction,
breaking, run-up, or wet/dry ownership by sampling bathymetry in its material.
Coherent donor/receiver copies use one spatial render owner; power windows apply
only to independent or orthogonal bands.
Complete when: producer, consumer, interval, sample phase, units, support,
filter, validity, staleness, error, state versions, reflection scope, and
render/foam ownership are explicit; single-mode normal and oblique tests close
phase, elevation, discharge or action, transmitted power, and reflection.
7. Integrate and verify the GPU path
Load only WebGPU and performance
for backend, resource, dispatch, precision, and lifecycle requirements.
Initialize WebGPURenderer before checking its backend or limits. Use
StorageTexture data with NoColorSpace and explicit precision. A global
producer/consumer dependency crosses a dispatch boundary. Select a validated
global ping-pong FFT—Stockham autosort or explicit-bit-reversal radix-2—or
workgroup-resident rows from transform correctness, initialized device limits,
occupancy, traffic, and sustained timing.
Render through one RenderPipeline and one output transform. Keep coefficient,
resolved-map, foam, query, and presentation generations immutable while a
render lease consumes them; resize, seed, spectrum, cadence, representation, or
origin changes either migrate compatible state or reset all dependent history.
Report actual allocations, peak live bytes, dispatches, precision comparison,
warm sustained timings, and fixed-camera final/no-foam/no-detail/no-post
captures for each named target.
Complete when: the initialized backend and selected limits are recorded;
all numerical gates above pass; no frame-critical readback exists; lifecycle
rebuild/dispose reaches an allocation plateau; and the final image has one
surface owner and one output transform.