Files
animated_wallpaper/config.js
T
Richard Zink 6eb25bdd03 physical lighting, volumetric clouds, terrain shadows, pixel-art render scale
- atmosphere: physical terrain lighting (air mass, Rayleigh beam colour,
  skylight, moon, starlight floor), aerial perspective for terrain and
  cloud deck, configurable haze/albedo/ambient gains
- clouds: march the deck in height instead of ray distance, IGN dither
  (no per-frame flicker), per-step height jitter, 4-octave fbm renormalised,
  softer density threshold, distance softening as a procedural mip,
  grazing path clamp, cloud shadows on the ground (cloudShadowDensity/
  Floor/Ambient) and cloud haze factor
- terrain: self-shadows from the elevation field (penumbra + shadow floor),
  soft mosaic rim (edgeFadeTiles), east/west axis corrected (+X = west)
- lights: per-object twilight switching for cars, city dots and ground blocks
- flights: contrail width 620 m, fade to zero by contrailFadeKm, aircraft
  size decoupled from trail width, count halved
- render: CONFIG.renderScale with integer upscale + image-rendering: pixelated
- data: nginx overpass proxy + cache, per-mirror cooldown ladder, ring-based
  cell ordering (disc fills evenly), settlements fetched before roads,
  interim chain persistence, two worker lanes (car/city) with progress logs
- removed: NASA VIIRS night-lights layer (fetch, canvas, texture, uniforms)
2026-10-11 15:04:21 +02:00

