# Reset wash

Clicking **Start again** triggers a timed shoreline pass instead of an instantaneous clear. The effect is coordinated in three layers:

1. `WaveResetEffect` maps the loaded `wave.mp3` duration to an incoming swash and an outgoing retreat.
2. `SandPostProcess` renders the transient water/foam optics over the established sand image while preserving the same shoreline contract used by the simulation.
3. `SandSolver.encodeWaveReset()` reinitializes only the newly wetted band on the bed state, so grooves are erased incrementally as the water front advances instead of all at once.

The shoreline logic is shared between rendering and erasure through `src/reset/wgsl.ts`, which keeps the visible front and the simulation clear mask aligned. The front intentionally travels with a slight diagonal tilt so the wash reads as a shallow-angle swash rather than a perfectly perpendicular wipe. During retreat, the CPU derives the near edge and horizontal span of the currently visible bed from the active camera, tracks the minimum offset of that same low-frequency shoreline function over that visible span, and eases the shoreline envelope to the screen edge. The 0.8 m simulation boundary is often well outside the viewport, especially in portrait layouts, so timing against it would make the water disappear visibly before the audio ends. The last visible water now reaches the screen edge at the loaded `wave.mp3` endpoint.

The erase frontier is deliberately separate from the sampled visual retreat state. It advances monotonically from just outside the near edge to beyond the far edge during the incoming phase and stores both the previous and current shoreline times. This is required for correctness when GPU submissions skip animation frames: crossing the crest in one jump still resets the entire unsampled incoming swath. The first visible strip is reset on the first frame even though water is already on-screen, and there is no crest-time or end-of-effect full-board reset fallback.

The reset compute dispatch is conservatively row-bounded from the previous/current fronts plus the maximum possible shoreline offset. It still evaluates the exact shared shoreline function for every candidate cell, but it no longer launches workgroups over rows that cannot possibly change on that frame.

## Water appearance

The water surface is driven by a deterministic packet comb: several shore-aligned gravity packets and shorter capillary packets are envelope-modulated by deterministic low-frequency fields, domain-warped, and harmonically sharpened before their heights, slopes, and horizontal choppy displacement are summed. The expensive procedural field is evaluated once per frame on a fixed 512×512 world-space grid matching the sand simulation, rather than being regenerated independently for every output pixel and every optical query.

A second fixed-resolution pass derives the caustic concentration field from the same sampled surface. For each bed point it approximately inverts the sun-to-bed mapping, refracts the sun through the live surface, projects neighboring surface samples onto the bed, and uses the flat-area / refracted-area ratio as light concentration. The fullscreen optical pass then samples this caustic field while performing camera refraction, so the transport solve is not repeated millions of times at display resolution.

The final fullscreen composite still solves the camera ray against the moving surface iteratively and applies Snell refraction back onto the sand image. Reflection uses exact unpolarized air/water dielectric Fresnel over an HDR analytic daylight sky with cloud and circumsolar radiance. Direct sun is handled by the low-roughness GGX/glitter response. Beer-Lambert extinction, shallow-water in-scatter, visible-spectrum dispersion, foam, and caustic radiance are evaluated in linear light. Refracted rays that leave the available scene framebuffer sample a mirrored extension of the finite scene color. The water shading, caustics, extinction, reflection, Fresnel response, and refractive motion therefore stay active through the final viewport pixel; neighboring pixels are never collapsed onto one clamped source row and no dry-source guard strip is exposed.

During the incoming erase, the expensive bed horizon-light field is intentionally held at its last valid state. The only cells whose geometry changes are already behind the water front, so dry visible sand keeps correct lighting while avoiding a full horizon/AO solve on every erase frame. The lighting field is recomputed once when retreat begins, before cleared sand is exposed again. Airborne particles are cleared at reset start, so their shadow and loose-grain draw passes are also skipped for the duration of the wash.

The main frame loop keeps the normal one-frame-in-flight policy while drawing. Desktop permits at most two submitted frames during the reset wash to avoid an every-other-RAF cadence on a slightly-over-budget water frame. Apple mobile WebKit stays at one frame in flight during the wash. GPU backpressure must not reset the fixed-step clock during ordinary drawing: if a WebKit queue-completion callback arrives a display interval late, the next accepted frame retains the elapsed time and executes the bounded catch-up steps instead of starving the simulation while pointer segments accumulate. At the audio endpoint input is re-enabled immediately; the final dry frame is submitted in that same JavaScript turn, and the one-frame GPU gate prevents later simulation work from overtaking it. A touch that began while the reset was locked is quarantined until its pointer ends. The reset also clears all non-height GPU transport/contact/particle fields once at wash start, while the wave front remains solely responsible for the progressive visible height reset. Desktop reset pacing is unchanged.
