Where are the tops on your route?
VFR pilots must stay 1,000 ft above the clouds they overfly — so live VFR traffic is a real-world cloud-top sensor. Enter a route; CloudTops reads the traffic, bounds the tops, and shows the NOAA model forecast next to it so you can judge both.
Airports and US VOR/NDBs, separated by spaces, commas, or hyphens —
e.g. KALB-GDM-KPWM or KPVD, PUT, KAUG. RNAV fixes not yet supported.
Generating
Route map
Chart: FAA VFR Sectional (official FAA tile service — check chart currency before flight). Zoom in to see airports; click one to add it to your route.
Altitude profile along-route distance vs. altitude MSL
What the data says
Where this might be wrong
How this works — the exact logic
- Your route becomes a corridor: great-circle legs between your waypoints, buffered ±20 NM. Plain spherical trigonometry.
- Live ADS-B returns every aircraft inside that corridor between 2,000 and 18,000 ft.
- Each aircraft is tagged likely-IFR or likely-VFR by fixed, published rules — additive weights, no model: squawking 1200 (−4), airline-format callsign (+3), callsign in an airline route database (+3), aircraft type — light piston/trainer (−2), high-performance single or light twin (−1, they're IFR-flown constantly) vs. jet/turboprop (+2), level at a hemispheric VFR altitude after altimeter correction (−2) or an IFR altitude (+1), N-number with nothing on file (−1), and weather context — IFR/LIFR conditions at the nearest reporting station (+2, MVFR +1), since VFR flight is less plausible in that weather. Score ≥ +2 → IFR, ≤ −2 → VFR, otherwise unknown. Every aircraft's applied rules are visible in its hover tooltip.
- Then the regulation does the math: VFR flight must stay 1,000 ft above any cloud it overflies (14 CFR 91.155). So every VFR aircraft cruising level above a METAR-reported layer base pins the tops: base ≤ tops ≤ lowest on-top VFR altitude − 1,000 ft. That inequality — not an opinion — is the "inferred tops" band on the profile.
- PIREPs outrank us: a pilot-reported top inside our bound raises confidence; one outside it replaces our bound. Direct observation wins over inference, always.
- The NOAA model layer (GFS cloud cover by pressure level) is drawn beside ours, never blended in — so you can see where the forecast and the real traffic disagree and judge for yourself.
Is this AI?
Not where it counts. The report on this page is produced by deterministic code — no machine learning, no neural networks, no language model interpreting weather. The same inputs always produce the same output, every number traces to a METAR, an ADS-B return, a PIREP, or a NOAA grid value you can inspect right here, and the rules are the six steps above. Where confidence is weak the report says so instead of guessing. We hold ourselves to that honestly the only way that means anything: every day we score our inferences against what pilots actually reported, in public, on the reliability dashboard. (The software was written with AI-assisted programming, but no AI runs in the data path.)
Built with
Python 3 server-side — standard library only, zero packages: the corridor math, classification, and inference are ~small, readable modules. Vanilla JavaScript client-side with Leaflet (BSD-2, self-hosted) for the map — the charts are hand-drawn SVG. Shipped as a small Docker container behind nginx.
Data sources & dependencies
- adsb.lol — live ADS-B traffic (primary source); community-run aggregator, open data (ODbL)
- OpenSky Network — fallback traffic source (research network)
- aviationweather.gov (NOAA Aviation Weather Center) — METARs, TAFs, PIREPs, and airport data
- Open-Meteo — serves the NOAA GFS pressure-level cloud cover behind the model layer
- FAA VFR Sectional tile service (FAA Aeronautical Information Services) — the chart itself; also published as raster charts on the 56-day cycle
- OurAirports — public-domain airport and navaid database (how idents like C37 resolve)
- adsbdb — community callsign→route database (airline-route evidence in the classifier)
- Leaflet — the only third-party code in the product, vendored locally