Break progression

ONE wave through its own break: five clocks from the last at which it is still unbroken to the first at which its crest is whitewater, marked in every frame. The window is that wave's measured break event, not a fixed slice of the period. The two ends of the range that breaks here.

Provenance

captured
2026-08-29T02:21:39.473Z
built
2026-08-29T02:21:39.473Z
commit
f3b6bde · f3b6bdedbbbbc2375b144f2ae00f69acafcb44cc
branch
main
working tree
clean
app build
011a5ba2daf9 — sha256/12 over the files build_site.py ships as sim/
note
Causal foam lead, accelerating curl crash, derived incident direction, and marine-layer controls.
links resolve to
../sim/ (published mode)

What this is. A QA instrument. Deterministic captures of the pointbreak wave model at clocks pinned by the model itself, laid out so a defect is visible by scanning rather than by hunting. Every cell is labelled with the exact URL hash it was taken at and links into the simulator at that state, so any frame here can be reopened and argued with.

What this is not. Not a validation. The model is unvalidated against measured surf — a first validation pass, model residuals against an independent record of a specific day, is the largest open gap in the project. These sheets check the model against itself: that it is deterministic, that its phases differ, that something breaks where something should. They cannot tell you whether it matches Pleasure Point. Not a surf report, not a forecast, and not usable for any decision about entering the water.

What a still cannot show. No frame here supports a claim with a verb of motion in it. What each cell states is a position at a known clock, read out of the model; the frames are ordered by the model's own clock rather than by inspection, so a reader may compare those positions across a row. The pictures themselves still cannot tell you which way anything is going, and nothing on these pages is measured off them except the pixel foam fraction, which is labelled as such.

Licence. Code, docs and these renders are MIT (© 2026 Andy Edmonds). The frames are Produced Works under ODbL — the coastline geometry they are drawn over is OpenStreetMap-derived, and the seabed is NOAA NCEI (public domain). Attribution below is required; share-alike is not triggered by a rendering. The full file-by-file split is LICENSES.md in the repository.

Coastline & spots: OpenStreetMap contributors, ODbL 1.0 · Bathymetry: NOAA NCEI Monterey Bay 1/3″ coastal DEM, NAVD88 · Seasonality: CDIP MOP v1.1 SC116 hindcast (Scripps).

This is the published view: 2 of 6 rows. The full local sheet runs six wave sizes — four condition bundles (day=small, modelcard, overhead, big) and two h0=-only rows that hold period and tide fixed. Published here are the two ends of the range that breaks at this site: the model-card day (H₀ 1.50 m, T 14 s) against the big groundswell (H₀ 2.50 m, T 17 s). The h0= rows separate height from period, which is a QA question rather than a reader's, and day=overhead sits between the two kept rows.

Why the small end is not the smallest day. day=small is H₀ 0.70 m, and Second Peak's measured peel floor is 1.08 m — below it the baked break line abandons the oblique reef branch and the peel collapses (stage α 6.6° against a 41° target). Measured on the same instrument as every other row, whitewater at the tracked crest there peaks at 0.193, against 0.858–0.890 for every row that breaks. It is not a small break; it is not a break. Publishing it under a header that says breaking — whitewater at the crest would be the exact failure this page exists to avoid, so it stays on the local sheet, labelled, with its own numbers on it. All five clocks are kept in every row: this sheet is a progression, and a progression sampled at three points stops being one.

Why only the drone camera. The local sheet shows the same rows and the same derived clocks from the cliff stand as well, and that view is the better one in principle — throw, curtain and collapse are all edge-on from a low profile. In practice the break at Second Peak is several hundred metres off that stand, so the tracked crest lands within about 20 px of the horizon and the whitewater burst this page measures at 0.86 is a few pixels of grey. The crop is bounded at 2.4× before upsampling turns soft, so it cannot rescue the frame; reaching further needs capture at a higher device scale factor, which moves the pixel-corridor coordinates the foam measurement depends on. That is deliberate work rather than a side effect, so the cliff rows stay on the local sheet until it is done.

