← OBSERVATORY
The Science of Stargazing

How we decide
whether tonight is worth it

A plain guide to the five things that actually matter when you look up — and the full algorithm, for the curious.

How it works

Your worst factor sets a ceiling.
Everything else can only lower it.

If you're in the middle of a bright city, a crystal-clear sky with a new moon still won't let you see the Milky Way. The light pollution sets a ceiling — the most you could ever see from that spot — and no amount of good weather can raise it.

Five things combine to predict tonight's sky. Four of them — light pollution, clouds, darkness, and precipitation — act as ceilings. Any one of them alone can sink your score. The rest — the moon, transparency, and seeing — act as reducers: they can pull the score down from the ceiling, but never raise it.

Scores are computed using Liebig's law of the minimum: score = ceiling × degradation. The ceiling is the minimum over the hard-limiting factors (LP, cloud, darkness, precipitation). Degradation is the weighted geometric mean of soft factors (moon, transparency, seeing). A final modifier stack (fog risk, wind) multiplies in.

ceiling    = min(LP, cloud, darkness, 1 − precip_prob)
degradation = moon^w_m × transparency^w_t × seeing^w_s
score       = ceiling × degradation × modifier

Each component is normalized to [0, 1]. Ceilings are absolute — a score of 0.26 for light pollution guarantees the final score ≤ 0.26. The model replaces the previous weighted-geometric-mean implementation, which could under-weight the light pollution factor and produce scores well above the physical ceiling.

The five factors

What tonight's sky is made of

Light pollution Ceiling
A city glow is a wall you can't see past.
Every city pushes light into the sky. That glow makes faint stars disappear — they're still there, but your eyes can no longer tell them apart from the lit-up background. Light pollution is usually the single biggest factor in what you'll see tonight. It's also the only one you can truly change, by driving somewhere darker.

Source: DJ Lorenz 2024 North America atlas (15480×8160 palette-indexed PNG, equirectangular). A pixel lookup yields a Bortle band, which maps to a sky brightness in mpsas (magnitudes per square arcsecond) via the Light Pollution Index model.

def light_pollution_score(mpsas):
    linear = clamp((mpsas - 16) / 6, 0, 1)
    return linear ** 1.3    # perceptual curve
# mpsas ≈ 17.5  (Bortle 7)   →  0.17
# mpsas ≈ 19    (Bortle 5)   →  0.41
# mpsas ≈ 21    (Bortle 3)   →  0.79
# mpsas ≈ 22    (Bortle 1)   →  1.00

The ^1.3 curve reflects that sky brightness is logarithmic — each 1-mpsas step is ≈ 2.5× brighter sky — so inner-city bands need to degrade faster than a linear scale would suggest. High elevation slightly raises this ceiling (thinner atmosphere), capped at 1.0 so it can never exceed the pristine value.

Clouds Ceiling
Anything between you and a star blocks it.
Cloud cover is the simplest physical cap: if the sky is overcast, no amount of dark site, new moon, or steady air gets you a star. We blend three forecast services because any one of them can miss an approaching cloud bank.

Sources: 7Timer (astronomy-specific), NWS (US-only, localized), Open-Meteo (layer-aware: low/mid/high clouds). Blended into a unified 1–9 band.

cloud_score(cc) = ((9 − cc) / 8) ** 1.5

Blend rule: when sources strongly disagree (spread > 0.33 on the normalized scale), the worst source wins — honest pessimism beats a charitable average that splits the difference between "clear" and "overcast."

Darkness Ceiling
The sun must be far below the horizon.
You can't see stars during the day, and twilight hides the faintest ones for an hour or two after sunset. Full astronomical darkness — which is when the algorithm gives you the highest possible score for this factor — happens when the sun is more than 18° below the horizon.

Source: PyEphem computes the sun's altitude for the observer's location and time. The darkness_quality field smoothly ramps from 0 (sun above horizon) through 0.5 (civil/nautical twilight) to 1.0 (sun below −18°, astronomical dark).

darkness_ceiling = darkness_quality   # 0.0 (daylight) → 1.0 (full dark)

As a ceiling, daytime drives the whole score to zero instantly — no smooth multiplier can "leak" through.

Precipitation Ceiling
Rain and snow end the night.
If rain is falling you can't observe, and if rain is likely you're going to get wet waiting for a gap. We let even a moderate chance of rain cap the score — because in practice, a 60% chance is enough to discourage setting up a telescope.

