weather-meta 📍
About

About weather-meta

weather-meta blends forecasts from six independent weather services into one view, showing you what most of them agree on and how much they disagree. Every source is named. Every chip can be toggled. The math is simple enough to explain.

Why this exists

Open any weather app and you get one forecast from one company, presented as if it were the weather itself. But every forecast is a model, and different services use different models, parameters, and proprietary blends. They disagree all the time, especially on precipitation, and the disagreement carries information you don't see when you only consult one of them.

weather-meta puts those disagreements on the screen. You see the median of the services you trust, the range across them, and a colour-coded agreement label telling you when the sources are tightly clustered versus far apart. If you don't trust AccuWeather, toggle its chip off and watch the median recompute. If you only trust government data, narrow to NWS. The page is built around the assumption that transparency about uncertainty is more useful than false precision.

How it works

Every numeric field — temperature, feels-like, wind, precipitation probability, precipitation amount — is the median across whichever sources you currently trust, alongside the min/max range and an agreement label ( high, medium, low). The thresholds are field-specific: 3°F spread is still “high” agreement for temperature, but 3 percentage points spread on rain probability counts as the same. Wind direction is averaged with a circular mean so the 360°/0° wrap doesn't break things. Conditions text uses the most common label across sources.

The median isn't a flat one-vote-per-source though. NWS publishes the NOAA model output that Pirate Weather is built on, so if you have both chips on, NOAA would quietly be counted twice. We avoid that with a cluster-aware median: sources that share a parent model vote as one block, then the median is taken across the blocks. The NOAA cluster (NWS + Pirate Weather) casts one combined vote, Open-Meteo (a multi-model blend, heavy on ECMWF) casts one, and AccuWeather, Visual Crossing, and Tomorrow.io each cast one — five effective voices, even though six chips are lit up.

Toggling chips is purely a re-rendering of cached source data — no upstream API calls fire when you flip a source on or off. Forecasts are cached on a 30-minute TTL (60 minutes for AccuWeather, since its free tier is 50 calls/day). NWS severe weather alerts cache for 5 minutes. Both use stale-while-revalidate: if the upstream is slow or down, you still get the last good response.

How each search helps the data get better

The clusters above are based on what each service is built on, not on observed behaviour. Two services in different clusters might still move in lockstep in practice, and the only way to find out is to watch them over time. So every forecast we serve also logs two things in the background:

Both of these are quiet background jobs — they happen as a side-effect of you loading the page, with no extra requests. The more locations and times we cover, the richer the dataset that v1.6 will use to refine the blend. So loading the forecast for your actual home, your parents' town, the trailhead you're driving to — those are all useful samples we wouldn't otherwise have.

Two honest caveats: the “ground truth” we compare against is the nearest ASOS/AWOS weather station, which can be 5–20 miles from your exact location. And precipitation probability (POP) has no direct observation — it's a probability, not an observation — so it isn't scored here. Everything else is.

The sources

Each chip carries a small ⓘ with the genealogy — what the service is actually built on:

What we deliberately skipped

Where the radar comes from

Animated precipitation radar via RainViewer, rendered on Leaflet with a CARTO Dark Matter basemap. Both are free for personal/non-commercial use, which fits the current friends-tier rollout. If this ever opens up wider, that licensing gets re-evaluated.

Privacy and tracking

Your IP address is used to guess your starting location via a self-hosted MaxMind GeoLite2 database — no external geolocation service is called. The lookup result is cached for 24 hours per IP. City searches are routed through OpenStreetMap's Nominatim and cached for 30 days; US ZIP codes go through zippopotam.us so we get the USPS-preferred place name. There's no analytics, no third-party tracker, no account, and no cross-site state. Your trusted-source selections live in your browser's localStorage; nothing about them leaves the page.

Tapping the crosshair button next to the search bar invokes the W3C Geolocation API in your browser, not on our server. Your browser may consult its own location services (Google's on Chrome/Edge, Apple's on Safari) to answer — that's a choice between you and your browser vendor, outside our control. Only the coordinates you explicitly authorize ever reach weather-meta, and only for that single request.

What's next

v1.5 (this version) is the structural step: clusters prevent NOAA from quietly voting twice, and the two background datasets above start accumulating. v1.6 is where those datasets actually feed back into the blend — sources that historically track each other get partially down-weighted (they're less independent than the cluster grouping alone implies), and sources that historically get closer to the real observations get nudged up. We're not in a hurry: the more weeks of data behind those weights, the more honest they'll be.

Tech

Python 3.13 + FastAPI + Jinja2 + htmx. SQLite cache. Leaflet + RainViewer. Icons derived from Lucide (MIT). Hosted on a Proxmox LXC at home. Source at deliberately.erratic.network/chris/weather-meta.

Who made this?

Chris with the help of Claude Code — credit where it's due, yanno? Source on Forgejo.


Got a suggestion?

Source you'd like added, something off about the UI, idea we should consider — write it here. No account, no email. Just goes into a local table I check by hand.