Technical · How it works All guides ↗

How Sunmaxxing tracks the sun

A short tour of the data, the math, and the architecture behind Berlin's live sun and shadow map.

Sunmaxxing is a map of roughly 2,000 Berlin terraces, rooftops, and beer gardens, each one annotated with whether it's currently in the sun. Everything on the map updates every minute, with no app install and no account required. Here's what's under the hood.

The question

For any given terrace in Berlin, right now, we want to answer: is the sun hitting this place? That question sounds simple, but it has three independent parts:

Each of these has a reliable public data source. The interesting engineering is combining them quickly enough that the whole map renders in a couple of seconds on a phone.

1. Where the sun is — SunCalc

The easiest part. For any (latitude, longitude, timestamp), the sun's position in the sky is a closed-form astronomical calculation. We use the open-source suncalc JavaScript library to get the sun's altitude (how high above the horizon) and azimuth (which compass direction) at 1-minute granularity. No network calls, no API keys — it's pure math.

2. What's in the way — OSM buildings + shadow projection

This is where the real work lives. We pull every building footprint in Berlin from OpenStreetMap — about 40,000 polygons with associated height tags (where present; we fall back to a conservative average where not). For each terrace on the map, for each minute of the day, we project shadows from every nearby building and ask: does any shadow cover this terrace's centroid?

The calculation per terrace per minute is roughly:

sunVector = suncalc(lat, lon, time)
if sunVector.altitude <= 0: return "night"
for building in nearbyBuildings(terrace, radius):
  shadowPolygon = extrudeShadow(building, sunVector)
  if shadowPolygon.contains(terrace.point):
    return "in shadow"
return "in sun"

Nearby is roughly 500m — a taller building that far away can still cast a shadow onto a terrace when the sun is low. We build a spatial index (R-tree) on building footprints so the nearby-building lookup is O(log n) instead of O(n).

3. Are there clouds — Open-Meteo

Even with clear geometric sun, 90% cloud cover means you're not getting golden hour. We fetch Berlin's current and hourly cloud cover from Open-Meteo, a free weather API. The map combines the two signals: a terrace with geometric sun and <30% clouds gets the full sunny pin; with heavier cover it gets a muted "partly cloudy" state rather than a false-positive golden dot.

The performance problem

Computing shadows on 2,000 terraces × 1,440 minutes per day × ~100 nearby buildings each = roughly 300 million polygon-inside tests per day, if we were naive. That's obviously too much to do in the browser.

The trick: it's boringly deterministic. Given the buildings, the terraces, and a day's timestamp, tomorrow's shadow map is exactly the same set of computations as today's — nothing stochastic. So we don't compute at runtime. We pre-compute.

Nightly bake

A GitHub Action runs at midnight Berlin time, computes every terrace's sun windows for the next day, and writes a single static JSON bundle:

windows-today.json
  {
    "klunkerkranich": [
      ["06:12", "13:45"],   // sunny window #1
      ["14:02", "19:38"]    // sunny window #2
    ],
    ...
  }

The client loads this bundle (under 2 MB gzipped) and does a constant-time lookup: at this minute, is this terrace inside one of its sun windows? No geometry, no math — just a range check. This is what lets the map stay interactive on a mid-range phone.

Weather stays live

The cloud-cover layer is the only thing we can't bake — weather changes hour to hour. So the static bundle carries geometric sun, and a second tiny API call gets fresh cloud data. The client layers them together in the browser.

The stack

What it isn't

A few things Sunmaxxing is deliberately not:

Why it's free

Sunmaxxing is a personal project, not a startup. There's no subscription, no ad, no account, no tracking cookie. It runs on free tiers of Cloudflare Pages, GitHub Actions, and the public APIs listed above. If it gets popular enough to hit a free-tier limit, we'll figure that out then.