All projects

Wave Laboratory

Side project · simulation & graphics

The lab needs a browser with WebGL 2. It runs on desktop and on phones.

At a glance

4 FFT cascades, from 1 km swells down to centimetre ripples

280 water columns, at most, sampled under a hull to integrate pressure

328 + 25 unit and long-running validation tests, counted by grep

The problem

I have always liked how much of the sea can be written as a short recipe. A real ocean is not one wave. It is thousands of simple waves of different lengths and directions added together, and oceanographers describe how much energy each one carries with a curve called a spectrum. Ships respond to that sum, not to any single wave.

I wanted a place to build a sea from those published recipes, drop ships into it, and watch and measure what happens, with the physics honest enough that the numbers mean something and the page still running smoothly in a browser tab.

Approach

The sea is built from named, published models. The energy curve comes from JONSWAP, Pierson–Moskowitz or Bretschneider spectra, with a depth correction (TMA) for shallow water and Hasselmann’s fetch-limited growth for seas built by wind. Waves are spread over directions, and the speed of each wave follows the dispersion relation, including water depth and surface tension. The surface is then synthesised with an inverse FFT on four grids of different sizes, with a “choppy” sideways displacement that sharpens crests.

Ships float by pressure integration: for each moment, the simulation sums the water pressure over the part of the hull that is underwater (buoyancy plus the Froude–Krylov wave force), then adds added mass, damping, propulsion, rudder and wind. A freak wave can be placed on purpose using NewWave theory, which lines up many components so their crests meet at one point and time. The Draupner crest, 18.5 m in a sea with a significant height of 11.9 m, is the reference case.

What I built

Results

The Wave Laboratory app in its light theme. A container ship leaves a white wake on a blue-green sea in the central 3D view, with a round chart overlay. A scene list is on the left, a sea-state panel with significant wave height, wind and depth is on the right, and playback controls and chart tabs run along the bottom.

The lab with its default moderate sea: a container ship under way, with scene tree, sea-state readout and instrument dock.

The app in its dark theme following a container ship through a rough blue sea with white wake and rain. The sea-state panel explains a focused wave group with a 12 m crest in an 8 m significant-height sea, peaking at t = 75 seconds.

The rogue-wave preset: a container ship in a storm sea, with a focused NewWave crest due at a gauge 380 m ahead.

A container ship seen from low over a pink and orange sunset sea. A bright sun glows near the horizon and a column of glare runs down the water toward the viewer. Controls for camera mode and view size sit at the top.

The lab at sunset: a container ship in a Beaufort 6 sea, HDR bloom on.

The same container ship and sunset sea as the previous view, but the sun is a smaller, crisper disc with no glow spreading across the ship, and the water glints less strongly.

The same view with bloom off.

A view from under the water. A dark green ship hull runs diagonally across the lower left. Above it, the bright rippled underside of the sea surface shows foam patches against deep blue water. A small round chart sits at the lower right.

Underwater camera at 6.1 m, looking at a hull from below.

An underwater view in which everything is tinted dark green, with slightly lighter and darker patches. No hull is visible, and a chart panel sits at the lower right.

Turbid coastal water (Jerlov type 9) at 3.7 m: blue light is absorbed first, so the view is dark green.

Heads up. This is a teaching and exploration tool, not a certified seakeeping program. The reference notes list what is not modelled, such as current shear with depth, wave refraction by currents, and floodwater sloshing. The technical report covers these limits.

What I learned

Stack

TypeScript React Three.js WebGL 2 Web Workers Zod Zustand Vite Vitest Playwright axe-core