What it is
ATS Sea State puts the state of the world ocean on one map. A bundled snapshot of 870 NOAA National Data Buoy Center stations (latest observations, 2026-07-12 02:30Z) colors every wave-reporting buoy from calm deep teal through cyan and amber to severe red; stations without wave sensors render as small gray dots. Click any buoy — or any patch of open water anywhere on Earth, in drop-a-probe mode — and the app fetches a live five-day wave forecast on the spot from the Open-Meteo Marine API, drawn on a hand-rolled canvas chart: hourly wave height in cyan, swell as a dashed white line, day gridlines, and dual meter/foot axes with the maximum labeled.
The architecture is a deliberate hybrid. NDBC serves no CORS headers, so live browser polling of the buoy network is impossible — the snapshot keeps 870 real stations on the map with zero infrastructure. Open-Meteo Marine is keyless and CORS-enabled, so the forecast layer is genuinely live, cached for fifteen minutes per point. The combination delivers a live-feeling global ocean product that still runs from static files with an empty .env.
A glass detail card presents snapshot readings in dual units — meters and feet, knots, °C and °F, hPa — and the panel offers buoy-ID search, region quick-links (North Atlantic, Pacific Northwest, Gulf, Hawaii, Great Lakes), a wave-height legend, a top-10 roughest-seas list and stats chips.
The experience
The oceans basemap sets the mood, and the buoys tell the story — colored clusters off both US coasts, the Great Lakes speckled, severe seas standing out in red. Two clicks cover the whole product:
- Click a buoy. The card opens with the station's snapshot readings in dual units, then the live five-day Open-Meteo Marine forecast draws beneath: wave height, swell, midnight gridlines, the maximum labeled. Buoys draw calm-first so severe seas stay on top and clickable; the selection gets a gold ring.
- Or click anywhere at sea. Missing a buoy drops a probe: the same live forecast for that exact point, anywhere on the world ocean. Clicking Kansas politely reports that the point looks like land.
- Navigate like a mariner. Buoy-ID search, one-tap region jumps, a wave-height legend matching the classification ramp, and a top-10 roughest-seas list — the snapshot's maximum is 3.5 m at buoy 22107 in the East China Sea.
- Honest loading states. A spinner while the forecast fetches, an error card with retry, and a friendly land explanation — the failure modes are designed, not accidental.
The data — where it comes from
A bundled NOAA snapshot, one live keyless forecast API, and an anonymous basemap.
| Source | What it provides | Refresh | Cost | Attribution |
|---|---|---|---|---|
| NOAA NDBC latest station observations (bundled ndbc-snapshot.json) | 870 buoys: position, significant wave height, dominant period, wind, air/sea temperature, pressure | Static snapshot, 2026-07-12 02:30Z — NDBC serves no CORS headers, so live browser polling is not possible | Free | NOAA National Data Buoy Center |
| Open-Meteo Marine API (live, keyless, CORS-enabled) | 5-day hourly forecast per point: wave height, period and direction, swell and wind-wave heights | Live on demand, cached 15 minutes per point | Free (CC BY 4.0) | Open-Meteo (open-meteo.com), CC BY 4.0 |
| Esri Oceans basemap (anonymous) | Bathymetric ocean basemap under the buoy layer | Vendor-managed | Free (no API key) | Esri attribution renders in-map automatically |
How it was assembled
The application is a fully static single-page app — no backend, no database, no server-side rendering. The stack: TypeScript, ArcGIS Maps SDK for JavaScript 5.0.x, Calcite Design System 5.0.x, and Vite 8.
Module map — the codebase is small and deliberate; each file owns one concern:
Key engineering decisions
- The snapshot + live hybrid is the architecture. NDBC has no CORS, so the buoy network ships as a bundled snapshot; Open-Meteo Marine is keyless and CORS-enabled, so forecasts are fetched live per point with a 15-minute cache. Each source is used the only way it can be — and the result still deploys as static files.
- The land guard. Open-Meteo Marine returns HTTP 200 with all-null hourly arrays for points over land. forecast.ts detects the all-null response and converts it into a friendly "looks like land" error with retry, instead of drawing an empty chart. A verified quirk on the same API: hourly.time carries 120 entries starting at today 00:00 UTC, with no seconds or zone suffix.
- Sparse sensors, honest rendering. Only 194 of the 870 buoys report wave height (156 report dominant period) — most stations are meteorology-only and render as small gray dots rather than being dropped or faked. Every field except ID and position is nullable, and the format helpers emit "—" for missing values.
- Map-edge geometry. The probe uses event.mapPoint.longitude, which keeps growing past ±180° as the user pans across world copies — it is normalized with ((lon + 540) % 360) − 180 before reaching the forecast API.
- Small traps, recorded. @arcgis/core/Map.js shadows the ES2020 Map (globalThis.Map is used for real hashmaps); the chart canvas is display:none while loading, so drawing waits until it is visible — otherwise clientWidth is 0 and the chart renders blank; a monotonic forecastSeq token invalidates in-flight fetches when the user moves on. SDK 5.x attribution and Calcite 5 asset-path and dialog conventions follow the portfolio baseline.