A browser-based tide clock that tracks where the tide actually is — not where a gear thinks it should be.
A mechanical tide clock has one gear that turns once every 24 hours and 50 minutes — the average lunar day. Set it to a local high tide, and it approximates the next one. It's elegant. It's also wrong.
Tides don't run on averages. Local geography shapes them: inlet width, basin depth, river mouth backpressure. The difference between the ocean beach and the intracoastal waterway a quarter-mile away can be two hours and several feet. A gear can't know that. A fixed calibration can't know that. Live data can.
On load, the clock requests your GPS coordinates and passes them through a coordinate transformation pipeline — WGS-84 geodetic to ECEF to ENU — to compute true metre-accurate distances to every NOAA tide prediction station in the network. That's roughly 3,400 stations.
The two nearest stations within 30km are fetched and their predictions blended using inverse-distance weighting with wrap-safe angle averaging to handle the 0°/360° clock face boundary. An exponential moving average smoother prevents the hand from jumping as blend weights shift.
The key insight is that NOAA's subordinate station network already encodes the hydraulic corrections for inlets, back bays, tidal rivers, and the ICW — inlet lag, basin attenuation, height ratio — for every named water body in the US. By ranking stations by physical distance rather than ID or region, a user standing on the ICW gets ICW-corrected predictions automatically. A user on the ocean beach gets ocean predictions. No hardcoded offsets. No geometry classification logic. The same code works on the Chesapeake, Puget Sound, San Francisco Bay, or anywhere else NOAA has subordinate coverage.
The clock is built on spatial-utils, a custom open-source JavaScript navigation library for WGS-84 geodetic math. It's the geometric foundation that makes location-aware prediction possible without region-specific logic.
spatial-utils is inlined into the clock rather than imported as a module to avoid ES module CORS restrictions when opening as a local file. Its role is purely geometric: it answers which station is relevant to your position. NOAA answers what the tide is doing at that station. The division of responsibility is intentional.
The clock face is a custom aerial photograph rendered as a tiny planet — a stereographic projection of a 360° equirectangular image that wraps the full panorama into a circular world, ocean at the center and the horizon curling around the edge.
The hand completes one rotation every 24 hours and 50 minutes. Alongside it: current tide phase (rising or falling), countdown to next high and next low, current water height in feet above MLLW, a water level arc on the rim showing fill proportional to height, a spring/neap indicator driven by moon phase math, and a station blend card showing which NOAA stations are active, their type, distance, and blend weight.
Started as a single widget demonstration of how a tide clock works mechanically. Expanded into a full location-aware prediction system through an iterative build covering coordinate math, NOAA API integration, multi-station blending, ICW/ocean differential, geolocation edge cases on macOS, Vercel deployment, and iframe embedding.
The geolocation flow uses the Permissions API to detect blocked access before firing getCurrentPosition, retries with enableHighAccuracy: false on macOS Core Location failures, and surfaces OS-level fix instructions when the browser permission is granted but the system is blocking. Data refreshes every 6 hours.
The narrative arc is simple: decorative approximation → location-aware precision → universally applicable without hardcoding.