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.

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; a configurable expiry (CONFIG.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.
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.

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:

python3 build.py            # -> background-standalone.html (no sibling files needed)

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):

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

    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
    }
};

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

Time format tokens

YYYY   4-digit year          (2026)     MM     month 01-12     (10)
DD     day 01-31             (07)       HH     hour 00-23      (14)
mm     minute 00-59          (35)       ss     second 00-59    (22)
sss    millisecond 000-999   (456)      a      am/pm            (pm)
A      AM/PM                 (PM)

MMM    month short name      (Oct, Okt, Ott, ...)
MMMM   month full name       (October, Oktober, ...)
DDD    weekday short name    (Mon, Mo, Lu, ...)
dddd   weekday full name     (Monday, Montag, ...)   lowercase!

Any other character is emitted verbatim (so a literal T in ISO-8601
needs no special token).
\n     newline    \t tab    \\ literal backslash

Adding a new language

Edit i18n.js and add entries for each existing key under your new language code, e.g.:

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:

window.THEMES['mytheme'] = 'mytheme.css';

Files

.
├── background.html          the page (UI, weather, day/night driver)
├── config.js                user-facing configuration
├── 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
├── vendor/
│   ├── clouds3d.js          WebGL scene renderer (clouds, terrain, sky, lights)
│   ├── suncalc.js           ISC (SunCalc by mourner)
│   ├── stars.json           Yale Bright Star Catalogue subset
│   └── LICENSE              per-library license attributions
├── default.conf             nginx config: port 58881 + tile CORS proxies
├── build.py                 single-file HTML bundler (see "Single-file build")
├── Dockerfile               builds nginx:alpine image
├── docker-compose.yml       one-service Compose file
└── LICENSE                  MIT (application code)

Licenses & data attribution

S
Description
No description provided
Readme MIT
6.5 MiB
0 Stars 1 Watchers 0 Forks
Languages
HTML 62.7%
JavaScript 27.5%
Python 7%
CSS 2.6%
Dockerfile 0.2%