viewport
1000 × 625 px, deviceScaleFactor 1
pinned
controls=0&q=high&speed=0 — frozen clock, no UI in frame, quality tier pinned
clock spacing
wave period T = 14.0 s, columns 0.17 s apart. the tracked wave's own BREAK EVENT at the takeoff station x = 2 m, where a crest first meets the line. Measured by seeding on the crest nearest the line at SET_ANCHOR_S = 45 s and walking time back and forward over 73 clocks (31.0–56.2 s), reading the model's foam AT THE CREST: still unbroken at t = 47.45 s (foam 0.01, crest 3 m seaward of the line) and whitewater at t = 48.15 s (foam 0.77). Columns are 0.70 s apart / 0.17 s per step, 0.050 of a period, so the tracked crest advances 0.05 of a crest spacing across the whole row.
the marks
One wave is tracked across each row. The line is its crest, marched station by station along the break line from the takeoff by the argmax of the displaced surface, seeded each column from the previous one, so the row follows the same wave rather than re-acquiring whatever is tallest. The ring is the point on that crest with the largest pocketcrestNear(θ)·bell(d)·env²·reef in shared/model-glsl.js, whose d term peaks where the crest is closest to the break line, so read along the crest it is this wave's own breaking point. Both are computed from the model and projected through the live camera matrices, not placed by hand. Before the wave reaches the line there is no ring, because there is no breaking point to ring; an empty first column is what still unbroken looks like, and a ring there would be sitting on the previous wave. The mark carries its own error bar: markerOffM in the JSON, and ring off line in each row header, is how far the ring sits from the break line over the columns where the wave is actually at it — a breaking point belongs on the line, so a mark that wandered onto the wrong wave would say so there.
the acceptance
foam on the crest in each row header is the model's whitewater at the tracked crest at each of the five clocks. It is this sheet's own pass/fail: a row must start pre-break and end broken. It is read on the crest and not at a fixed point in the water on purpose — at a point break the line is more or less permanently breaking somewhere, so a fixed station never goes quiet between waves, and an earlier version of this sheet reported foam 0.87 in a column whose own wave had not broken. Rows that cannot pass are labelled on the page and kept out of the published set rather than given a header that promises a break their frames do not contain.
framing
Cells are cropped to a window containing the tracked wave across all five columns of that row, computed from the marker's own projected extent and identical in every column, so the wave moves through a fixed frame instead of being re-centred out of its own motion. The camera is never moved — the crop is a pixel operation on the captured frame, which is why cam drift is still 0.00 m and why each cell's hash still reopens the full state. Each row header states its crop and how much closer it is; the full uncropped frame is one click away in the simulator, at the cell hash.
numbers
env — set envelope at the break line, through the repo's own setEnv twin (web-three/js/model-js.js) fed from the live uniforms.
crest — tallest displaced surface point on the ±45 m shore-normal transect nearest where the camera is aimed, read straight off the GPU through __pointbreak.curlProbe (which runs the shipped shader chunk, not a re-derivation). One of 11 transects along the stage; the stage-wide max is in the JSON as crestMaxM, and it barely moves across a wave period because some wave is always cresting somewhere on 200 m of line. These are displayed metres — the renderer applies a viewing gain of VIS = 3.2 (shared/model-glsl.js), so they are not physical wave heights.
crest±T/2 (set sheet only) — the same read swept across one full wave period centred on the column clock (12 sub-clocks) and maxed: the tallest wave to cross that station around this moment. This is the set sheet's headline number, and inst beside it is the single instant the frame shows. A set beat is 31–42 s per column against a 12–15 s wave, so the two are unrelated phases and an instant aliases the carrier into a number that claims to be about sets — measured at The Hook, the instant reads 3.13 m in the peak column where the envelope reads 5.27 m and the biggest wave (5.30 m) crossed 3.9 s earlier. When they disagree, the wave is simply between crests at that station in that frame.
foammodel — the shader's own foam field at those same transects: peak value, and the share of the 11 stations carrying foam ≥ 0.15. Camera-independent, so the framing cannot move it.
foampix — the camera's answer: share of samples at luma ≥ 160 over the whitewater attachment corridor, 25 points per line station spanning -15 m to +45 m across the break line in world metres (sections shift included), each the max of its 3×3 neighbourhood. Reads n/a where there is no baked line to project (Privates). It is corridor-local: whitewater that has already advected shoreward is outside it by design.
ceilM (JSON only) — the depth-limited crest ceiling 0.8·VIS·min(H₀Ks, γh) at those transects. null at Privates, and that is a result, not a gap: with no measured bed the site runs u_depthMix = 0, the seabed sampler is a 1×1 stand-in, and the depth the shader reads back is the storage format's own quantization floor (a flat 30.91 m stage-wide) rather than a seabed. γh then never binds and the "ceiling" is 1.878·H₀ wearing a depth limit's name, while the crest above it came from the depth-free synthetic branch. Privates has no measured bed and therefore no ceiling to be over. Its crest at the January set peak (5.32 m) is in family with all six mapped sites (4.99–5.32 m) on the same day.
links
Every hash is a live link into ../sim/web-three/ at that exact state, so clicking a suspicious frame drops you into the simulator at it. Those links are relative to this page and resolve against the simulator published beside the essay, so they work wherever this bundle is served from. Row headers link to column 1. Clicking the image opens that frame on its own. Frames were captured at 1000×625 and are published at 800×500 WebP to keep this page a reasonable download; every measurement on it was taken from the full-resolution capture before any crop, marker or downscale, and the hash link reopens the state at full fidelity in the simulator, which is better evidence than any still. Captures were taken on their own separate port so they never touched your dev server.

