Live sky over Springfield, Missouri

LIVEMon 3:06 AM

The Living Ozarks

Hover to preview. Click to pin a card. Scrub through the night. Stars, planets, the Sun, the Moon, the river, and the hills all drawn from live data above Springfield.

Tonight above Springfield

computed from the time currently on the slider above. Drag it to preview any moment through tomorrow morning.

Sky schedule
Sunset7:57 PM
Civil dusk8:25 PM
Astro dusk9:33 PM
Astro dawn4:45 AM
Civil dawn5:54 AM
Sunrise6:22 AM
Planets up right now

No naked-eye planets above the horizon. Scrub the slider toward dusk to bring them back.

Positions from reduced Meeus ephemeris for Springfield, MO.

Meteor showers active
  • Eta Aquariidspeak 05/05
    dust from Halley's Comet, best in the pre-dawn hour
    ZHR ~50 at peak

Live inputs

observer: Springfield, MO · 37.2090° N, 93.2923° W · America/Chicago

Local clock
3:06:21 AM CDT
updates live
Julian day
2461157.83774
JD = unix_ms/86400000 + 2440587.5
Solar altitude
-32.14°
below horizon
Solar azimuth
33.62°
clockwise from north
Sunrise (Springfield)
Sunset (Springfield)
Lunar phase
WMO weather
- → clear
James River flow
USGS 07052500, Galena MO
Gauge height
above datum, stage height
7-day flow change
daily-mean discharge, week over week
Stargazing score
composite of moon, cloud, twilight, Bortle
Cloud cover tonight
mean, dusk through astronomical dawn
Astronomical twilight
hours of true dark
Today vs. normal (high)
1991–2020 daily normal
Record high for date
Springfield, 1940–present

Theory of operation

Solar position

Given a UTC timestamp, the canvas computes a Julian day JD = unix_ms / 86400000 + 2440587.5, then the number of days since the J2000.0 epoch: d = JD − 2451545.0. From dwe derive the Sun’s mean anomaly, mean longitude, ecliptic longitude, mean obliquity of the ecliptic, right ascension, and declination, all of them polynomial approximations published by the USNO and NOAA with arc-minute accuracy over the 21st century.

The horizontal coordinates (altitude, azimuth) come from a rotation through the local hour angle, computed from Greenwich Mean Sidereal Time at the given UTC and the observer’s longitude:

sin(alt) = sin(φ) sin(δ) + cos(φ) cos(δ) cos(H)
cos(Az)  = (sin(δ) − sin(alt) sin(φ)) / (cos(alt) cos(φ))

where φ is the observer latitude, δ the solar declination, and Hthe hour angle. Good to about 0.5°, more than enough to place a 4.5-pixel sun on a 40-pixel mark.

Sunrise, sunset, solar noon

The site uses the NOAA spreadsheet formulation, which is a separable-day-of-year polynomial model of the equation of time and solar declination. For a given date, solar noon in minutes after UTC midnight is:

solarNoonMin = 720 − 4 × longitude − eqtime

Sunrise and sunset are solarNoon ∓ 4 × H₀, with H₀ the horizon hour angle after correction for atmospheric refraction and the finite solar disc (-0.833°). The date passed to this routine is the visitor’s current Springfield-localcalendar date, not UTC. Otherwise visitors hitting the site in the evening Central time (early next day UTC) would get tomorrow’s sunrise.

Lunar phase

Every night sky above the hills places the Moon at its actual current phase. The computation is a simple synodic-period model keyed to a known new-moon reference epoch (2000-01-06 18:14 UTC):

phase = ((JD − refJD) / 29.530588853) mod 1
illumination = (1 − cos(2π × phase)) / 2

The shadow disc inside the canvas is offset by (1 − 2 × illumination) × 5px, flipping direction across the full-moon midpoint to render waxing vs. waning. Drift between this simple model and the true Moon is under a day over a century. Plenty for a 4-pixel disc.

Weather

Every hour, the server asks Open-Meteo what Springfield looks like right now. Open-Meteo returns a WMO 4677 weather code which this site collapses into five palette buckets:

