swellread-engine/2026.10.1
Everything the engine does, written down
One function, one set of weights, one code path shared by the pages, the REST API and the agent tools. If you disagree with a factor, you can change it here and the same change applies everywhere.
Weights
Nominal weights, renormalised over what exists
| Factor | Nominal weight | Dropped when |
|---|---|---|
| Swell power | 0.22 | no swell height or period from the source |
| Peel speed | 0.20 | no verified tide value for this break |
| Wind quality | 0.20 | wind speed, wind bearing or swell bearing missing |
| Tide window | 0.18 | no verified tide value for this break |
| Direction match | 0.12 | no swell bearing from the source |
| Period cleanliness | 0.08 | no swell height or period from the source |
The nominal weights sum to exactly 1. When a factor has no real data behind it, its weight is removed and the rest are divided by what is left, so the score is always an average of comparable quantities rather than a quietly penalised score. A dropped factor shows as not scored in the interface, with the reason in its own words.
The six factors
What each one computes and why
Swell power weight 0.22
P = ⅟₁₆ · ρ · g · Hs² · Tp, with seawater density ρ = 1025 kg/m³. Score is P / 22, capped at 1. The 22 kW/m reference is a serious day: about 1.8 m at 11 s.
Peel speed weight 0.20
c = √(g · depth), depth being water over the take-off. Scored 1 between roughly 8.5 and 19 km/h, easing down on both sides — slower is mush, faster than about 25 km/h is unridable. This factor is dropped entirely when no tide value exists, because there is no depth to take the square root of.
Wind quality weight 0.20
Alignment = 1 − (1 + cos θ)/2, where θ is the angle between where the wind is from and where the swell is from: 1 when straight offshore, 0 when straight onshore. Multiplied by a speed envelope that peaks near 4 m/s and decays to 8% by 15.5 m/s.
Tide window weight 0.18
Each break type has a band of water over the take-off where it works. The score ramps up from 55% of the minimum safe depth to the bottom of the band, holds at 1 through the band, and ramps back down to 0 at twice the top of the band.
Direction match weight 0.12
1 − smoothstep(12°, 75°, Δ) where Δ is the angle between the swell bearing and the bearing the break peels cleanly from. Inside 12° is full marks; past 75° the swell has wrapped the headland.
Period cleanliness weight 0.08
smoothstep(6 s, 9.5 s, Tp) with the score eased off by up to 35% beyond 14 s, because a very long swell stands up steeper than a shallow reef can hold.
Physics
The two formulas that carry most of the weight
Green's law on the reef face
Hb = Hs₀ · (γ · tanβ)^¼
with γ = 0.78, the solitary-wave breaking index. The result is the height at which the swell trips on the face. It is then capped by the water you actually have, Hb = min(Hb, γ·depth), because a wave in water shallower than Hb/γ has already broken further out and is closing rather than standing up.
Shallow-water celerity
c = √(g · depth)
This is why tide matters more than most forecasts admit. The same 1.2 m swell over a 0.6 m take-off peels at about 10 km/h and is unridable; over 2 m it peels at 19 km/h and is the best thing on the coast. The drag control on every break page exists to make that relationship visible.
Gates
The hard rules that override the weighted sum
- flat (blocking): a swell below 0.35 m scores zero. There is nothing to break on, and a low number would be more honest than a low score.
- reef-exposed (blocking): when water over the take-off falls below the break type's minimum safe depth, the score is clamped to at most 0.08. This is a hazard, not a score.
- blown-out (×0.55): wind above 13.5 m/s.
- exposed (×0.7): a swell above 2.6 m, which is big enough to hold you under on a bad take-off.
- covering-fast (informational): a flooding tide above 0.7 m/h while the take-off is still shallow, which usually means it will keep standing up as you sit out there.
Tide windows by break type
| Break type | Ideal band | Minimum safe | Reads |
|---|---|---|---|
| reef pass | 0.9–2 m | 0.60 m | needs the pass covered |
| reef | 0.6–1.4 m | 0.40 m | wants the reef just covered |
| point | 0.5–1.2 m | 0.30 m | prefers a filling tide |
| beachbreak | 0.8–2.2 m | 0.30 m | works across the tide |
| river mouth | 0.7–1.6 m | 0.40 m | needs the bar swept |
Bands
Score to verdict
- ≥ 0.70 — Get in the water
- ≥ 0.50 — Worth the drive
- ≥ 0.30 — Marginal, one good hour at most
- < 0.30 — Stay home
- 0 — Nothing to surf
The day's window is the longest contiguous run of hours at or above 0.50, ties broken on mean score. If nothing clears 0.50 the app still returns the single best hour rather than shrugging.
Integrity
How the chain is built
seal_n = SHA-384( UTF-8(prevSeal) || canonicalJson(event_n) )
genesis prevSeal = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
Canonical JSON sorts object keys recursively by UTF-16 code unit, preserves array order, drops undefined and normalises non-finite numbers. seal and prevSeal are stored beside the event, never inside it, so the body that gets hashed is exactly the body in the table.
Deleting a session is a soft delete that keeps a tombstone, so the chain stays replayable forever. The replay tool recomputes every link and names the first broken one.
genesis = 000000000000000000000000…
Data
Sources, and what happens when they fail
NOAA CO-OPS
Hourly tide predictions, MLLW datum, keyless. The observed water_level product is deliberately not used for forecasts because it stops at "now".
Open-Meteo
Marine for swell height, period and bearing; Forecast for wind, air temperature, cloud and rain. Both keyless, both CC BY 4.0.
Failure
If every upstream fails, a sealed dated synthetic profile is shown and labelled fallback. It never replaces anything a user created, and it never invents a tide for a break with no station.
Break coordinates describe real coastlines. Peel orientation, reef slope and take-off depth are this project's editorial estimates and are labelled as such wherever they appear. Repository https://github.com/aniruddhaadak80/swellread.