Drone camera — overhead

Same six rows, same derived clocks, from above: this is where the along-crest peel (the zipper) reads instead. Tilted 15° off nadir since 2026-08-22 — it stood at 6.25°, near enough to straight down that a crest had no silhouette and the shot could only show the plan of the break, never its form.

row ↓ / clock →
1. still unbroken0% of the event · crest 0.00 Λ on
2. a quarter into the break25% of the event · crest 0.01 Λ on
3. halfway through the break50% of the event · crest 0.02 Λ on
4. three quarters through75% of the event · crest 0.04 Λ on
5. breaking — whitewater at the crest100% of the event · crest 0.05 Λ on
model-card day bundle · day=modelcard
H₀
1.50 m
T
14.0 s
tide
0.00 m
Δf
0.006 Hz
ξ
0.65
step
0.17 s
row spans
0.70 s
peel
+4 m
ring off line
≤ 2.70 m
cam drift
0.00 m
foam on the crest 0.01 → 0.01 → 0.25 → 0.54 → 0.79
OSM/NCEI Second Peak · u 668 m · reef -1.30 m NAVD88 · breaker anatomy · bed measured+reef · model-card day Cropped to 420×263 of the 1000×625 capture (2.38× closer), the same window in all five columns. Camera untouched — that is what the cam drift above still measures. #preset=secondpeak&cam=drone&day=modelcard&controls=0&q=high&speed=0
frame at sim 47.45 s
t 47.5 s
unbroken · foam on the crest 0.01crest 3 m outside the linering at x = +2 m
env 1.00 crest 3.00 m
foammodel 0.87 (100% of stage) foampix 17.0%
#preset=secondpeak&cam=drone&day=modelcard&controls=0&q=high&speed=0&sim=47.45
frame at sim 47.62 s
t 47.6 s
unbroken · foam on the crest 0.01crest 3 m outside the linering at x = +2 m
env 1.00 crest 3.43 m
foammodel 0.87 (100% of stage) foampix 16.4%
#preset=secondpeak&cam=drone&day=modelcard&controls=0&q=high&speed=0&sim=47.62
frame at sim 47.8 s
t 47.8 s
starting to break · foam on the crest 0.25crest 2 m outside the linering at x = +2 m
env 1.00 crest 3.85 m
foammodel 0.87 (100% of stage) foampix 17.4%
#preset=secondpeak&cam=drone&day=modelcard&controls=0&q=high&speed=0&sim=47.8
frame at sim 47.97 s
t 48.0 s
starting to break · foam on the crest 0.54crest 1 m outside the linering at x = +6 m
env 1.00 crest 4.19 m
foammodel 0.87 (100% of stage) foampix 17.6%
#preset=secondpeak&cam=drone&day=modelcard&controls=0&q=high&speed=0&sim=47.97
frame at sim 48.15 s
t 48.1 s
breaking · foam on the crest 0.79crest on the linering at x = +6 m
env 1.00 crest 4.49 m
foammodel 0.87 (100% of stage) foampix 17.8%
#preset=secondpeak&cam=drone&day=modelcard&controls=0&q=high&speed=0&sim=48.15
big clean groundswell bundle · day=big
H₀
2.50 m
T
17.0 s
tide
-0.20 m
Δf
0.004 Hz
ξ
0.65
step
0.11 s
row spans
0.42 s
peel
+0 m
ring off line
≤ 6.11 m
cam drift
0.00 m
foam on the crest 0.02 → 0.03 → 0.17 → 0.71 → 0.85
OSM/NCEI Second Peak · u 668 m · reef -1.30 m NAVD88 · breaker anatomy · bed measured+reef · big clean groundswell Cropped to 420×263 of the 1000×625 capture (2.38× closer), the same window in all five columns. Camera untouched — that is what the cam drift above still measures. #preset=secondpeak&cam=drone&day=big&controls=0&q=high&speed=0
frame at sim 44.15 s
t 44.1 s
unbroken · foam on the crest 0.02crest 4 m outside the linering at x = -1 m
env 1.00 crest 7.73 m
foammodel 0.97 (100% of stage) foampix 13.7%
#preset=secondpeak&cam=drone&day=big&controls=0&q=high&speed=0&sim=44.15
frame at sim 44.26 s
t 44.3 s
unbroken · foam on the crest 0.03crest 3 m outside the linering at x = -1 m
env 1.00 crest 7.98 m
foammodel 0.97 (100% of stage) foampix 13.5%
#preset=secondpeak&cam=drone&day=big&controls=0&q=high&speed=0&sim=44.26
frame at sim 44.36 s
t 44.4 s
starting to break · foam on the crest 0.17crest 3 m outside the linering at x = -1 m
env 1.00 crest 8.19 m
foammodel 0.97 (100% of stage) foampix 13.7%
#preset=secondpeak&cam=drone&day=big&controls=0&q=high&speed=0&sim=44.36
frame at sim 44.47 s
t 44.5 s
breaking · foam on the crest 0.71crest 2 m outside the linering at x = -1 m
env 1.00 crest 8.38 m
foammodel 0.97 (100% of stage) foampix 14.4%
#preset=secondpeak&cam=drone&day=big&controls=0&q=high&speed=0&sim=44.47
frame at sim 44.58 s
t 44.6 s
breaking · foam on the crest 0.85crest 1 m outside the linering at x = -1 m
env 1.00 crest 8.54 m
foammodel 0.97 (100% of stage) foampix 14.8%
#preset=secondpeak&cam=drone&day=big&controls=0&q=high&speed=0&sim=44.58

