How it works

How the Sounding Explorer works

Where the data comes from, what each panel shows, and how every value is ranked against the station's own archive. Everything runs client-side in your browser.

← Open the explorer

1. Data sources

A sounding can arrive from four places. The app tries them in order of freshness, and the status line names the source you're looking at.

SourceCoverageLatencyResolution
SPC observedUSFastest — ~1 h post-synoptic~110–200 levels
IEM RAOBUS + Canada~1 h behind SPC~180–250 levels
UW mirrorGlobal6×/day cronFull BUFR
NOAA IGRA v2Global, 1905–now~1–2 day lagMandatory + significant

A balloon takes 90 minutes to two hours to ascend, so no source has a complete 12Z sounding before roughly 13:30 UTC — that is the floor on "real-time", not a delay in the app. The University of Wyoming feed sends no CORS headers, so a scheduled job mirrors ~600 stations six times a day onto a data branch the browser can read; complete UTC days are also bundled into a permanent, growing archive (the 📅 date picker reaches it).

2. The physics engine

All analysis is SHARPlib compiled to WebAssembly (~250 KB) running in your tab — no backend. The same compute_sounding() entry point is compiled natively for the offline climatology builder, so a live value and its historical percentile always come from identical code.

3. What the panels show

Skew-T

Three parcel traces: long-dashed white = most-unstable (MU), dashed orange = surface-based (SB), dotted cyan = 100-hPa mixed-layer (ML). When the most-unstable air is at the surface, MU and SB coincide and draw as one line. The right-edge ticks mark each parcel's LCL/LFC/EL, and the Parcels table's CAPE/CINH rows belong to the same three parcels.

Parcels table — CAPE and ECAPE

ECAPE (Peters et al. 2023) is CAPE with entrainment: updraft dilution is derived from the storm-relative wind and shear, so it uses the kinematic profile as well as the thermodynamic one. The table shows CAPE and ECAPE per parcel plus the implied updraft speeds √(2·CAPE) and √(2·ECAPE) — the gap between them is the buoyancy entrainment takes away. The ECAPE/CAPE ratio can exceed 100%: the formulation nets a storm-relative kinetic-energy gain against the entrainment loss, so a ratio above 1 means vigorous inflow more than pays for the mixing.

Moist static energy

h is the moist static energy at each level and h* what h would be if the level were saturated. A boundary-layer parcel conserves its h, and it is buoyant wherever its h exceeds the environment's h* — that is the layer generating the CAPE. The summary rows give boundary-layer h, the minimum h* aloft, their difference (negative ⇒ conditionally unstable), column-integrated MSE, and the 12-hour change in both.

Hodograph

Height-coloured trace with Bunkers left/right movers. The effective inflow layer — the air the storm can actually ingest — is highlighted directly on the curve, and effective SRH is computed over that layer rather than a fixed 0–1 km. Corfidi MCS vectors are reported in the Kinematics table, not drawn. The effective-layer Bunkers vector is drawn only when it differs from the fixed-layer one by more than 3 kt.

Derived levels

Computed directly from the profile: WMO tropopause (lowest level where the lapse rate drops to ≤ 2 K/km and stays there on average through the next 2 km), cold point (column temperature minimum — the definition that matters in the deep tropics), PBL top (first level whose virtual potential temperature exceeds the surface value by 0.5 K), and column relative humidity (∫q dp ⁄ ∫qsat dp, with a 700–500 hPa mid-level mean).

Winter

Fog and cloud layers

The fog probabilities are empirical: three logistic models (fog, ceiling ≤ 1,000 ft, ceiling ≤ 3,000 ft) trained on 87,568 sounding–METAR pairs from 14 co-located stations over 2006–2025, using features from the lowest 3 km (peak low-level RH is the dominant one). Cross-validated AUC is ~0.9 both year-blocked and station-held-out, which is why it runs globally. The probabilities describe conditions at sounding time — they are not a forecast. The panel also reports the classic FSI screen, the bulk Richardson number (is the surface layer decoupled?), and dew-vs-frost. The cloud-layer detector uses height-dependent RH thresholds (over ice below freezing) calibrated against ~49k ceilometer-verified bases; boxes are coloured by phase and the dashed teal band is the DGZ. Thin or broken cloud between sounding levels can be missed.

4. Climatology — where a value ranks

Each station's entire IGRA period of record is analysed once, offline, and reduced to percentile breakpoints and record extremes at 73 day-of-year anchors, each pooling a ±10-day window across every year on record. The indices ranked: precipitable water, 850/700/500 hPa temperature and dewpoint, 500 hPa height, 1000–500 hPa thickness, freezing level, wet-bulb zero, 850/250 hPa wind speed, ECAPE and SHIP.

What you seeWhat it means
Nothing — plain textBetween the 10th and 90th percentile: ordinary for this station on this day of year.
Red tint + P93 Above the 90th percentile; the tint ramps within the tail (P91 faint, P99 loud).
Blue tint + P4 Below the 10th percentile, shaded the same way.
★ with a yearAn outright station record for this time of year, with the year the previous mark was set.

Nothing is flagged unless the index has at least 30 samples in the window. Soundings qualify for the climatology only with a real surface dewpoint, ascent to 400 hPa, and dewpoint on ≥60% of levels — gaps are never filled with invented data, and calm no-CAPE days count as ECAPE zeros rather than being skipped, so the percentiles describe all days, not just convective ones.

The 📊 Climatology button opens the annual curve for any ranked variable: shaded 25–75% and 10–90% bands, the median, red/blue record envelopes, and a gold dot marking the sounding on display.

5. The record watch

Six times a day, every station's newest sounding is compared against its climatology. The 🏆 map features stations whose latest launch broke a station record (all-time or for the time of year), with near-record (P95+) stations as context dots. Each label carries its sounding's launch time — records persist until the station launches again, so labels older than 48 h fade to unlabeled stars rather than pinning stale text to the map. The drill-down table under a station lists what stands and what this sounding broke.

6. The code

The repository is a plain GitHub Pages site — what you're reading and what runs in your browser are the same files. Browse the repository →

FileWhat it does
skewt_wasm.cpp The single source of truth for the physics: a C++ wrapper over SHARPlib, compiled to WebAssembly for the browser and natively for the climatology.
app.js The entire client: source fallback and parsers, the WASM bridge, and every chart drawn on canvas.
build_climo.py Per-station climatology from the full IGRA record, with the quality gates.
flag_anomalies.py The record watch: compares each new sounding to its station's climatology.
mirror_soundings.py · archive_days.py Mirrors the UW feed and bundles complete UTC days into the permanent archive.
skewt-data.yml The scheduled job: mirror → archive → flag → publish, six times a day.

The data lives on orphan branches served CORS-open: skewt-data (latest soundings), skewt-archive (day bundles) and skewt-climo (per-station climatology).

7. Limitations

← Open the explorer