0, 1          → clear
2, 3          → clouds (partly cloudy, overcast)
45, 48        → mist (fog, rime)
51–67, 80–82  → rain (drizzle, rain, showers)
71–77, 85–86  → snow
95–99         → rain (thunderstorms collapse here)

Responses are cached in Upstash Redis for an hour, which trims the outgoing request rate to ~24/day regardless of traffic. Open-Meteo is free, requires no API key, and is fully open-source, picked deliberately so the whole pipeline stays inspectable end to end.

Tonight's stargazing score

Springfield is a Bortle class 5 sky: suburban, with a visible Milky Way on the right night but plenty of scattered urban glow. Whether tonight is one of those right nights comes down to four knobs, and the score is just their product:

moonScore     = 1 − 0.85 × moonIlluminated        // full moon floors 0.15
cloudScore    = 1 − meanCloudCoverPct / 100
bortleScore   = 1 − (bortleClass − 1) / 8          // Bortle 5 → 0.5
twilightScore = min(1, astronomicalTwilightHours / 5)

score         = round(10 × moonScore × cloudScore × bortleScore × twilightScore, 1)

The cloud term is a 12-hour mean of Open-Meteo's cloud_cover hourly field, averaged across the window from civil dusk (sun altitude -6°) to astronomical dawn the next morning (sun altitude -18°). The astronomical-dawn math is the same NOAA spreadsheet formulation used for sunrise and sunset, just with the zenith-angle parameter bumped from 90.833° to 108°.

Because all four terms live in [0, 1], the composite does too; a one-decimal 0 to 10 scale comes from a final rounding step. Above the canvas, the score is the number on the stat card. Inside the canvas, it becomes skyClarity: the count of rendered stars and their alpha ceiling both scale with it, so a murky-moonlit sky on the site means a murky-moonlit sky in the disc.

Climate anomaly (today vs. the 30-year normal)

Sky color shifts subtly rose when Springfield is running warmer than usual for the date, and subtly cool when it’s running under. Both sides come from one number: the difference between today’s forecast high and the climatological mean high for this calendar date over the WMO-standard 1991 to 2020 window.

# 85 years of daily high/low from Open-Meteo Archive, once.
GET https://archive-api.open-meteo.com/v1/archive
    ?latitude=37.209&longitude=-93.2923
    &start_date=1940-01-01&end_date=2024-12-31
    &daily=temperature_2m_max,temperature_2m_min
    &temperature_unit=fahrenheit&timezone=America%2FChicago

# Group by MM-DD:
#   CLIMATE_NORMALS[mmdd] = mean high/low over 1991-2020
#   CLIMATE_RECORDS[mmdd] = max-high + min-low with source year, 1940-present
# Commit both to src/lib/climate-normals.ts.

At request time, the /api/climate-anomalyroute only fetches today’s forecast high and low (one Open-Meteo call). It subtracts from the static normal, compares to the static record, and caches for 24 hours keyed by Springfield-local MM-DD. By midnight central, the key rolls and a fresh normal gets loaded without any upstream change.