What the five clocks are (2026-08-20). They are that row's own break event, measured rather than chosen: from the last clock at which the model's whitewater at the tracked crest is still at or below 0.02, to the first at which it reaches 0.60, at the takeoff station — the place where a crest first meets the break line, so where this wave's break starts and its peel begins. The window is found by seeding on the crest nearest the line at the set anchor and walking time backwards until that crest is unbroken and forwards until it is whitewater. Walking backwards is the whole point: the clock this sheet needs is before it breaks, and there is no forward-only way to find that from an anchor that already sits mid-break.

Two things this sheet got wrong first, both recorded because the numbers hid them. (1) The span. The columns used to run one full wave period at T/5. A crest advances exactly one crest spacing per period, so column 5 sat 0.80 of a spacing on — 0.20 from where the next wave upstream had been in column 1, and the sheet very nearly aliased back onto itself. That ratio is not site-specific: advance ÷ spacing = Δt ÷ T cancels the local wavelength, so it was 0.80 on every row, at every camera, at every H₀. It is visible in the sheet's own reads — laid out over a full T the tracked breakpoint goes 36 / 56 / 88 / 4 / 36 at day=small, back on column 1's station to the metre. (2) The anchor, which survived the first fix. The row was anchored on the argmax of crest height at the break line. A wave is tallest at the line, and at the line it is already breaking — so the anchor sat at or after break onset by construction, and the frames showed it: an established whitewater band up-line of the mark in column 1, foam 0.87 in the first cell, and the tracked wave then decaying across the row (crest 2.98 → 2.71 m) rather than breaking. A correct measurement of the wrong instant.

Why the foam is read at the crest and not at a station. At a point break the line is more or less permanently breaking somewhere, so whitewater at a fixed point in the water never goes quiet between waves — it is the bore left by the wave before. That is how a column whose own wave had not broken could report foam 0.87. Every acceptance number on this sheet is read on the tracked crest, so it is this wave's state and nobody else's.

What is on this sheet. Six wave sizes at preset=secondpeak. Four rows are the curated condition bundles from web-three/js/conditions.js: a bundle moves H₀, period, tide and Δf together, the way a real day does. Two rows are h0= only, which moves swell height against the site card's own T = 15 s, tide and Δf — so the pair isolates height from period and tide. Read the bundle rows for "what does this day look like" and the h0 rows for "what does size alone do".