347 lines
18 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* Wallpaper background config.
*
* Properties:
* theme - Active palette theme. One of the keys in window.THEMES
* (default 'ash'). The matching CSS file is loaded at
* startup.
* language - UI / i18n language code (default 'en').
* timeFormat - Format string for the clock using the token set below
* (Moment.js-like names, own mini-formatter).
* debug - If 1, the right-side debug panel is shown.
* units - Object with 'temperature' ('C' or 'F').
* location - Geographic coordinates for sun/moon calculations. Used
* by SunCalc (https://github.com/mourner/suncalc). Object
* with 'lat' (latitude, -90 to +90) and 'lon' (longitude,
* -180 to +180). Positive lat = north, positive lon = east.
* Optionally add 'label' (string) to override the
* reverse-geocoded city name.
* Note: the height grid is axis-aligned (N up); terrain
* matches the square ground mosaic and straight flights
* (prefetch direction via #fetch=N). Heights: Copernicus
* DEM via terrarium tiles (z9, ~200 m).
* terrain - 3D ground from real elevation data. Object:
* enabled - true (default) / false = flat plane.
* exaggeration - vertical scale factor on the DEM
* (default 8; 1 would be physically
* true and nearly invisible at this
* horizontal scale).
* weatherSim - If true, ignore open-meteo and cycle through
* ['sunny','cloudy','stormy','night'] automatically every
* weatherSimInterval ms. Useful for A/B testing every
* theme without waiting on the API.
* weatherSimInterval - Sim cycle period in ms (default 30000).
* orbit - Figure-eight flight (Bernoulli lemniscate) around
* CONFIG.location instead of a straight drift. The
* eight's self-crossing sits exactly on the configured
* coordinate. Each time the aircraft crosses that point
* a new random orientation is picked and blended in
* over one lobe (spline through the crossing). Object:
* radiusKm - lobe tip reach from the centre in km
* (~13 km per tile at z11/48deg N).
* Clamped (~190 km) so tips + fog horizon
* fit inside the 43x43 tile mosaic.
* clockwise - travel direction along the eight.
* reorient - random re-aim at every crossing
* (default true; false keeps the bearing).
* Set to null (or remove) for the straight-line flight.
* fog - Ground fog / distance haze. Object with 'distanceKm'
* (visibility range at which terrain fully blends into
* a milky haze; default 120). false/0 disables fog.
* clouds - Vertical extent of the cloud deck in world units
* (1 unit = ~12.9 km at zoom 11; camera band 15-35 km,
* i.e. y ~ 1.16-2.70). Default puts the deck top (~11.6
* km) below the lowest flight altitude.
* tiles - All downloads (street tiles, elevation grid, last
* weather payload, geo label) live permanently in
* IndexedDB and are never re-fetched once cached; a
* warm cache = fully offline rendering. Set
* expireDays (number > 0) to re-fetch street tiles
* periodically (e.g. 90).
*
* Time format tokens (subset):
* YYYY 4-digit year (2026)
* MMMM full month name (October)
* MM month with leading zero (10)
* DD day with leading zero (07)
* HH 24-hour hour with zero (18)
* mm minute with leading zero (14)
* ss second with leading zero (25)
* a am/pm lowercase (pm)
* A AM/PM uppercase (PM)
* dddd full weekday name (lowercase!)
* Any other character is emitted verbatim.
*/
window.CONFIG = {
theme: 'ash',
language: 'en',
timeFormat: 'DD. MMMM, YYYY ‒ HH:mm:SS',
debug: 0,
units: {
temperature: 'C'
},
// Data attribution at the bottom of the info box (automatic text
// based on the enabled layers). false = hidden.
credits: true,
// Internal 3D resolution scale (0.3..1, default 1). Lower values
// render the clouds/map shader at reduced resolution and upscale
// via CSS - use it if fps is low (GPU-bound) despite a quiet CPU.
// 1 = native. 0.5 = a quarter of the pixels (half width, half height),
// upscaled 2x as hard blocks; the DOM UI on top stays crisp.
renderScale: 0.5,
// WebGL scene frame cap (default 30; 0 = free-running/vsync). All
// scene motion is time-based, so the cap only changes smoothness.
maxFps: 30,
location: {
// Latitude and longitude for sun/moon calculations
lat: 48.5667,
lon: 13.4319
},
// weatherSim: true = Szenario-Wetter statt Open-Meteo (A/B-Durchlauf
// aller Wetterstufen alle weatherSimInterval ms). false = echtes
// Wetter von Open-Meteo.
weatherSim: false,
weatherSimInterval: 30000,
// All downloads (basemap tiles, elevation grid, weather JSON, geo
// label) stay in IndexedDB permanently and are never re-fetched once
// cached. expireDays (days, > 0) re-enables periodic basemap refresh.
tiles: {
expireDays: null,
// true = draw tile borders + N/S + x/y coords into the ground
// texture (debug orientation). Off = clean map.
debug: true
},
// Ground imagery: 'satellite' = ESRI World_Imagery (aerial, no
// labels), 'streets' = ESRI World_Street_Map (with place names).
// Both cached permanently (separate cache prefix per style).
ground: {
style: 'streets'
},
// Figure-eight course around location (lat/lon); the crossing point
// sits exactly on the coordinate. null/removed = straight flight.
orbit: {
radiusKm: 60, // lobe tip: max distance from the centre
clockwise: true,
reorient: true, // random heading at every crossing pass
// Slow vertical bobbing inside the 15-35 km flight band:
// centre 25 km (~1.93 world units), amp = half the band
// (10 km ~ 0.77); hard clamp at the band limits.
// periodS = seconds per cycle, tilt = camera nose reaction to
// climb/descent rate. enabled: false = constant 25 km.
altBob: { enabled: true, amp: 0.77, periodS: 90, tilt: 0.3 }
},
// Ground fog: distance haze. distanceKm = visibility until fully
// saturated haze (false/0 = off).
fog: {
distanceKm: 120
},
// ---- Civil aviation (CONFIG.flights) ---------------------------------
// Low-poly jets (CC0 model, vendor/plane3d.js) with randomized target
// airports: the destination is rolled at every take-off (hubs weighted
// via wHub/wField/wStrip). Paths are gentle Bezier curves resampled to
// arc length (constant ground speed) flown as a full profile: take-off
// and climb, cruise, descent and landing, a short ground hold, then a
// fresh destination. By day sun-lit silhouettes; by night white
// strobing position lights with a red beacon. Contrails form above
// contrailStartKm on contrailChance of the aircraft and stay visible
// for contrailLifeS. All distances in km (world scale: 1 unit = one
// z11 tile ~ 12.9 km at 48.6 deg N).
flights: {
enabled: true,
altitudeKm: 14, // cruise altitude
climbKm: 100, // climb/descent run to/from cruise altitude
count: 21, // max simultaneous aircraft
speedKmh: 850, // mean ground speed
speedVar: 0.18, // +/- deviation (0..0.5)
minLegKm: 60, // shortest leg (no taxi hops)
holdMinS: 50, // ground hold at destination (min..max)
holdMaxS: 150,
wHub: 4, // destination weights: hub / airfield / strip
wField: 2,
wStrip: 1,
contrailChance: 0.85, // share of aircraft with a persistent trail
contrailStartKm: 6, // trail formation altitude
contrailLifeS: 300, // trail visibility duration
contrailWidthM: 620, // full trail width
// trails fade out completely by this distance (start is 35% of it)
contrailFadeKm: 100,
// Night strobes. Size shrinks with distance but never below
// lightPx, and never grows past lightMaxPx for a jet right above.
// The white strobe is a double flash: two equal flashes
// blinkGapS apart, then blinkPeriodS - blinkGapS - blinkFlashS
// of darkness (1.4 - 0.16 - 0.08 = 1.16 s pause).
lightPx: 7,
lightMaxPx: 16,
lightBoost: 9,
blinkPeriodS: 1.4,
blinkGapS: 0.16,
blinkFlashS: 0.08
},
// 3D ground from real elevation data (Copernicus DEM, terrarium
// tiles). enabled: false = flat plane. exaggeration: vertical
// elevation scale (1 = real, nearly invisible; default 8).
// wireframe: true = overlay mesh edges as orange lines (debug).
// carLights: moving lights on the real road network (OSM/Overpass
// curves, additive over the terrain at night). JSON once, cached.
// cityLights: settlement lights - OSM place nodes (city/town/
// village/hamlet) as slightly yellow glow dots from dusk on;
// bigger settlements (place class + population tag) get more and
// brighter dots. One Overpass query, cached permanently.
// Data (c) OpenStreetMap contributors, ODbL.
terrain: {
// soft rim at the mosaic border, in tiles (1 tile ~ 13 km)
edgeFadeTiles: 4,
enabled: true,
exaggeration: 8,
wireframe: false,
// How far the day map is dimmed at full night: 0 = pitch black,
// 1 = unchanged. Only affects night; the day map is untouched.
// (The NASA VIIRS city-lights layer was removed; city lights come
// from OSM place nodes via cityLights.)
nightDim: 0.22,
carLights: true,
cityLights: true
},
// Car lights on the real road network (on/off stays in terrain.carLights).
// Two layers now: a thin road line (bandPx / bandBrightness) and one
// round point sprite per vehicle (minPx..maxPx), same idea as the city
// dots' gl_PointSize floor. laneM is the real distance between the two
// traffic directions, so they split close to the camera and merge into
// one line far away instead of drifting apart with the camera distance.
// dashM: mean vehicle spacing (secondary roads use dashM * 2.8).
// speed: multiplier on the real driving speed (motorway ~120 km/h,
// secondary ~70 km/h at dashM spacing).
carLights: {
roadWidthM: 24, // real carriageway width of the road line
bandPx: 1.2, // pixel floor for the road line
bandBrightness: 0.10, // road line glow (points are the main layer)
minPx: 3.6, // guaranteed vehicle light diameter
maxPx: 10, // ceiling for a light right above the camera
boost: 5, // extra light size close to the camera
laneM: 8, // real distance between the two directions
lanePx: 2.6, // screen floor for that distance, so the two
// directions never collapse into one stream
// Traffic lives on a disc around the configured location: roads
// outside rangeKm carry no vehicles. fadeKm / fadeSpanKm are the
// distance fade belonging to that disc (start, and how far past it
// until nothing is left) - keep them at or beyond rangeKm so the
// region never ends in a hard cut.
rangeKm: 200,
fadeKm: 150,
fadeSpanKm: 60,
// 'auto' = side of the road taken from location (lat/lon): left in
// the UK, Japan, Australia, India, Southern Africa, ... and right
// everywhere else. Set 'left' or 'right' to force it.
trafficSide: 'auto',
dashM: 620, // mean vehicle spacing on motorways
// Traffic spread around the motorway network (gaussian): peak on the
// motorway itself, thinning with distance to the nearest one.
// spreadKm = sigma, spreadFloor = share that far side roads keep.
spreadKm: 45,
spreadFloor: 0.30,
speed: 1.0, // flow speed multiplier
density: 1.0, // vehicle density multiplier
brightness: 1.3 // vehicle light intensity
},
// Headlights and house lights do not ignite at once: every single
// light gets its own sun-altitude threshold between offDeg (nothing
// lit) and onDeg (everything lit), drawn per vehicle / per city block.
// offDeg +5 deg is roughly 30 min before sunset here, onDeg -6 deg is
// civil twilight. In the morning the same thresholds switch the lights
// off again as the sun climbs.
lights: {
offDeg: 5,
onDeg: -6,
softDeg: 1.2
},
// Physical lighting: direct beam from a Kasten-Young air mass with
// Rayleigh + ozone + aerosol extinction, diffuse skylight and moonlight
// from the same model. terrainLighting: 'palette' switches back to the
// old hand-painted hillshade. turbidity scales the aerosol load,
// moonGain the moonlight strength, nightFloor the darkest skylight
// (airglow + starlight), albedoTint is the theme's share on surfaces.
atmosphere: {
terrainLighting: 'physical',
turbidity: 1.0,
// moonlight strength; scales the direct term AND the diffuse sky
// lift, both with the illuminated fraction (new moon -> none)
moonGain: 0.60,
// stylised starlight + airglow floor on a dark night (real
// integrated starlight is ~1e-6 of daylight and would be invisible)
starGain: 0.18,
// extra share on the ground. The product is the starlit floor and
// must stay below the skylight at sunset, otherwise the night would
// be brighter than the moment of sunset. Moonlight (moonGain) is
// what lifts a night above that floor.
starGainGround: 0.4,
nightFloor: 0.10,
// cloud albedo in physical mode: theme hue kept, brightness comes
// from the light model (the night palette alone would double-darken)
cloudBrightness: 1.0,
// moving cloud patches darken the ground beneath them (the terrain
// samples the same cloud field as the sky pass)
cloudShadows: true,
cloudShadowDensity: 1.8, // optical depth through the deck
cloudShadowFloor: 0.06, // darkest shade under a thick cloud
cloudShadowAmbient: 0.65, // share of the skylight a cloud also blocks
// ridges occlude the sun for the ground behind them (march over the
// same height field the mesh was built from)
terrainShadows: true,
// brightness a fully shadowed slope keeps (0 = pitch black, 1 = off)
terrainShadowFloor: 0.42,
// how much of the ground haze applies to the cloud deck (clouds sit
// above most of the air that causes it)
cloudHaze: 0.22,
albedoGain: 1.0,
// share of the daylight column that the distance fog / horizon takes,
// and the diffuse skylight share on surfaces
skyGain: 0.45,
// Aerial perspective in physical mode: the terrain is attenuated by
// exp(-tau) and the scattered light of the same air column is added.
// airGain scales the Rayleigh airlight (blue-biased), hazeResidual
// is how much of the surface survives at the full fog distance
// (0.3 = distant ground keeps 30 % of its own colour).
airGain: 0.55,
hazeResidual: 0.30
},
// Star colour: every catalogue star gets its real blackbody tint, mixed
// towards white by this factor. 0 = pure white points, 1 = full colour.
stars: {
tint: 0.10, // 90 % white + 10 % of the star's real colour
brightness: 1.6 // overall glow of the catalogue points
},
// Day simulation: true = a full 24 h cycle runs in cycleSeconds
// (sun rises east, culminates south, sets west).
// false = real sun/moon positions via SunCalc.
// fixedTime: 'HH:MM' (or a number 0-24) freezes the sim clock at
// that time of day (moving objects, clouds, cars and the weather
// sim keep running unaffected); null/omitted = the clock advances.
day: {
// simulation: true runs a full 24 h cycle in cycleSeconds.
// 60 s = one simulated day per minute of wall time (one simulated
// hour every 2.5 s). The analog clock and the sun/moon tables follow
// the simulated date.
simulation: true,
cycleSeconds: 60,
fixedTime: null
},
// Cloud deck in world units (1 unit ~ 12.9 km at zoom 11; flight
// band 15-35 km = y ~ 1.16-2.70). Keep the top below the lowest
// flight altitude.
clouds: {
top: 0.9,
bottom: -0.3,
// per weather level (0 = clear, 1 = overcast); overrides the built-in
// sunny 0.20 / cloudy 0.70 / stormy 0.80 / night 0.45
density: { stormy: 0.80 }
},
};
// List of available themes. The key is the theme id (used in config.theme),
// the value is the CSS file name. Add a new theme by adding a new entry here
// and dropping the corresponding CSS file in the same directory.
window.THEMES = {
'ash': 'ash.css',
'gruvbox': 'gruvbox.css',
'nord': 'nord.css'
};