Why this exists
I keep a 1987 Bayliner 3288 in a slip at the Port of Everett. Deciding whether a given day is worth going out on meant opening four or five tabs: tide predictions in one, currents in another, the coastal zone forecast in a third, a model in a fourth, and then doing the arithmetic in my head about whether the wind would be against the ebb through the middle of the afternoon.
None of those sources is wrong. They are just written for different readers, and none of them answers the question I actually have, which is: is this a good day to take my boat out, on the water I take it out on?
So this site does the tab-opening and the arithmetic once, every few hours, and puts the answer on a page. It is opinionated on purpose. The limits it scores against are mine — a comfort line for a 36-year-old flybridge cruiser and the people aboard her, not a safety limit for anybody else.
The checklists came later, and for the same reason. They were written for my own boat log, and they turned out to be the part other people ask for, so they are published too — tickable on a phone, and made to be printed and kept in a sleeve by the helm. The log book they were written for stays private; the lists are not worth keeping to myself.
What it actually does
Every run is the same four steps, per marine area:
- FetchNOAA tide and current predictions for the area's station, the NWS coastal zone forecast text, the MET Norway model for the area's lat/lon, and a WDFW rules feed.
- ScoreEach day gets wind, sea state, current and daylight scored against that area's configured bands. The worst factor rules the day — a day is not "good" on average, it is good only if nothing in it is bad.
- RateThe day gets one of five ratings, a plain-language label, and a note naming the limiting factor, so the rating always says why.
- PublishThe page is written to a temporary file and renamed over the live one, so a reader never sees a half-written page. The previous version is kept beside it.
It is deliberately dull. There is no database, no login, no queue, no JavaScript framework, and nothing to keep running between builds. Each page is a single file of HTML with its styles inlined, written to disk and served by nginx. If every moving part of this stopped at once, the last pages it wrote would go on being served, correctly, indefinitely.
What it refuses to do
A forecast site that is confidently wrong is worse than one that is plainly unavailable, so the generator would rather publish nothing than publish something it cannot stand behind:
- An area with no tides and no model does not publish. Two dead sources is not a forecast.
- A rating the index has no label for is a fault in this code, not a weather condition. It is reported as a fault rather than dressed up as "no forecast".
- If any area raises an exception, the index is not rewritten at all — an index full of "Unavailable" over six working pages turns a bug into an outage.
- A page that comes out suspiciously short, or without a closing tag, is never installed.
The last good page stays up in every one of those cases. Stale and labeled with its build time beats fresh and wrong.
Where the numbers come from
Four sources, each doing the one thing it is best at. How often each of them changes is a property of the source, not of this site — the times below are approximate, and are what the site is built around rather than a promise made by anyone publishing the data.
| Source | What it gives | How often it changes |
|---|---|---|
| NOAA CO-OPS | Tide heights and current slack/max times for the area's reference station | Published in advance; effectively static |
| NWS coastal zone forecast | The human forecast text for PZZ134 (Admiralty Inlet) and PZZ135 (Puget Sound), including wave language | About four times a day, and on amendment |
| MET Norway locationforecast | Hourly wind speed and gusts at the area's coordinates | Roughly hourly |
| WDFW rules feed | Season and emergency-rule notices | Irregular — whenever the department posts one |
Tide and current times are predictions: astronomy, computed years ahead, not measurements. They do not know about a storm surge. Wind and waves come from a mix of a human-written zone forecast and a numerical model, and where the two disagree the site takes the more pessimistic reading.
How often this updates
The build runs on a small virtual machine in Amazon's Oregon region and nowhere else. It is not triggered by anyone visiting, and no laptop needs to be awake for it — the box fetches, scores, renders and publishes entirely on its own.
A missed run is caught up rather than skipped: if the machine is asleep or rebooting when the timer should have fired, it runs at the next start rather than waiting for the following one. Each run is spread by up to two minutes so six areas do not hit the same source at the same instant.
| Rebuild | every hour, on the hour |
|---|---|
| Browser cache | 5 minutes — a rebuild is visible within that |
What the version number means
Every page on this site carries a version in its top corner — the one you clicked to get here. It names the exact build of the generator that wrote the page you are looking at, and it links to the changelog, which lists every release this site has ever had.
The site shipped as 1.0.0 on 15 September 2026. From there the numbering is the ordinary one and means what you would expect: 1.0.x for a fix or a small change, which is most releases; 1.x.0 for a redesign, a new section or a new capability; 2.0.0 for a rebuild, which is not planned and would have to be earned.
Before 1.0 it was a plain serial — v42 followed v41 and told you nothing else. Sixty of those builds are on the changelog, kept exactly as they were written, because a version scheme that made the history look tidier than it was would be its own kind of lie.
The number moves when new code is installed, not when the site rebuilds. The site rebuilds itself on a timer and those rebuilds do not change it: same code, new weather. What changes it is a release, at which point the changelog gets an entry naming what moved.
A release does not reach the site by being uploaded. The build is staged in full on the server first, the new code's own test suite is run there against that staged copy, and only a suite that passes gets swapped into place. A build whose tests fail cannot go live at all — the live site is left untouched and the previous install stays on disk for a one-command rollback. That is why the version and the test count are worth printing together: the number tells you which code wrote the page, and the count tells you what it had to survive to get here.
For nerds
Constraints, chosen up front and kept:
- Standard library only. No pip install, no virtualenv, no dependency that can rot. The generator runs on whatever Python the OS shipped.
- No build step and no bundler. The CSS is a file that gets inlined; there is nothing to compile.
- No JavaScript for anything that matters. The scripts a page carries are all conveniences: applying your saved theme before the page paints, the theme toggle itself, the ticks on the checklists, a staleness warning that can only ever appear, and on a trip page a button that copies the float plan. Turn JavaScript off and every page still reads, and still prints — you lose the conveniences, never the content. (This used to say “two small scripts” and count them. It stopped being true, which is what counting things in prose does.)
- No third-party anything at runtime. No analytics script, no CDN, no fonts fetched per visitor, no trackers. Nothing on the page phones anyone else.
- The landing page reloads itself. Every five and a half minutes, so the reading from the boat's own weather station stays current for anyone who leaves it open on a laptop. Only that page: trip plans and checklists are read for minutes at a time, and pulling those out from under someone would be worse than a stale number.
- Themes in CSS custom properties — light, dark, and a red-on-black night mode for reading on the boat after dark without wrecking your night vision.
- Atomic writes. Every page is written to a temp file and renamed into place; the previous version is kept beside it as
index.prev.html.
Deploys go as a versioned tarball. The installer stages the whole thing, runs the new build's own test suite on the server, and only swaps it into place if the suite passes — a build that fails its tests cannot reach the live site, and the previous install stays on disk for a one-command rollback.
The numbers in the table below are read off this build while it is being written. Nothing there is typed into the page by hand.
| Last build took | 11.8 seconds |
|---|---|
| Marine areas | 6 of 6 built on the last run |
| Scored ahead | 14 days per area |
| Generator | 27 Python files, 16,646 lines, standard library only |
| Test suite | 1130+ assertions, run on the server before an install is allowed to proceed |
| Page weight | 74 KB the front page, 223 KB an area page |
| Version | 1.12.5 2026-10-11 |
What this is not
It is not an official forecast, and it is not a substitute for one. There is no Coast Guard wind limit for recreational boats; the one official threshold is the National Weather Service Small Craft Advisory at 20–33 kt sustained, and the limits used here sit at roughly half that. A day this site calls rough is a day I would not enjoy, not a day that is necessarily dangerous — and a day it calls good can still be the wrong day for your boat, your crew, or your plan.
Season and shellfish information is verified by hand for Marine Area 8-2 only. Everywhere else it is a convenience copy of a feed. Confirm with WDFW and the Department of Health before you put a pot in the water or a fish in the box.
Check the real forecast. Then check the sky.
Colophon
The name and the artwork come from a flyer Jess made for her DJ nights at the Bluemoon in Seattle. The boat is being renamed TANGLEFOOT in her honor — why, and how we plan to do it — and the site got the name first.
Set in Archivo Narrow, Source Sans 3 and IBM Plex Mono. Written with the help of Claude, which also wrote most of this page — the opinions about boats are mine.