Weather.Co is now free for every pilot — always free for CFIs and flight schools.
Personal instrument · macOS · in active development

Anvil
The radar bench I built for myself.

A pro-grade macOS weather-analysis workstation — GR2Analyst / AWIPS-class. Gate-accurate GPU Level II radar, a self-decoded severe-weather product suite, dual-pol validated against the NWS, forecaster tools, and a 3D volume viewer.

Not on the App Store. Not for sale. This is the studio's own instrument — the page exists because it's the most technically ambitious thing here, and worth showing.

PlatformmacOS 26 · Apple Silicon
ClassGR2Analyst / AWIPS
EngineSwiftUI + WebGL + Py-ART
DistributionPersonal · not for sale

A workstation for interrogating the atmosphere — not a forecast you're handed.

Most weather apps tell you the answer. Anvil is the opposite: a bench for pulling apart raw radar and forecast data and forming your own judgment, the way GR2Analyst, GRLevelX, and AWIPS do for operational meteorologists. It decodes the same NEXRAD Level II/III volumes the National Weather Service works from, renders them gate-accurately on the GPU, and layers on the severe-weather products a chaser actually reads.

Gate-accurate Level II
Super-res reflectivity, velocity, ZDR, CC, SW rendered from the full Level II volume on a WebGL custom layer — crisp at any zoom, with a multi-frame animation loop.
Severe-weather suite
Composite, VIL, VIL-density, echo tops, azimuthal shear, MESH hail, rain rate, divergence, storm-relative velocity — all self-decoded from Level II, products that exist as no public tile anywhere.
Dual-pol, validated
ZDR / CC / KDP / HCA hydrometeor classification — with an accuracy harness that scores every product against the operational NWS Level III as ground truth.
Forecaster tools
VAD wind profile + hodograph, graphical Skew-T with parcel theory, severe parameters (shear / SRH / STP / SCP), a live national hazards feed, storm tracking with timing cones.
3D volume viewer
The full Cartesian volume as translucent voxels and nested isosurfaces, with HCA classification, GLM lightning bolts, a cross-section curtain, and an orbit MP4 export.
Point interrogation
Right-click anywhere for a point forecast, ensemble plume, CAPE, a radar gate column, nearest METAR, and storm-threat ETA. Pin it to a watch list.
Gate-accurate, GPU-rendered, super-res

The same volume the NWS works from — rendered per-pixel on the GPU.

Single-site reflectivity, velocity, ZDR, CC, and spectrum width render from the full NEXRAD Level II volume (decoded with Py-ART) through a Mapbox WebGL custom layer: a fragment shader computes range and azimuth per pixel and samples a polar data texture — 16-bit values packed into the texture, an in-shader 256-entry color lookup, mask-aware bilinear smoothing in both azimuth and range so data never bleeds across echo edges.

The payoff is the thing the public radar tiles can't give you: crisp at any zoom level, no pixelation, with real per-elevation tilt selection and value-under-cursor readout. An animation loop streams N Level II volumes into N GPU textures and swaps between them.

Level II super-res WebGL custom layer In-shader bilinear Per-tilt N0/N1/N2/N3 Cursor value readout Animation loop .pal palettes Placefiles

GR2-class products, self-decoded from the raw volume.

These are the products that exist as no free tile anywhere — so Anvil computes them itself from the Level II volume in a local Python sidecar (Py-ART + MetPy), then renders them through the same GPU polar layer.

Composite · VIL · echo tops
Every reflectivity sweep resampled to a 1 km grid with beam heights → column max, integrated VIL (Greene-Clark), VIL density, and 18 dBZ echo-top interpolation.
Azimuthal shear
Region-based velocity dealiasing → ∂V/∂(arc) rotation field on a diverging ramp. Sign-verified: cyclonic Northern-Hemisphere rotation reads positive (red).
MESH hail · rain rate
Witt SHI maximum-expected-hail-size with a temperature-weighted profile; Marshall-Palmer rain rate. ~0 for rain, lights up on hail cores.
Storm-relative velocity
Dealiased velocity minus the storm-motion radial (mean 850–300 hPa wind), so the mesocyclone couplet pops against the background flow.
Rotation + storm cells
Azimuthal-shear maxima gated by co-located strong echo → rotation markers; a storm-attribute table samples dBZ / VIL / ET / hail-proxy / rotation per cell, click-to-fly.
ZDR columns · hail signature
ZDR-column depth above the melting layer (an updraft signature) and an Aydin hail-difference product — both decoded from the dual-pol volume.

Validated against the NWS, not just rendered.

A radar product that looks right isn't necessarily right. Anvil ships an accuracy harness (validate.py) that resamples each self-decoded Level II product onto a shared grid and scores it against the operational NWS Level III as ground truth — coverage, correlation, bias, RMSE. Findings at KLSX:

