docs + nginx: full README rewrite (config reference, caches, theme lighting, attribution), drop unused carto/topo proxies, drop obsolete compose version key
This commit is contained in:
1 parent
4369afd3ef
commit
3395ac8f8e
3 files changed
+183
-163
No files matched your search
@@ -1,43 +1,51 @@
|
||||
# Animated Background
|
||||
|
||||
A browser-based animated desktop background: a WebGL flight scene over
|
||||
real terrain, driven by the real sun, moon, stars, and live weather.
|
||||
A browser-based animated desktop background: a WebGL flight scene over real
|
||||
terrain, driven by the real sun, moon, stars and live weather. Designed to be
|
||||
dropped into an iframe or kiosk browser and left running.
|
||||
|
||||
## Features
|
||||
|
||||
- **WebGL flight scene** (`vendor/clouds3d.js`): raymarched cloud deck,
|
||||
3D terrain mesh from real elevation data (Copernicus DEM), satellite
|
||||
or street basemap, figure-eight flight path around the configured
|
||||
location with a vertical bob between **15 and 35 km** flight altitude.
|
||||
- **Real day/night cycle** driven by the actual sun and moon position
|
||||
(SunCalc) for the configured latitude/longitude — or an accelerated
|
||||
24 h simulation (`CONFIG.day`) for wall-decoration purposes.
|
||||
- **Realistic night sky**: ~8700 stars from the Yale Bright Star
|
||||
Catalogue (crisp GL points, size by magnitude, colour by temperature),
|
||||
rotated with the sidereal rate for the site's latitude, plus a Milky
|
||||
Way band and a phase-correct moon.
|
||||
- **Aircraft**: low-poly jets (CC0 model) cruising the region on curved
|
||||
routes between real airfields, rolling a random destination at every
|
||||
take-off - sun-lit silhouettes with contrails by day, strobing position
|
||||
lights by night (all parameters in `CONFIG.flights`).
|
||||
- **Night lights**: animated car lights along the real motorway/road
|
||||
network and pale-yellow settlement lights (OSM place nodes; bigger
|
||||
towns get more and brighter dots), optionally a NASA VIIRS night-lights
|
||||
satellite overlay. All faded in from dusk, density shaped by a
|
||||
rush-hour curve.
|
||||
- **Live weather** (Open-Meteo): current conditions + 4-day forecast
|
||||
widget; the weather code selects the scene's cloud density and colour
|
||||
mood (sunny / cloudy / stormy / night).
|
||||
- **Fully cached, offline-capable**: basemap tiles, elevation grid,
|
||||
Overpass road/place data, star catalogue and the last weather payload
|
||||
live permanently in IndexedDB. Once warm, the scene renders fully offline, and a 15-minute cycle transparently retries anything that previously failed. Merged road chains and the decoded settlement list are cached as well and only recomputed when an Overpass cell is actually re-fetched.tiles.expireDays`) can refresh
|
||||
basemap tiles periodically.
|
||||
- **Location-aware**: set `CONFIG.location.lat/lon`; everything
|
||||
(flight anchor, terrain tiles, sun/moon, weather, star rotation) is
|
||||
computed from it. If the location is omitted, browser geolocation is
|
||||
tried, falling back to the built-in default (London).
|
||||
- Units (°C/°F), six UI languages, CSS-swappable colour themes
|
||||
(Ash/Gruvbox/Nord), configurable clock format, optional debug panel.
|
||||
- **WebGL flight scene** (`vendor/clouds3d.js`): a 3D terrain mesh built from
|
||||
real elevation data (Copernicus DEM), a raymarched volumetric cloud deck,
|
||||
and a figure-eight flight path around the configured location with a
|
||||
vertical bob inside the 15-35 km flight band.
|
||||
- **One physical light model** shared by sky, clouds, contrails, aircraft and
|
||||
ground: direct beam through a Kasten-Young air mass with Rayleigh, ozone and
|
||||
aerosol extinction, diffuse skylight, moonlight, a starlight floor and aerial
|
||||
perspective. Sun and moon come from the real positions (SunCalc) for
|
||||
`CONFIG.location`, or from an accelerated 24 h simulation (`CONFIG.day`).
|
||||
- **Theme-tinted lighting**: each theme may pull the physical colours toward
|
||||
its own palette and set its own day/night exposure, without replacing the
|
||||
physics (`window.THEMES[...].lighting`).
|
||||
- **Shadows**: moving cloud shadows sampled from the same cloud field the sky
|
||||
renders, plus terrain self-shadowing over the DEM height field.
|
||||
- **Realistic night sky**: ~8700 stars from the Yale Bright Star Catalogue
|
||||
(points sized by magnitude, coloured by temperature), rotated at the sidereal
|
||||
rate for the site's latitude, a Milky Way band and a phase-correct moon.
|
||||
- **Aircraft**: low-poly jets (CC0 model) on curved routes between real
|
||||
airfields, with a destination rolled at every take-off, sun-lit silhouettes
|
||||
and contrails by day, strobing position lights by night.
|
||||
- **Night lights**: vehicle lights moving along the real road network (Overpass)
|
||||
with correct driving side per country, and settlement lights from OSM place
|
||||
nodes (bigger towns get more and brighter dots). Both fade in light by light
|
||||
across twilight instead of switching as one block.
|
||||
- **Live weather** (Open-Meteo): current conditions plus a 4-day forecast
|
||||
widget; the weather code selects the cloud coverage and the colour mood.
|
||||
- **Fully cached, offline-capable**: basemap tiles, the elevation grid, Overpass
|
||||
road/place cells, the merged road chains and the decoded settlement list live
|
||||
permanently in IndexedDB; the star catalogue is inlined. Once warm, the scene
|
||||
renders fully offline (only weather is fetched live), and a 15-minute cycle
|
||||
transparently retries anything that previously failed.
|
||||
- **Pixel-art rendering option**: `CONFIG.renderScale` renders the 3D scene at
|
||||
a fraction of the display size and upscales it in exact integer blocks with
|
||||
`image-rendering: pixelated`, while the DOM UI stays crisp.
|
||||
- **Location-aware**: set `CONFIG.location.lat/lon` and everything (flight
|
||||
anchor, terrain tiles, sun/moon, weather, star rotation, traffic side) is
|
||||
derived from it. Without a location, browser geolocation is tried, falling
|
||||
back to the built-in default.
|
||||
- Six UI languages, CSS-swappable colour themes, configurable clock format,
|
||||
optional debug panel.
|
||||
|
||||
## Run with Docker (recommended)
|
||||
|
||||
@@ -47,112 +55,122 @@ docker compose up --build
|
||||
|
||||
Then open <http://localhost:58881/>.
|
||||
|
||||
The bundled nginx config (`default.conf`) is more than a file server: it
|
||||
provides CORS proxies for the tile sources (`/esri/`, `/carto/`,
|
||||
`/terrarium/`) that the scene needs for basemap and elevation data.
|
||||
A plain static server without these proxies (e.g.
|
||||
`python3 -m http.server 58881`) will run the UI but terrain and map
|
||||
tiles will fail to load.
|
||||
The bundled nginx config (`default.conf`) is more than a file server:
|
||||
|
||||
- CORS proxies for the basemap styles (`/esri/streets/`, `/esri/imagery/`) and
|
||||
the elevation tiles (`/terrarium/`).
|
||||
- CORS proxies for the three Overpass mirrors (`/overpass/api/`,
|
||||
`/overpass/coffee/`, `/overpass/mail/`) with a browser-like request, long
|
||||
timeouts and a 30-day `proxy_cache` (`overpass-cache.conf`) that also serves
|
||||
stale entries while a mirror is down. Public Overpass instances refuse or
|
||||
mis-handle cross-origin POSTs, and they are flaky; the cache means a cell the
|
||||
browser already paid for is never lost.
|
||||
- `Cache-Control: no-store` for HTML and JS, so a changed `CONFIG` takes effect
|
||||
on reload.
|
||||
|
||||
A plain static server without these proxies (e.g. `python3 -m http.server
|
||||
58881`) runs the UI, but terrain, basemap and road data will not load.
|
||||
|
||||
## Single-file build
|
||||
|
||||
`build.py` bundles everything into one self-contained HTML file
|
||||
(`background-standalone.html`) — all scripts, the star catalogue and all
|
||||
theme CSS are inlined, and theme switching keeps working:
|
||||
`build.py` bundles everything into one self-contained HTML file: all scripts,
|
||||
the star catalogue and every theme stylesheet are inlined, and theme switching
|
||||
keeps working.
|
||||
|
||||
```sh
|
||||
python3 build.py # -> background-standalone.html (no sibling files needed)
|
||||
python3 build.py # -> background-standalone.html
|
||||
python3 build.py out.html # -> custom output path
|
||||
python3 build.py --check # build to a temp file and verify, write nothing
|
||||
```
|
||||
|
||||
Runtime *data* is still fetched at load (tiles, Overpass, weather): serve
|
||||
the file from the Docker image for the full experience; opened via
|
||||
`file://` or a proxy-less server it renders sky, clouds and UI with an
|
||||
"OFFLINE"-labelled ground grid instead of basemap tiles.
|
||||
`--check` fails if anything is left that would need a sibling file (an external
|
||||
`<script src>` or a CSS `<link>`).
|
||||
|
||||
Runtime *data* is still fetched at load (tiles, Overpass, weather): serve the
|
||||
file from the Docker image for the full experience. Opened via `file://` or a
|
||||
proxy-less server it renders sky, clouds and UI with an "OFFLINE"-labelled
|
||||
ground grid instead of basemap tiles.
|
||||
|
||||
## Configuration
|
||||
|
||||
Edit `config.js` and reload. All keys (with defaults):
|
||||
`config.js` is the reference: every key is documented in place, and unused
|
||||
options are listed commented-out with example values. Most knobs are read every
|
||||
frame, so editing the file and reloading is enough - no rebuild needed.
|
||||
|
||||
Groups:
|
||||
|
||||
| Group | What it controls |
|
||||
| --- | --- |
|
||||
| `theme`, `language`, `timeFormat`, `units`, `credits` | palette, UI language, clock format, °C/°F, attribution line |
|
||||
| `debug` | debug panel (weather, shader counters, theme palette and lighting) |
|
||||
| `renderScale`, `maxFps` | internal 3D resolution and frame cap |
|
||||
| `location` | `lat`/`lon` (+ optional `label`) - everything geographic |
|
||||
| `weatherSim`, `weatherSimInterval` | ignore the API and cycle the weather looks |
|
||||
| `tiles` | basemap re-fetch interval (`expireDays`), tile-border debug overlay |
|
||||
| `ground.style` | `'satellite'` (ESRI World_Imagery) or `'streets'` (ESRI World_Street_Map) |
|
||||
| `orbit` | figure-eight path, `radiusKm`, direction, reorientation, `altBob` |
|
||||
| `fog.distanceKm` | distance haze (0/false = off) |
|
||||
| `clouds` | deck height (`top`/`bottom` in world units) and `density` per weather level |
|
||||
| `flights` | aircraft count, altitudes, speeds, destination weights, contrails, night strobes |
|
||||
| `terrain` | DEM mesh, `exaggeration`, `edgeFadeTiles`, `carLights`, `cityLights`, `wireframe` |
|
||||
| `carLights` | road width, light sizes, lane separation, traffic range/fade, `trafficSide`, spacing, speed, density, brightness |
|
||||
| `lights` | twilight switching window of every artificial light (`offDeg`, `onDeg`, `softDeg`) |
|
||||
| `atmosphere` | turbidity, moon/star gains, night floor, albedo/sky/air gains, haze residual, cloud albedo and cloud haze, cloud shadows, terrain shadows |
|
||||
| `stars` | star colour `tint` and `brightness` |
|
||||
| `day` | `simulation`, `cycleSeconds`, `fixedTime` |
|
||||
|
||||
### World scale
|
||||
|
||||
1 world unit = one zoom-11 tile ≈ 12.9 km at 48.6°N. World axes are
|
||||
`+X = west`, `+Y = up`, `+Z = north`; the camera flies the 15-35 km band
|
||||
(`y ≈ 1.16-2.70`). Cloud deck heights, fog distance and orbit radius are all
|
||||
expressed in these units or in km where noted.
|
||||
|
||||
### Themes and theme lighting
|
||||
|
||||
A theme is a stylesheet plus an optional lighting block:
|
||||
|
||||
```javascript
|
||||
window.CONFIG = {
|
||||
theme: 'ash', // 'ash' | 'gruvbox' | 'nord'
|
||||
language: 'en', // 'en' | 'de' | 'it' | 'es' | 'fr' | 'ru'
|
||||
timeFormat: 'DD. MMMM, YYYY \u2012 HH:mm:SS',
|
||||
debug: 0, // 1 = show right-side debug panel
|
||||
units: { temperature: 'C' }, // 'C' | 'F'
|
||||
credits: true, // data-attribution block in info box
|
||||
renderScale: 1, // 0.3..1 internal 3D resolution
|
||||
maxFps: 30, // WebGL scene frame cap (0 = free)
|
||||
|
||||
location: { // drives scene anchor, sun/moon, weather
|
||||
lat: 51.5074, // -90..+90, north positive
|
||||
lon: -0.1278, // -180..+180, east positive
|
||||
label: '' // optional name, skips reverse geocoding
|
||||
},
|
||||
|
||||
day: {
|
||||
simulation: false, // true: run 24 h in cycleSeconds;
|
||||
cycleSeconds: 20, // false: real sun/moon positions
|
||||
fixedTime: null // 'HH:MM' freezes the sim clock at
|
||||
// that time of day (cars, clouds,
|
||||
// weather keep moving); null = flow
|
||||
},
|
||||
weatherSim: false, // true: cycle weather themes, ignore API
|
||||
weatherSimInterval: 30000, // ms per sim step
|
||||
|
||||
clouds: { top: 0.9, bottom: -0.3 },// cloud deck, world units
|
||||
// (1 unit = ~12.9 km at z11 / 48.6 deg N)
|
||||
|
||||
orbit: { // null/omit = straight flight
|
||||
radiusKm: 60, // lemniscate lobe tip (clamped ~190 km)
|
||||
clockwise: true,
|
||||
reorient: true, // random heading at each crossing
|
||||
altBob: { // vertical bob, flight band 15-35 km
|
||||
enabled: true, // false = constant 25 km
|
||||
amp: 0.77, // half-band in world units
|
||||
periodS: 90, // seconds per bob cycle
|
||||
tilt: 0.3 // pitch reaction to climb/descent
|
||||
}
|
||||
},
|
||||
fog: { distanceKm: 120 }, // ground haze; 0/false = off
|
||||
|
||||
terrain: {
|
||||
enabled: true, // false = flat plane
|
||||
exaggeration: 8, // vertical DEM scale factor
|
||||
wireframe: false, // debug: orange mesh edges
|
||||
nightLights: false, // NASA VIIRS city-lights overlay
|
||||
carLights: true, // moving lights on real roads
|
||||
cityLights: true // settlement glow dots
|
||||
},
|
||||
|
||||
flights: { // see section above; all distances km
|
||||
enabled: true,
|
||||
altitudeKm: 14, climbKm: 100, // cruise / climb-descent run
|
||||
count: 84, speedKmh: 850, speedVar: 0.18, minLegKm: 60,
|
||||
holdMinS: 50, holdMaxS: 150, // ground hold at destination
|
||||
wHub: 4, wField: 2, wStrip: 1, // destination roll weights
|
||||
contrailChance: 0.6, // share of aircraft with a trail
|
||||
contrailStartKm: 6, // formation altitude
|
||||
contrailLifeS: 300, // visibility duration
|
||||
contrailWidthM: 310 // full trail width
|
||||
},
|
||||
|
||||
ground: { style: 'satellite' }, // 'satellite' | 'streets'
|
||||
tiles: {
|
||||
expireDays: null, // >0: re-fetch basemap tiles after n days
|
||||
debug: false // tile borders + coords on the ground
|
||||
window.THEMES['mytheme'] = {
|
||||
css: 'mytheme.css',
|
||||
lighting: {
|
||||
tint: 0.45, // 0 = pure physics, 1 = theme colour
|
||||
sun: '#f2e3c0', // direct beam
|
||||
sky: '#5e81ac', // skylight and horizon
|
||||
air: '#81a1c1', // aerial perspective / haze
|
||||
moon: '#d8e3ee', // moonlight
|
||||
gain: 0.95, // day exposure
|
||||
nightGain: 1.12 // night exposure
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
The physical model stays the base in every theme; `lighting` only shifts hues
|
||||
and exposure. Omit `lighting` for a purely physical theme. The palette itself is
|
||||
read from the CSS custom properties of the active stylesheet, so the scene and
|
||||
the UI always agree.
|
||||
|
||||
### Debug switches
|
||||
|
||||
All off by default, all documented in `config.js`:
|
||||
|
||||
- `debug: 1` - debug panel (bottom left): weather state, sun/moon, shader
|
||||
counters, the active theme palette and its lighting values.
|
||||
- `tiles: { debug: true }` - bake tile borders, N/S and x/y labels into the
|
||||
ground texture (orientation check).
|
||||
- `terrain: { wireframe: true }` - draw the mesh edges.
|
||||
- `ground: { style: 'streets' }` - labelled street map instead of satellite.
|
||||
- `day: { simulation: true }` / `weatherSim: true` - deterministic day cycle and
|
||||
weather cycle for comparing looks.
|
||||
|
||||
## URL hash parameters
|
||||
|
||||
`http://host:58881/#flight=N&fetch=S`
|
||||
|
||||
- `flight=N|NE|E|SE|S|SW|W|NW` — heading of the straight flight corridor
|
||||
(used when `CONFIG.orbit` is disabled)
|
||||
- `fetch=N|NE|E|SE|S|SW|W|NW` — direction the tile mosaic is prefetched
|
||||
ahead of the camera
|
||||
- `flight=N|NE|E|SE|S|SW|W|NW` - heading of the straight flight corridor (used
|
||||
when `CONFIG.orbit` is disabled)
|
||||
- `fetch=N|NE|E|SE|S|SW|W|NW` - direction the tile mosaic is prefetched ahead of
|
||||
the camera
|
||||
|
||||
## Time format tokens
|
||||
|
||||
@@ -173,42 +191,54 @@ needs no special token).
|
||||
\n newline \t tab \\ literal backslash
|
||||
```
|
||||
|
||||
## Adding a new language
|
||||
## Caches
|
||||
|
||||
Edit `i18n.js` and add entries for each existing key under your new
|
||||
language code, e.g.:
|
||||
Everything is stored in IndexedDB, database `background-tiles`, store `tiles`:
|
||||
|
||||
| Key pattern | Content |
|
||||
| --- | --- |
|
||||
| `sat/{z}/{x}/{y}` / `{z}/{x}/{y}` | basemap tiles, kept per style so satellite and street maps cache separately |
|
||||
| `elev/...` | decoded elevation grid cells |
|
||||
| `geo/mw11/...`, `geo/mw_e/...`, `geo/sec10/...` | Overpass road cells (motorway network and side roads) |
|
||||
| `geo/place_...` | Overpass place-node cells |
|
||||
| `geo/roadchains_v1` | merged road polylines (rebuilt only when a cell is re-fetched) |
|
||||
| `geo/placecache_v1` | decoded settlement list |
|
||||
| `geo/airports_v1` | airfield list used by the aviation layer |
|
||||
|
||||
The star catalogue is not cached: `build.py` inlines `vendor/stars.json` into
|
||||
`window.__starsData`. Weather is the only thing fetched live on every load.
|
||||
|
||||
Cached data is never re-fetched unless `CONFIG.tiles.expireDays` is set. To
|
||||
start clean, delete the `background-tiles` database in the browser.
|
||||
|
||||
## Adding a language
|
||||
|
||||
Add the key under the new language code in `i18n.js`:
|
||||
|
||||
```javascript
|
||||
window.TRANSLATIONS['Sunrise']['xx'] = 'MyTranslation';
|
||||
```
|
||||
|
||||
## Adding a new theme
|
||||
|
||||
Drop a new `*.css` file next to the others and add an entry to
|
||||
`window.THEMES` in `config.js`:
|
||||
|
||||
```javascript
|
||||
window.THEMES['mytheme'] = 'mytheme.css';
|
||||
```
|
||||
|
||||
## Files
|
||||
|
||||
```
|
||||
.
|
||||
├── background.html the page (UI, weather, day/night driver)
|
||||
├── config.js user-facing configuration
|
||||
├── background.html the page (UI, weather, day/night driver, debug panel)
|
||||
├── config.js user-facing configuration + window.THEMES
|
||||
├── i18n.js translation strings (en/de/it/es/fr/ru)
|
||||
├── ash.css default color theme
|
||||
├── gruvbox.css alternative color theme
|
||||
├── nord.css alternative color theme
|
||||
├── ash.css default colour theme
|
||||
├── gruvbox.css alternative colour theme
|
||||
├── nord.css alternative colour theme
|
||||
├── vendor/
|
||||
│ ├── clouds3d.js WebGL scene renderer (clouds, terrain, sky, lights)
|
||||
│ ├── suncalc.js ISC (SunCalc by mourner)
|
||||
│ ├── clouds3d.js WebGL scene renderer (sky, clouds, terrain, lights)
|
||||
│ ├── suncalc.js sun/moon positions - ISC (SunCalc by mourner)
|
||||
│ ├── plane3d.js low-poly aircraft model - CC0
|
||||
│ ├── stars.json Yale Bright Star Catalogue subset
|
||||
│ └── LICENSE per-library license attributions
|
||||
├── default.conf nginx config: port 58881 + tile CORS proxies
|
||||
├── default.conf nginx: port 58881, CORS proxies, cache headers
|
||||
├── overpass-cache.conf nginx proxy_cache_path for the Overpass cache
|
||||
├── build.py single-file HTML bundler (see "Single-file build")
|
||||
├── Dockerfile builds nginx:alpine image
|
||||
├── Dockerfile builds the nginx:alpine image
|
||||
├── docker-compose.yml one-service Compose file
|
||||
└── LICENSE MIT (application code)
|
||||
```
|
||||
@@ -217,11 +247,15 @@ window.THEMES['mytheme'] = 'mytheme.css';
|
||||
|
||||
- **Background application code**: MIT (see `LICENSE`)
|
||||
- **SunCalc** (vendored): ISC - <https://github.com/mourner/suncalc>
|
||||
- **Aircraft model** (`vendor/plane3d.js`): CC0
|
||||
- **Open-Meteo** (weather API): CC BY 4.0 - <https://open-meteo.com/>
|
||||
- **OpenStreetMap** (basemap tiles via ESRI/Carto, Overpass road and
|
||||
place data): ODbL - <https://www.openstreetmap.org/copyright>
|
||||
- **Copernicus DEM** (terrain via Mapzen terrarium tiles): open data
|
||||
- **NASA GIBS VIIRS** (optional night-lights overlay): public domain
|
||||
- **ESRI** basemaps (`World_Imagery`, `World_Street_Map`) served through the
|
||||
bundled proxy: Esri, its licensors and contributors -
|
||||
<https://www.esri.com/en-us/legal/acknowledgmentsandstandards>
|
||||
- **OpenStreetMap** (Overpass road and place data): ODbL -
|
||||
<https://www.openstreetmap.org/copyright>
|
||||
- **Copernicus DEM** (terrain, via Mapzen/TILEZEN terrarium tiles): open data -
|
||||
<https://registry.opendata.aws/terrain-tiles/>
|
||||
- **Yale Bright Star Catalogue** (star positions, via `vendor/stars.json`)
|
||||
- **OpenStreetMap Nominatim** (reverse geocoding): ODbL -
|
||||
<https://nominatim.org/release/>
|
||||
Reference in new issue
Block a user