What was left out. day=pulse and day=stormy (the bank has six days; the four sampled span the surf-worthy range and stormy is marked good: false). One site only — cross-site comparison is the other sheet's job. Second Peak is ξ 0.65, so it spills more than it plunges; Sewers (ξ 1.15) is the site to open if you are judging throw specifically. Tide is not swept independently: it rides inside the bundles, which is why the bundle and h0 rows are not directly comparable and are labelled as such.

The foam number can be occluded. It samples the projected attachment corridor whether or not terrain sits in front of it, so a low camera behind a bluff reads low foam for a wave that is breaking fine. The model-side pocket/brk readings in the JSON are the check; a cell is only flagged FLAT when both the pixels and the model say nothing broke.

The marker can be wrong, and it will say so. The line is the tracked crest and the ring is the point on it with the largest pocket — this wave's own breaking point. Both are model reads, not annotations, so each carries a number. ring off line in the row header is the worst distance, over the columns where this wave is actually at the line, between the ring and the break line it belongs on. And where no part of the wave has reached the line yet there is no ring at all, rather than a ring on the previous wave: an empty column 1 is what still unbroken looks like, and it is the single clearest tell that the anchor is now in the right place.

A row that does not break says so. Acceptance is measured per row and printed on it: whitewater at the tracked crest must start pre-break and end broken. Two rows cannot pass, and both are H₀ 0.70 m at a site whose measured peel floor is 1.08 m — day=small peaks at 0.193 and h0=0.7 at 0.341, against 0.858–0.890 for every row that breaks. Their headers say crest intact → crest gone instead of promising a break, their five clocks span the crest indicator's own collapse, and they are kept out of the published set. The gap between 0.35 and 0.85 is empty across the whole bank, which is why the 0.60 line sits in it.

Stills in a known order. Each cell states a position at a known clock; nothing on this page claims a direction of travel from the pictures. The row header's peel is the difference between two model reads at two model-derived clocks, not a motion measured off the frames — which is the distinction MEASUREMENT_LESSONS 1 is about. The frames are ordered by the model's own clock, so the ordering is established rather than inferred, and that is the only reason a reader may read the five states as a sequence.

Where this sheet says n/a, and why. Two measures go blank at Privates, and only at Privates, because that site's coastline defeats the contour fit (16.5 m RMS) and it runs on a synthetic stage rather than a surveyed seabed. foampix needs a baked break line to project a corridor onto; with no measured bed there is no baked line, so there is nothing to sample and the cell reads n/a instead of sampling an arbitrary band of pixels. ceilM, the depth-limited crest ceiling in the JSON, is null for the same root cause one step further on: the site runs u_depthMix = 0, the seabed sampler is a 1×1 stand-in, and the depth the shader reads back is the storage format's quantization floor rather than a seabed — so γh never binds and the "ceiling" would be 1.878·H₀ wearing a depth limit's name. Privates has no measured bed and therefore no ceiling to be over. An earlier sheet did print that number and produced a headline defect ("2.2× over its ceiling") that was entirely an artifact of dividing by it. Everything else on the Privates row — crest, foammodel, the set envelope — is measured the same way as every other site and is directly comparable.

Method caveat. Column 1 of every row is captured from a cold load at its own #sim=; columns 2–5 advance the clock with __pointbreak.setSim() plus two rAF ticks — the mode scripts/capture_temporal.mjs validated against per-frame reloads. Each cell's hash reproduces its frame either way.

Camera drift, recorded not assumed. The #aim cameras frame the baked line's action centroid and smooth over ~6 s of sim time, so advancing the clock could move the instrument between columns of one row (MEASUREMENT_LESSONS 11: an instrument that frames itself on the signal is not a fixed instrument). Each row header carries the largest camera displacement between any two of its five columns. A row with a non-trivial drift there is comparing slightly different windows, and its numbers should be read accordingly. The camera legitimately does differ between rows — the break line moves with H₀ — so only within-row drift is a concern.

Stills, not motion. Nothing here is a motion read off the pictures (MEASUREMENT_LESSONS 1). The positions in the cells and the peel in the row headers are model reads at clocks the model itself derived, and the frames are ordered by that clock, so the ordering is established rather than inferred — which is precisely what lesson 1's third failure ("peel direction is rightward, confirmed across two frames") lacked. Read as pictures, these frames still cannot tell you which way the peel runs.

← all QA sheets in this build · raw measurements (JSON) — the same provenance block, machine-readable · the essay · the repo · commit f3b6bde