When I added the ground-texture code I left the comment as
multiple JS-line-comments ('// Horizon...') between strings.
Those are NOT inside any string, so JS sees:
'// Horizon: ... satellite <- opens string, never closes
// ground texture ... <- JS thinks these are line comments
...
The string stays open until the next ',, then the next string
line parses as a continuation of an unterminated string and the
whole file is rejected. Wrap the comment into one quoted string.
Pulled a 5x4 tile mosaic of the Passau region (zoom 10) from
ESRI's free ArcGIS World Imagery service, stitched + resized to
1024x768 JPEG (277 KB). Added to Dockerfile as ground.jpg.
Shader side:
- New uniform uGround (texture unit 1)
- horizonMask: rd.y in [-0.20, -0.02] -> ground fades in from 0
to 1 as we look further down
- gUv derived from rd.xz so looking forward/down scrolls across
the satellite image (parallax-like feel)
- Distant ground fades toward uBg via a 'fog' factor so the horizon
blends naturally with the sky gradient
Loading is async — clouds render before ground texture arrives.
Falls back to a black sample if ground.jpg fails to load.
- skyDarkness 0.85 -> 0.55 so night sky is deep but not pitch
black, leaving room for stars to show through
- skyMask widened (-0.10 -> -0.20 lower bound, 0.30 -> 0.40 upper)
so stars appear closer to the horizon in the cockpit view
- Star tint multiplier 0.9 -> 1.0 (full bright) for clearer dots
- fbm3: 4 -> 3 octaves
- cloudDensity: 4 layers (base + mid + fine1 + fine2) -> 2 layers
(base + mid at 20%)
Cost per pixel: 16 noise3 -> 6 noise3 per step, ~62% cheaper.
Visual: clouds still have secondary detail but the fine wispy
edges are gone — back to the cleaner silhouette.
'cloud' was referenced outside the for-loop where it was
defined. Use 1.0 - trans (transmittance) as a cloud mask
instead — same information, properly scoped.
clouds3d:
- Star twinkle: 3 Hz ±45% -> 0.5 Hz ±15% (gentle scintillation
instead of static-like flickering)
- Night darkening: applied only to sky pixels via the cloud mask,
not to clouds. The darkening factor jumped 0.45 -> 0.85 so the
night sky really does go black around moonlit clouds. Clouds
keep their uLight-tinted colour so they remain visible.
clouds2d:
- Same darkening approach but via multiplicative factor (no cloud
mask in the 2D shader): col *= 1 - nightFactor * 0.7
- Remove dive component from camY. Was mix(2.0, 1.2, diveT)
(occasional dips into cloud top); now fixed at 2.2, always above
the y=1.5 cloud ceiling. Cloud deck always visible below.
- WEATHER_DENSITY bumped again: sunny 0.55->0.65, cloudy 0.85->0.90,
night 0.65->0.75. Even 'sunny' is now a partly-cloudy sky.
- clouds3d: third fBm detail layer at 3.2x frequency (was 2 layers).
Contributions: base 1.0, mid 0.30, fine 0.12. Combined with the
tighter smoothstep (0.18 -> 0.16) the silhouettes have wispier,
more interesting edges instead of soft blobs.
- Same density bump in clouds2d for consistency.
- WEATHER_DENSITY bumped: sunny 0.20->0.55, cloudy 0.70->0.85,
night 0.45->0.65. Stormy stays at 1.0 (already maxed).
Even 'sunny' now has visible cloud structure, not just clear sky.
- clouds3d: add a second fBm layer at 4x frequency for fine detail,
blended at 18% into the base. Combined with the tighter smoothstep
(0.24 -> 0.18) the cloud silhouettes have more interesting edges
instead of soft blob shapes.
- Same density bump applied to clouds2d so swapping renderers
doesn't change the visual weather.
- config.renderer: clouds2d -> clouds3d (user request)
Reworked all four weather palettes so the cloud body is much
lighter than the sky — the way real clouds look against blue.
Added boneLight and chalkLight to the getColors keys/map.
Also reduced applyWeatherTheme mix from 0.6 to 0.4 so the
day/night sun-position palette stays visible underneath
the weather tint instead of being washed out by it.
Weather: sky cloud light bg
sunny: blueLt chalk chalkLt blue (was: blueLt stone)
cloudy: blue boneLt chalk steel (was: blue mid)
stormy: steel stone bone panel (was: steel panel)
night: void icon chalk void (was: void raised)
The previous noise.png was random pixels (gradient 0.064 per
step), which produces grainy static when sampled at fine octaves
instead of fluffy clouds. New noise uses 4 octaves of bilinear-
interpolated value noise with Perlin's quintic fade curve, giving
a much lower gradient (0.012) so multi-octave sampling produces
soft cloud shapes.
My previous patch used rfind('};') which landed on the THEMES
block's closer instead of CONFIG's. Moved weatherSim + weatherSimInterval
into CONFIG and cleaned the duplicated block. Rebuild docker so
the corrected file replaces the broken one.
- weatherSim config flag (default false): cycles through
['sunny','cloudy','stormy','night'] every weatherSimInterval ms
instead of pulling open-meteo
- Debug-panel buttons: Sunny / Cloudy / Stormy / Night for
instant manual switch; Auto-Cycle toggle, Stop, 5s/15s cycle
- Cleanup: removed duplicate unwrapped tick() in the init block
(the wrapped one already starts the rAF chain)
The sim keeps the existing 5-minute palette transition between
states, so you still see the colour shift rather than a hard cut.
Rewrite clouds2d.js to sample a noise texture instead of computing
fBm with hash functions. Each octave is one texture lookup with
doubled UV scale and halved amplitude.
- Same visual range as before but ~4x cheaper on ALU
- REPEAT-wrapped texture tiles seamlessly when scrolled by time
- Per-pixel hash21 jitter keeps the regular tiling from flickering
- Exposes noise as a uniform, so multiple fragment shaders could
share it
- Theme-agnostic: pulls colours from __currentPalette
Also adds #renderer=clouds2d URL-hash override for quick A/B
testing between the volumetric clouds3d and the 2D version.
The previous commit used ASH.blue and ASH.blueLight but these
were not registered in the getColors() key list or CSS-variable
map, so they were undefined and hexToInt blew up trying to call
.replace on undefined. Added both as proper keys, mapped to the
existing --blue / --blue-light CSS variables.
Ash palette has only muted grays as 'blue' (steel, steelLight).
Switched day entries + sunny/cloudy weather themes to use the
brighter blue and blue-light swatches so a clear midday reads as
a bright sky rather than a gray wash.
- sunPalettes[4] (sunAlt 0.4): sky/bg steel -> blue
- sunPalettes[5] (zenith): sky/bg steelLight -> blueLight
- weatherThemes.sunny: sky steelLight -> blueLight, bg mid -> bone
- weatherThemes.cloudy: sky steel -> blue
Three coordinated changes:
1. nightPaletteNew.cloud: void -> raised (slightly lighter than
sky) so clouds have contrast against the new-moon sky
2. nightPaletteFull.cloudShadow: panel -> void so cloud undersides
fade into the night sky, while the lit topsides (border) stand
out clearly
3. getNightColor() now keeps 'light' (moonlight) bright and only
partially darkens sky/bg. The previous version squashed
everything toward black, killing cloud structure.
Shader side:
- max(0.15, uDayProgress) keeps a baseline moonlight tint on the
lit side of clouds even when uDayProgress happens to be 0
- The final darken pass is now driven by nightFactor (sunAlt-based)
instead of the unreliable uDayProgress
Inserted two extra palette entries around the sunset transition:
- alt -0.262 (-15deg): already full-night (void), so ~1h after
sunset the sky is properly dark instead of still 11% into the
long -5.7deg -> -90deg void ramp
- alt -0.087 (-5deg): dusk anchor (panel) — was at -0.1
- alt 0.087 (+5deg): morning anchor (blood) — was at 0.15
Net effect: +5deg to -15deg transition (≈80 minutes of real time
in summer) now sweeps through golden -> rust -> panel -> void
instead of golden -> rust -> panel -> panel/void.
Three fixes:
1. getColor() switched from 'sunPos.altitude > 0' to
'> sunPalettes[0].alt' (-pi/2). The day palette interpolates
through twilight to deep-below-horizon, so an hour after sunset
(sunAlt ~ -15deg) we now stay on the day palette and get the
intended dark 'panel' colour instead of switching to the moon
palette immediately.
2. getNightColor() now actually applies MIN_NIGHT_BRIGHTNESS via
shiftColor toward black (the old 'brightness' var was unused
dead code).
3. applyWeatherTheme() cuts the weather tint by up to 70% at
night, so a 'cloudy' theme no longer washes the night sky with
the brighter weather sky colour.
The cloud horizon flickered when the camera was far from the
deck because the regular raymarch steps aligned with the cloud
boundary. Two cheap fixes:
- Per-pixel jitter (hash21 of gl_FragCoord + uTime) shifts each
ray's start by up to one stepSize; the brain averages the
random offsets into a smooth edge
- Wider smoothstep (0.18 -> 0.24) softens the binary cloud/no-cloud
boundary the jitter is now dithering across
- Cruise altitude 2.6 -> 2.0 (just above cloud ceiling y=1.5,
gives the 'just above the clouds' airliner feel)
- Expose __currentSunAlt (radians) from animate() so the
renderer can tell night from day without misreading
realDayProgress (which mixes pre-dawn + post-sunset hours)
- Replace (1 - uDayProgress) star gate by smoothstep over uSunAlt:
nightFactor = 1 when sun is well below horizon, 0 when above
- Widen sky band so stars are visible from the horizon upward
(cockpit-perspective frame is mostly below rd.y=0.3)
- Cloud ceiling lowered from y=1.8 to y=1.5 (thinner deck)
- Cruise raised from y=2.0 to y=2.6 (clear margin above clouds)
- Dive endpoint raised from y=0.9 to y=1.2 (skims top, never deep)
- Dive exponent 5 -> 8 (much rarer dives)
- Early-out and ceiling bounds updated to match new y=1.5
- Cruise altitude lowered to y=2.0 (just above cloud top at y=1.8)
- Look direction nearly level (rd.y ~ -0.05 at horizon line)
- Stars rendered as 3D hash-grid sphere, only above horizon and
only when uDayProgress < 1 (i.e. at night). Subtle sin twinkle
keeps them from looking like static dither.
Camera altitude is now driven by pow(sin,5) so the view stays at
y=2.6 most of the time and only briefly dips to y=0.9 to skim the
top of the cloud deck. Marching also skips the empty gap between
camera and cloud ceiling when looking down from above.
Reduce raymarch iterations from 28 to 18 and fBm octaves from
4 to 3 (with step size 0.55->0.85 to preserve cloud coverage).
Per-pixel cost drops by ~55% while visual density stays similar.
Raymarches a horizontal cloud layer (28 steps, 4-octave 3D fBm).
Camera path is sinusoidal in altitude so the view dips into the
cloud deck at intervals for a 'fly through the edges' feel.
Density threshold from weather: sunny -> sparse, stormy -> overcast.
Renderer is selected via CONFIG.renderer ('clouds2d' or 'clouds3d').
- Remove all hardcoded fallback hex/rgb values
- Skip draw until __currentPalette is published by parent
- applyWeatherTheme now also mixes cloudShadow (was dropped)
The 2D WebGL renderer now creates its own <canvas> overlay inside
the target element, instead of expecting the target to already be a
canvas. This fixes 'canvas.getContext is not a function' when the
target is a <div>.