The canvas tint is a clamp: magnitude = min(1, |anomalyF| / 15). Warm anomalies mix skyTop and skyBottom toward a warm rose (#E8A87C), cool anomalies toward a cool slate-blue (#7FA8C9), maxing out at 25% mix when the anomaly reaches 15°F. It’s deliberately too subtle to notice on a calm day, and unmistakable on a freak one.

Ozark hydrology (James River)

The blue line slipping through the hills is the James River at Galena, Missouri (USGS gauge 07052500, about 40 miles downstream of Springfield). Two calls go out of a Next.js route handler on each cache miss, in parallel:

# discharge (code 00060) and gauge height (code 00065), latest
GET https://waterservices.usgs.gov/nwis/iv/?sites=07052500
    &format=json&parameterCd=00060,00065&siteStatus=active

# daily-mean discharge, 7 days back, for the trend percent
GET https://waterservices.usgs.gov/nwis/dv/?sites=07052500
    &format=json&parameterCd=00060&period=P7D

Each discharge reading lands in cfs (cubic feet per second). To say whether today is high or low for the season, the card compares the live value against a static day-of-year median pulled from 30 years of daily values (1994 through 2023), grouped by MM-DD and committed to src/lib/river-normals.ts. That file is regenerated by a one-off script, not re-fetched at runtime, so the route stays fast and the USGS service stays unstressed.

In the disc itself, flow is normalized to the [0, 1] range with a simple clamp against 0.2 × median (typical low) and 3 × median (near bankfull). Opacity and stroke weight scale with that number, and an outer glow turns on above 0.55 to read as flood. Below the Ozark ridges, the water gets wider and brighter when the river is running.

Rendering

~500 lines of TypeScript. Zero runtime dependencies added: pure HTML5 Canvas 2D, no WebGL, no animation library, no particle engine. Draw loop throttles at ~40fps (25ms minimum frame time). Per-session randomness is a mulberry32 PRNG seeded from sessionStorage, so every visit draws a slightly different set of stars and data markers.

The render loop pauses via IntersectionObserver when the logo leaves the viewport, and the browser pauses requestAnimationFrame itself when the tab is hidden. Total payload delta vs. the static SVG it replaced: ~4 KB gzipped.

Accessibility: the source-of-truth SVG lockup stays in the DOM with role="img" and an aria-label. The canvas is aria-hidden and carries no semantic weight. Both the OS-level prefers-reduced-motion: reducemedia query and the site’s own “Reduce motion” toggle unmount the canvas entirely and reveal the static SVG.

Prior art & references

None of this math is new. The references below are the shoulders this page is standing on. Worth a click if any of the above caught your interest.

  • suncalc· Vladimir Agafonkin

    The JavaScript library for sun/moon math. ~4k stars, battle-tested, tiny. Does everything this page does (and more). If you're building anything production-grade that needs solar or lunar positions, start here.

  • Astronomy Engine· Don Cross

    High-accuracy TypeScript port of VSOP87 / NOVAS. Arc-second-level precision if you need it. Overkill for a 40-pixel canvas, but the reference when you need real ephemeris data.

  • The original government spreadsheet this site's sunrise/sunset math comes from. NOAA publishes the coefficients and the derivation; this is canonical.

  • Open-Meteo· Patrick Zippenfenig · Bremen

    Free, open-source weather API. No key, no rate-limit drama, sensible WMO codes. The weather tint you see in the logo is pulling from them right now.

  • USGS Water Services· U.S. Geological Survey

    Public REST + JSON interface to every USGS stream gauge in the country. Zero auth, zero rate limits worth mentioning, and enough history to do proper day-of-year statistics. The James River flow card, the normalized-flow bar, and the river line drifting through the hills all come from this feed.

  • Bortle Dark-Sky Scale· John E. Bortle / Sky & Telescope (2001)

    The nine-class system amateur astronomers use to rate a site's sky-darkness. Class 1 is a desert with no light dome; Class 9 is inner-city. Springfield sits at class 5: suburban, Milky Way on a good night, Pleiades easy, M31 direct. This page treats that class as a static input and combines it with live moon + cloud to get tonight's number.

  • Open-Meteo Archive API· Patrick Zippenfenig

    Daily weather for any point on Earth going back to 1940, served as JSON with no key. The 30-year climate normals and the all-time date records that drive the anomaly card were computed from this API exactly once and committed to the repo, so runtime traffic stays at one tiny call per day for today's observation.

  • mulberry32· bryc

    The per-session PRNG. 15 lines, period 2^32, passes PractRand up to 2^26 bytes. Perfect for ‘deterministic from a seed, different-looking for each seed’ use cases.

  • Next.js 16· Vercel

    The framework running all of this. App Router, React 19, edge-adjacent route handlers, Turbopack turned off for deliberate reasons.

$ head -n 1 /src/components/LivingOzarksCanvas.tsx

"use client";

If you want something like this on your own site, let’s talk.