diff --git a/background.html b/background.html
index ec6f974..abdfb92 100644
--- a/background.html
+++ b/background.html
@@ -803,6 +803,20 @@ window.addEventListener('resize', scheduleFitInfoPanel);
backgroundColor: hexToInt(ASH.void),
speed: 2.5
},
+ rain: {
+ skyColor: hexToInt(ASH.steel),
+ cloudColor: hexToInt(ASH.panel),
+ lightColor: hexToInt(ASH.bone),
+ backgroundColor: hexToInt(ASH.void),
+ speed: 1.8
+ },
+ snow: {
+ skyColor: hexToInt(ASH.blue),
+ cloudColor: hexToInt(ASH.chalk),
+ lightColor: hexToInt(ASH.chalkLight),
+ backgroundColor: hexToInt(ASH.steel),
+ speed: 0.7
+ },
night: {
skyColor: hexToInt(ASH.void),
cloudColor: hexToInt(ASH.raised),
@@ -1444,6 +1458,22 @@ window.weatherThemes = {
backgroundColor: hexToInt(ASH.panel),
speed: 2.5
},
+ rain: {
+ // Wet grey overcast, faster cloud motion
+ skyColor: hexToInt(ASH.steel),
+ cloudColor: hexToInt(ASH.stone),
+ lightColor: hexToInt(ASH.bone),
+ backgroundColor: hexToInt(ASH.panel),
+ speed: 1.8
+ },
+ snow: {
+ // Cold pale overcast, slow drifting cloud field
+ skyColor: hexToInt(ASH.blue),
+ cloudColor: hexToInt(ASH.boneLight),
+ lightColor: hexToInt(ASH.chalk),
+ backgroundColor: hexToInt(ASH.steel),
+ speed: 0.7
+ },
night: {
// Very dark sky, moon-lit clouds
skyColor: hexToInt(ASH.void),
@@ -1458,7 +1488,8 @@ window.weatherThemes = {
if (code === 0) return 'sunny';
if (code >= 1 && code <= 3) return 'cloudy';
if (code >= 45 && code <= 48) return 'cloudy';
- if ((code >= 51 && code <= 67) || (code >= 80 && code <= 82) || (code >= 71 && code <= 77) || (code >= 85 && code <= 86)) return 'stormy';
+ if ((code >= 51 && code <= 67) || (code >= 80 && code <= 82)) return 'rain';
+ if ((code >= 71 && code <= 77) || (code >= 85 && code <= 86)) return 'snow';
if (code >= 95) return 'stormy';
return 'cloudy';
}
@@ -1497,19 +1528,64 @@ window.weatherThemes = {
// Cycles through a list of weather states automatically. Useful
// for A/B testing the scene against every theme without
// waiting on the open-meteo API.
- var simWeathers = ['sunny', 'cloudy', 'stormy', 'night'];
+ var SIM_CFG = window.CONFIG || {};
+ var simWeathers = (Array.isArray(SIM_CFG.weatherSimStates) && SIM_CFG.weatherSimStates.length)
+ ? SIM_CFG.weatherSimStates.slice()
+ : ['sunny', 'cloudy', 'rain', 'snow', 'stormy', 'night'];
var simIndex = 0;
var simTimer = null;
var __weatherSimActive = false;
+ function weatherSimProfileFor(name) {
+ var profiles = SIM_CFG.weatherSimProfile || {};
+ var p = profiles[name] || {};
+ var defaults = {
+ sunny: { code: 0, tempC: 18, humidity: 45, precipMm: 0.0, snowMm: 0.0, windKmh: 8, windDirDeg: 240 },
+ cloudy: { code: 3, tempC: 14, humidity: 70, precipMm: 0.0, snowMm: 0.0, windKmh: 14, windDirDeg: 260 },
+ rain: { code: 61, tempC: 10, humidity: 92, precipMm: 1.2, snowMm: 0.0, windKmh: 22, windDirDeg: 270 },
+ snow: { code: 73, tempC: -2, humidity: 85, precipMm: 0.4, snowMm: 0.8, windKmh: 12, windDirDeg: 300 },
+ stormy: { code: 95, tempC: 16, humidity: 88, precipMm: 2.5, snowMm: 0.0, windKmh: 35, windDirDeg: 250 },
+ night: { code: 0, tempC: 8, humidity: 65, precipMm: 0.0, snowMm: 0.0, windKmh: 6, windDirDeg: 220 }
+ };
+ var d = defaults[name] || defaults.cloudy;
+ return {
+ code: Number(p.code !== undefined ? p.code : d.code),
+ tempC: Number(p.tempC !== undefined ? p.tempC : d.tempC),
+ humidity: Number(p.humidity !== undefined ? p.humidity : d.humidity),
+ precipMm: Number(p.precipMm !== undefined ? p.precipMm : d.precipMm),
+ snowMm: Number(p.snowMm !== undefined ? p.snowMm : d.snowMm),
+ windKmh: Number(p.windKmh !== undefined ? p.windKmh : d.windKmh),
+ windDirDeg: Number(p.windDirDeg !== undefined ? p.windDirDeg : d.windDirDeg)
+ };
+ }
+
+ function applyWeatherSimState(name) {
+ var p = weatherSimProfileFor(name);
+ currentWeatherCode = p.code;
+ window.__currentWeather = {
+ code: p.code,
+ tempC: p.tempC,
+ humidity: p.humidity,
+ precipMm: p.precipMm,
+ snowMm: p.snowMm,
+ windKmh: p.windKmh,
+ windDirDeg: p.windDirDeg
+ };
+ window.__currentWind = {
+ speedKmh: p.windKmh,
+ dirDeg: p.windDirDeg
+ };
+ setWeatherTheme(name);
+ }
+
function startWeatherSim(intervalMs) {
stopWeatherSim();
var step = intervalMs || (window.CONFIG && window.CONFIG.weatherSimInterval) || 30000;
simIndex = 0;
- setWeatherTheme(simWeathers[simIndex]);
+ applyWeatherSimState(simWeathers[simIndex]);
simTimer = setInterval(function() {
simIndex = (simIndex + 1) % simWeathers.length;
- setWeatherTheme(simWeathers[simIndex]);
+ applyWeatherSimState(simWeathers[simIndex]);
}, step);
__weatherSimActive = true;
}
@@ -2229,6 +2305,33 @@ window.weatherThemes = {
var now = sceneNow(); // virtual calendar: follows the same
// clock the sky and the clock hands use
var current = weatherData.current;
+ if (__weatherSimActive) {
+ var simState = currentWeatherThemeName || simWeathers[simIndex];
+ var simProf = weatherSimProfileFor(simState);
+ current = {
+ temperature_2m: simProf.tempC,
+ relative_humidity_2m: simProf.humidity,
+ weather_code: simProf.code,
+ wind_speed_10m: simProf.windKmh,
+ wind_direction_10m: simProf.windDirDeg,
+ precipitation: simProf.precipMm,
+ snowfall: simProf.snowMm
+ };
+ currentWeatherCode = simProf.code;
+ window.__currentWeather = {
+ code: simProf.code,
+ tempC: simProf.tempC,
+ humidity: simProf.humidity,
+ precipMm: simProf.precipMm,
+ snowMm: simProf.snowMm,
+ windKmh: simProf.windKmh,
+ windDirDeg: simProf.windDirDeg
+ };
+ window.__currentWind = {
+ speedKmh: simProf.windKmh,
+ dirDeg: simProf.windDirDeg
+ };
+ }
var hourly = weatherData.hourly;
var daily = weatherData.daily;
@@ -2253,7 +2356,7 @@ window.weatherThemes = {
// button has manual control)
var curTemp = current.temperature_2m;
var curCode = current.weather_code;
- if (SIM_ACTIVE) {
+ if (SIM_ACTIVE && !__weatherSimActive) {
var curIdx = findHourlyIndex(eff, hourly.time);
curTemp = hourly.temperature_2m[curIdx];
curCode = hourly.weather_code[curIdx];
@@ -2285,7 +2388,7 @@ window.weatherThemes = {
: Math.round(curTemp) + '\u00b0C';
var curWindSpeed = current.wind_speed_10m;
var curWindDir = current.wind_direction_10m;
- if (SIM_ACTIVE && hourly.wind_speed_10m && hourly.wind_direction_10m) {
+ if (SIM_ACTIVE && !__weatherSimActive && hourly.wind_speed_10m && hourly.wind_direction_10m) {
var curIdx = findHourlyIndex(eff, hourly.time);
curWindSpeed = hourly.wind_speed_10m[curIdx];
curWindDir = hourly.wind_direction_10m[curIdx];
diff --git a/config.js b/config.js
index 58b1e66..7bbb885 100644
--- a/config.js
+++ b/config.js
@@ -101,9 +101,22 @@ window.CONFIG = {
// false = live Open-Meteo conditions (also used for the theme choice).
// true = cycle sunny -> cloudy -> stormy -> night, useful to compare
// all weather looks without waiting on the API.
- weatherSim: true, // test mode: cycles sunny/cloudy/stormy/night
+ weatherSim: true, // test mode: cycles configured weather states
weatherSimInterval: 15000, // ms per weather step when weatherSim is true
// weatherSim: false, weatherSimInterval: 30000
+ // Synthetic weather states used by weatherSim. Each state sets theme,
+ // Open-Meteo-like weather_code, temperature, humidity, precipitation and wind.
+ weatherSimStates: ['sunny', 'cloudy', 'rain', 'snow', 'stormy', 'night'],
+ weatherSimProfile: {
+ sunny: { code: 0, tempC: 18, humidity: 45, precipMm: 0.0, snowMm: 0.0, windKmh: 8, windDirDeg: 240 },
+ cloudy: { code: 3, tempC: 14, humidity: 70, precipMm: 0.0, snowMm: 0.0, windKmh: 14, windDirDeg: 260 },
+ rain: { code: 61, tempC: 10, humidity: 92, precipMm: 1.2, snowMm: 0.0, windKmh: 22, windDirDeg: 270 },
+ snow: { code: 73, tempC: -2, humidity: 85, precipMm: 0.4, snowMm: 0.8, windKmh: 12, windDirDeg: 300 },
+ stormy: { code: 95, tempC: 16, humidity: 88, precipMm: 2.5, snowMm: 0.0, windKmh: 35, windDirDeg: 250 },
+ night: { code: 0, tempC: 8, humidity: 65, precipMm: 0.0, snowMm: 0.0, windKmh: 6, windDirDeg: 220 }
+ // weatherSimStates: ['sunny', 'rain', 'stormy']
+ // weatherSimProfile: { rain: { code: 80, tempC: 9, precipMm: 2.0, windKmh: 28 } }
+ },
// ------------------------------------------------------------------
// Weather data
@@ -183,7 +196,7 @@ window.CONFIG = {
// only ever reports sunny/cloudy/stormy, so the deck keeps that
// coverage through the night as well - 'night' is used by the weather
// simulation (weatherSim: true) alone.
- density: { sunny: 0.25, cloudy: 0.50, stormy: 0.80 },
+ density: { sunny: 0.25, cloudy: 0.50, rain: 0.75, snow: 0.65, stormy: 0.80 },
// 'night' intentionally has no own density: it follows the last
// non-night weather density (sunny/cloudy/stormy).
// Cloud motion from Open-Meteo wind. Open-Meteo reports wind FROM
@@ -197,7 +210,7 @@ window.CONFIG = {
// wind: { speedScale: 0 } // static clouds
// wind: { speedScale: 140, maxSpeed: 0.12 } // very visible motion
}
- // density: { sunny: 0.05, cloudy: 0.9, stormy: 1.0 }
+ // density: { sunny: 0.05, cloudy: 0.9, rain: 0.95, snow: 0.85, stormy: 1.0 }
},
// ------------------------------------------------------------------
@@ -538,7 +551,7 @@ window.CONFIG = {
// time, 1440 = one simulated day per minute. Clock hands, digital readout,
// weather and sky all use the same clock. Hash overrides: #at=... #speed=...
day: {
- startAt: '2026-10-12T18:30', // test clock: evening, then accelerated
+ startAt: '2026-01-15T18:30', // test clock: winter evening, then accelerated
speed: 1440 // one simulated day per minute
// startAt: null, speed: 1 // real time
},
diff --git a/vendor/clouds3d.js b/vendor/clouds3d.js
index aa3ff87..1b0e245 100644
--- a/vendor/clouds3d.js
+++ b/vendor/clouds3d.js
@@ -5973,9 +5973,11 @@ function hex(rgb) {
var WEATHER_DENSITY = {
sunny: 0.25,
cloudy: 0.50,
- // rain and storm both map to 'stormy' (open-meteo codes 51-67, 71-77,
- // 80-86, 95+). Full 1.0 packed the deck so hard that the march showed
- // its own layering, so the top end is capped below overcast.
+ rain: 0.75,
+ snow: 0.65,
+ // Thunder/rain/storm codes are split into rain/snow/stormy themes.
+ // Full 1.0 packed the deck so hard that the march showed its own
+ // layering, so the top end is capped below overcast.
stormy: 0.80
};
var LAST_NON_NIGHT_CLOUD_DENSITY = WEATHER_DENSITY.cloudy;