A free, static web app that shows which Berliner Bäder‑Betriebe pools are open right now, how much longer they'll be open, and how long it takes to get there by public transport from your location — on a transit‑focused map of Berlin.
- 🗺️ Map: Leaflet + ÖPNVKarte tiles (all U‑/S‑Bahn, tram and bus lines), free, no API key.
- 🚆 Directions: the free, keyless VBB transport REST API computes real public‑transport journey times, live in the browser.
- 🕒 Opening hours: scraped from
berlinerbaeder.de
by a GitHub Action every 30 minutes and committed as
data/pools.json. - 💸 Excludes pools with separate/extra entrance pricing (e.g. Strandbad Halensee) — see below.
- 🧭 Two modes: Jetzt (open now from your location) and Planen (pick a start, day & time).
- 🍽️ Optional destination ("Ziel danach"): add a place you're heading afterwards (e.g. a restaurant) and pools are ranked by total start → pool → destination transit time, so you find the one best-connected to both. Each leg gets its own Google Maps directions link.
- ⭐ Favourite places: save your common start/end points (home, work, a go-to restaurant) with the ☆ button and reuse them as start or destination in one tap. Stored locally in your browser.
- 🚶 Pools within walking distance show the walking time ("zu Fuß") instead of being marked "no route" — so a pool right next door reads as a short walk, not a dead end.
Everything runs client‑side, so it hosts for free on GitHub Pages.
berlinerbaeder.de ──(GitHub Action, every 30 min)──> scraper/scrape.mjs ──> data/pools.json
│
data/pools-meta.json (static: coords, official URL, category, exclusions) │
└──────────────────────────────┬────────────────────────────────────────┘
▼
Browser (index.html + js/*) merges the two,
filters to OPEN & non‑excluded pools, draws Leaflet
markers, and asks the VBB API for journey times.
Why a scheduled scrape instead of fetching on every page load? GitHub Pages only serves static files, and berlinerbaeder.de blocks direct cross‑origin browser requests (no CORS). The Action refreshes the data every 30 minutes and commits it; each commit redeploys Pages, so visitors always get near‑current hours without any backend.
The list of pools changes with the seasons (Sommerbäder vs. Hallenbäder) and renovations. If the
timetable lists a pool that data/pools-meta.json doesn't know yet, the scraper reads its address
from the pool's detail page, geocodes it via Nominatim and stores the position as autoMeta in
pools.json, so the pool still shows up (and the workflow run gets a warning). Adding the pool to
pools-meta.json then pins its exact position and category.
| Path | Purpose |
|---|---|
index.html, css/, js/ |
the static app (ES modules, no build step) |
data/pools.json |
generated opening hours (rolling window, currently 14 days) — committed by CI |
data/pools-meta.json |
hand‑curated: coordinates, official URL, category, excluded flag |
scraper/scrape.mjs |
fetch + parse (+ geocode pools missing from the metadata) → writes data/pools.json |
.github/workflows/scrape.yml |
cron job that runs the scraper and commits changes |
- Create a new GitHub repo and push this folder to the
mainbranch. - Settings → Pages → Build and deployment → Source: Deploy from a branch, branch
main, folder/ (root). - Settings → Actions → General → Workflow permissions: Read and write permissions (so the scrape job can commit the refreshed data).
- Open Actions → “Refresh opening hours” → Run workflow once to generate fresh data (it then runs automatically every 30 min).
- Your site is live at
https://<user>.github.io/<repo>/.
Scheduled GitHub Actions only run from the default branch and may be delayed a few minutes under load — fine for opening hours.
Per the request, pools whose entrance pricing differs from the standard BBB tariff are not shown. These were identified by cross‑checking the official tariff page: every pool listed there is shown; the ones absent (separately operated lidos) are excluded:
Sportbad Britz · Strandbad Friedrichshagen · Strandbad Grünau · Strandbad Halensee · Strandbad Jungfernheide · Strandbad Orankesee · Strandbad Plötzensee · Strandbad Wendenschloss
(Strandbad Wannsee is on the standard tariff, so it stays.) To change this, edit the
excluded flags in data/pools-meta.json.
# refresh the data once (Node 18+; uses a File polyfill for Node 18)
cd scraper && npm install && node scrape.mjs && cd ..
# serve the static site (ES modules need http://, not file://)
python3 -m http.server 8000
# open http://localhost:8000- Opening hours & official pool pages — Berliner Bäder‑Betriebe.
- Journeys & geocoding — VBB transport.rest by Jannis Redmann.
- Map tiles — ÖPNVKarte / memomaps.de (CC‑BY‑SA) and OpenStreetMap contributors.
- Step‑by‑step directions open in Google Maps (transit mode).
Pool coordinates were geocoded via OpenStreetMap Nominatim and stored statically; pools not in
pools-meta.json yet are geocoded the same way by the scraper.