Sources: NWS precipitation chance, Open-Meteo precipitation probability, 7Timer precipitation type.

if active precip detected:         score = 0.010     # hard gate
else:
    precip_ceiling = clamp(1 − best_precip_prob/100, 0, 1)
# 60% rain → ceiling 0.40
# 80% rain → ceiling 0.20
The moon Degrader
A bright lamp in the sky.
A full moon washes out the dim stars. A new moon is invisible. It matters most when the moon is actually in your sky — a full moon below the horizon barely affects things. Our algorithm uses the moon's altitude, not just its phase.
moon_score_stargazing(frac, impact):
    base = max(0.05, (1 − frac) ** 0.7)
    # Altitude-aware: moon below horizon → impact=0 → score → 1.0
    return base + (1 − base) × (1 − impact)

moon_score_astrophotography(frac, impact):
    base = (1 − frac) ** 1.2    # hard zero at full moon
    return base + (1 − base) × (1 − impact)

Astrophotography is more moon-sensitive (bright sky ruins long exposures). Naked-eye observation tolerates moonlight slightly better, so the stargazing floor is 0.05, not 0.

Transparency Degrader
How clean the air is between you and space.
Smoke, haze, dust, humidity — they all scatter starlight before it reaches you. On a "clear" summer night after a wildfire, transparency is still poor even if the forecast says zero clouds.

Sources: 7Timer transparency band, Open-Meteo surface visibility, PM2.5/AOD from smoke data.

transparency_score(t)   = (8 − t) / 7
ts = ts × 0.55 + visibility_score × 0.45
ts = ts × (1 − smoke_penalty)
Seeing Degrader
How still the air is.
Stars twinkle because the air between you and them is turbulent. You won't notice this with your naked eye, but through a telescope it's the difference between a sharp Saturn and a wobbly smear. It matters more for astrophotography than for stargazing.
seeing_score(s) = (8 − s) / 7
# seeing 1 (<0.5″) → 1.0     (exceptional)
# seeing 4          → 0.57    (typical good)
# seeing 8 (>2.5″) → 0.0     (unusable for fine detail)
Classification

Ceilings vs. degraders

A factor is a ceiling if, alone and at its worst, it can make stars invisible. A factor is a degrader if it can pull quality down within whatever ceiling the hard factors set, but can't, on its own, prevent observation.

FactorRoleWorst-case effect
Light pollutionCeilingCity sky — faint stars invisible no matter the weather
Cloud coverCeilingOvercast — nothing visible through clouds
DarknessCeilingDaylight — sun drowns out all stars
PrecipitationCeilingRain/snow — observation impossible
MoonDegraderFull moon — washes out dim stars, bright ones remain
TransparencyDegraderHeavy haze — dims stars, doesn't hide them
SeeingDegraderTurbulent air — blurs detail, doesn't hide stars
Modifier stack

After ceiling × degradation

A small stack of soft modifiers multiplies the ceiling×degradation product. These represent practical observing conditions that don't fit cleanly into ceiling or degrader roles.

AI adjustment

One last pass for context

An LLM (Gemini 3 Flash via OpenRouter) sees all the raw data, the computed scores, and the physical ceiling, then returns an adjusted score with a short reasoning string. It catches context the algorithm can't: an urban light dome outside the Bortle zone, a cold front clearing a humid night, a known dark-sky site's microclimate.

Three guardrails keep it honest:

Data sources

Where every number comes from

SourceProvidesCoverage
USNOMoon illumination fraction (daily)Global
7TimerAstronomy-specific cloud/seeing/transparency (72h, 3h intervals)Global
NWSReal-time cloud cover, precip, dewpoint, windUS only
Open-MeteoLayered clouds (low/mid/high), visibility, precip probabilityGlobal
PyEphemSun/moon altitude, twilight times, dark windowGlobal (local computation)
DJ Lorenz 2024Light pollution atlas (Bortle zones, mpsas)North America
Open-Meteo ElevationElevation in meters (atmospheric thickness)Global
Open-Meteo Air QualityPM2.5, PM10, aerosol optical depthGlobal
Known limitations

Where the forecast can be wrong

"The stars you can see tonight are the ones
your ceiling allows, minus what the weather takes."