What it is
ATS River Pulse brings the USGS National Water Information System into the browser, live. Pick any of the 50 states and every active stream gauge appears on a topographic basemap with its latest instantaneous discharge (cfs) and gage height (ft), parsed from a single NWIS instantaneous-values request — roughly 350 to 1,500 gauges per state (Colorado, the smoke-test state, loads 368). Gauges are classified by flow within the state using quantile buckets over the log-flow range: dry gray through low cyan to high blue, with the top decile in bold ATS blue.
Selecting any gauge opens a glass detail card with its live readings, a seven-day hydrograph drawn on a canvas (minimum/maximum labels and a gold "now" marker), and a deep link to the official USGS station page. A stats row tracks the gauge count, the state's highest-flow site and the state total in cfs — and everything silently refreshes every five minutes without disturbing the selection.
The application is zero-credential end to end: the basemap is anonymous, the USGS water services API is public and CORS-enabled, and there is no server anywhere — the browser talks to the federal API directly.
The experience
Choose a state and within seconds its river network lights up — hundreds of classed dots over a topographic base, the biggest rivers unmistakable in bold blue. The interface is a working hydrologist's view with a consumer finish:
- One picker, fifty states. Each state loads from a single instantaneous-values request; a search box filters the gauge list by name, and the list caps at 600 rendered items so California-scale states never tank the DOM — the map always shows every gauge.
- Flow classification that adapts to the state. Quantile buckets computed over the state's log-flow range make the symbology meaningful in both Arizona and Louisiana: dry gray, low cyan, high blue, and the top decile in bold #0061FF. The selected gauge carries a gold ring.
- A real hydrograph. The detail card draws seven days of 15-minute discharge (~672 points) with min/max labels and a gold marker at the newest reading, alongside live cfs and gage height and a launch link to the USGS station page.
- Live, quietly. Values refresh every five minutes in the background — the selected site is re-found by ID in the new payload, its card numbers update, and the gold ring stays put. A UTC clock in the chrome keeps the timestamps honest.
The data — where it comes from
One live federal API and one anonymous basemap — no key, no server, no cost.
| Source | What it provides | Refresh | Cost | Attribution |
|---|---|---|---|---|
| USGS NWIS Instantaneous Values API (waterservices.usgs.gov/nwis/iv — live) | Latest discharge (parameter 00060) and gage height (00065) for every active gauge in a state; 7-day series for the hydrograph | Live at load and every 5 minutes; hydrograph on demand; upstream readings at 15-minute cadence | Free | U.S. Geological Survey — provisional data subject to revision |
| Esri topo-vector basemap (anonymous) | Light topographic basemap under the gauge 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
- WaterML's quirks are handled — verifiably. The NWIS JSON returns one timeSeries entry per site × parameter: Colorado returned 713 series for 368 unique sites, so parsing groups by site code. Point values are strings ("7.30") and are converted explicitly. And the -999999 no-data sentinel really does appear as a latest reading (15 of 713 Colorado series) — it is filtered, or "dry" sites would display -999,999 cfs.
- Multi-method sites and missing parameters. Some series carry multiple values[] blocks — multi-method sites such as a backup gage-height sensor. The state load takes the freshest usable point across all blocks; the hydrograph takes the longest block. Not every site reports both parameters (Colorado: 351 of 368 had discharge, 347 gage height), so every reading on a gauge is nullable and renders "—" when absent.
- Timestamps compared as instants, not strings. NWIS dateTime values carry local UTC offsets ("2026-07-11T22:30:00.000-04:00") that differ across a state — freshness comparisons go through Date.parse, never string order.
- Refresh that respects the user. Request-sequence guards prevent a slow state fetch from overwriting a newer one after rapid state switching, and the five-minute silent refresh re-binds the selection by site ID rather than clobbering it.
- Small traps, recorded. Importing @arcgis/core/Map.js shadows the global Map — imported as EsriMap because the app uses real Map<string, number> buckets. view.goTo() needs a real Extent instance, not a plain object. Setting canvas.width resets the 2D transform, so the devicePixelRatio scale is reapplied on every draw. SDK 5.x attribution and Calcite 5 asset-path conventions follow the portfolio baseline.