0.86
Reflectivity corr
bias +0.26 dBZ
0.90
Velocity corr
bias +0.03 m/s
0.93
KDP corr
after the L3 fix
0.71
VIL corr
bias −1.14

The harness paid for itself: it caught a broken KDP retrieval (correlation 0.08 — the fast φDP-gradient fallback was garbage), which is why KDP and HCA now serve the operational NWS Level III product directly, accurate by construction. The honest pass is the point — if a product can't be trusted, it gets fixed or replaced.

Everything you need to build a forecast judgment.

Beyond the radar scope: the analysis tools an operational forecaster reaches for, wired to free public data.

VAD + hodograph
Velocity-azimuth-display wind profile from the Level II volume (Py-ART Browning), drawn as an SPC-style hodograph with altitude bands, storm motion, and 0–6 km bulk shear.
Skew-T + parcel theory
Graphical Skew-T from a 10-level forecast sounding with a lifted-parcel path, CAPE/CIN shading, and the LCL — plus a severe-parameter grid (shear, SRH, STP, SCP, EHI).
Live hazards feed
A national NWS warnings + watches list, sorted tornado → severe → flash-flood, tap-to-fly, with clickable warning polygons on the map and SPC convective outlooks + mesoscale discussions.
Storm tracking
RadarScope-style SCIT storm tracks with time-tick hashes, a forward timing cone projected great-circle to named towns, and a watch list with storm ETA along/cross-track.
Surface obs + mosaic
Live METAR surface-observation plots (temp / dew / wind), CONUS radar mosaic, GOES satellite basemap, and NDFD forecast layers with a forecast-hour scrubber.
Annotations + export
Geo-anchored polygons, arrows, freehand and text annotations; a presentation mode for clean broadcast radar; PNG and orbit-MP4 export. Built on the "Forge" design system.
The full volume, in three dimensions

The storm as a solid you can fly around.

A Three.js viewer renders the full Cartesian reflectivity grid as translucent voxels and smooth nested isosurfaces (30 / 45 / 55 dBZ shells via surface-nets). Switch the field to Z / V / ZDR / CC, or to HCA hydrometeor classification voxels. GLM satellite lightning bolts jag down through the volume, faded by age. A cross-section curtain cuts a vertical RHI wall through any azimuth.

Click-readout gives dBZ, height, range and azimuth at any voxel; a column profile plots dBZ-vs-height; the whole thing exports as an orbiting MP4. Geography (county and state lines, town-name labels) grounds it to the map below.

Voxels + isosurfaces Z / V / ZDR / CC / HCA GLM lightning Cross-section curtain Orbit MP4 export Bloom + sky gradient

A native shell, a web map engine, and a Python radar brain.

Three layers, each doing what it's best at. The front end is SwiftUI + AppKit. The map is Mapbox GL JS running in a WKWebView — seeded from the Weather.Co macOS engine — driven by a typed JS command channel. The radar decoding runs in a local Python FastAPI sidecar so MetPy / Py-ART do the heavy lifting off the main thread.

Front end
SwiftUI + AppKit · SwiftData (AnvilSchemaV1, versioned) · macOS 26, Apple Silicon
Map engine
Mapbox GL JS in a WKWebView · WebGL custom layer for the GPU radar · typed JS command channel with a two-stage queue
Radar sidecar
Python FastAPI (l3server.py) · Py-ART + MetPy · decodes NEXRAD Level II/III from AWS, serves polar textures + products over localhost
Data sources
All free, no key: NEXRAD L2/L3 (AWS) · IEM mosaic + GOES tiles · NWS RIDGE / alerts / SPC · NDFD · Open-Meteo · GOES-19 GLM lightning
Accuracy
validate.py — scores each L2 product vs operational NWS L3 (coverage / correlation / bias / RMSE)
Design system
"Forge" — charcoal RadarScope panels, forge-amber #F2960D accent, adaptive over-map chips
Build
XcodeGen vendored · com.kuhlman.anvil · not sandboxed (localhost sidecar) · not App Store
Status
v0.1 · in active development · personal use only
Why is a personal tool on the studio site? Because it's the most technically demanding thing the studio has built — a real-time GPU radar renderer, a self-decoding meteorology pipeline validated against the NWS, a 3D volume viewer — and that's worth showing even though you can't download it. It's also where techniques get proven before they reach the shipping apps: the radar engine was seeded from Weather.Co's Mac code, and what's learned here flows back. If you build weather software and want to compare notes, write me.

Built for one forecaster. Shown for everyone.

Anvil isn't for sale and isn't on the App Store — it's the studio's own radar bench. But the techniques, the architecture, and the lessons are all written up. Start with the lab guides, or read how the shipping apps are made.

Read the lab guides → See Weather.Co →