More
Under the hood
What happens in update() and render() each frame, and what the package changes over the weatherpro.html page it grew from.
#update(dt)
#Ease the weather
Each eased key moves toward its target by 1 − exp(−dt × transitionRate). The time eases the short way round the clock. state.cloud is raised to at least what the rain, snow and lightning imply.
#Gust the wind
A slow product of sines gives a gust factor of 0.5–1.2. The wind vector is wind × 13 m/s × gust, and its direction wanders ±0.25 rad around windDirection.
#Read the camera and lights
The camera position, view-projection and its inverse go into the shared uniform block, together with up to four local lights, read fresh from your PointLights.
#Surfaces, particles, lightning, fog
The wetness and snow model steps forward. Rain and snow set their instance counts from the intensity and the tier. Lightning fires automatic strikes and thunder events and computes the flash. The fog maps the weather to density and height, and drives scene.fog on the plain path.
#Occlusion
When the camera leaves the middle 40 % of the map, or when something asked for it, the scene is rendered top-down into a depth texture. The weather meshes and anything with userData.weatherOccluder = false are hidden for that render.
Everything shares one uniform block, weather.uniforms, bound by reference into every weather shader and every patched material. No uniform is copied per material per frame.
#render(target)
#Scene
Your scene renders into a half-float target with depth. Patched ground materials write their puddle reflectivity into alpha.
#Fog
A reduced-resolution raymarch of exponential height fog. It samples the shadowLight's shadow map with hardware comparison (sampler2DShadow) for the shafts, adds lamp halos and lightning in-scattering around the bolt, and drifts with the wind.
#Reflections and composite
A screen-space ray for each puddle pixel, with the raindrop rings bending its normal. Misses fall back to the sky colours. Then the fog is composited on top.
#Bloom and tone
A six-level bloom chain, then exposure × ACES to the output.
#Parity with weatherpro.html

snowfall, same view. The page left pitched roofs almost bare and put snow under the well roof. The package covers the slopes, keeps sheltered ground clear and adds glints.The presets carry the page's values. Only the key names changed: thunder → thunderstorm, foggy → foggyDawn, light → lightning, windDir → windDirection, and so on. The full list is in the changelog.
#What changed
#Snow on roof slopes
Coverage uses the true face normal, from screen-space derivatives. The page used the smooth vertex normals, which under-report a pitched roof's slope and left roofs almost bare. Snow is now also shaded with the face normal, glints in the sun (shadow-aware), and turns cool blue in shade.
#Dry under roofs
Patched surfaces read the occlusion map. There's no snow, no puddles and no ripples under the well roof, the benches or the eaves. The shelter edge is soft and slanted with the wind, and tall covers such as tree crowns let some weather through.
#Occlusion that follows the camera
The page baked one 80 m map at the origin. The package keeps an 80 m map centred near the camera and re-renders it as the camera moves, so the weather works anywhere in a large world. setOccluders() limits it to the objects you choose.
#Rain that reads as rain
A motion-blurred drop profile with a brighter head, per-drop brightness, and drops that glow when you look toward the sun or moon.
#Puddle reflections that ripple
The SSR normals include the same raindrop rings as the surface, so reflections break up where the drops land.
#Lightning in the fog
The bolt lights the fog volume around it. The demo sky gets a hot, cloud-structured core where the bolt enters the deck. on('strike') and on('thunder') report distance and delay.
downpour in the demo, lamps on#A library instead of a page
- three r160 from a CDN → npm
^0.186.THREE.Clock→THREE.Timer.PCFSoftShadowMap→PCFShadowMap, sampled withsampler2DShadowin the fog pass. - Materials are patched with
patchMaterial(), lighting comes fromsetLighting(), and the particles live in your scene. - Plain
renderer.render()works, with aFogExp2fallback and puddles without SSR. - Presets are data files, with Copy preset JSON,
getParams(), and easedset()andloadPreset().
#Limitations
- WebGL2 and
EXT_color_buffer_floatare required. There's no WebGPU or TSL version. render()uses its own HDR chain and ACES tonemapper, and doesn't compose withEffectComposerpasses.- The occlusion map is a single top-down layer. Moving occluders need a manual refresh, and everything under the top surface is sheltered.
- SSR is screen-space: off-screen objects aren't reflected.
- Puddles only appear on
{ ground: true }materials and assume a roughly horizontal surface. Wetness has no per-object drying. - Up to four local lights affect the particles and fog.
patchMaterialsupports standard and physical materials only. - Audio is synthesized noise, with no samples, and needs a user gesture to start.