NatureGL Skyv1.1.0

Guides

Performance

Most of the frame goes to the cloud march at a fraction of the screen resolution. The bakes are throttled and time-sliced, so there's nothing sudden, and all of it is tunable.

#Measured

median across presets on high
~75 fps
clear and cirrus skies
110+ fps
full-sky decks: dawn, cumulonimbus
30–45 fps

These are for an Apple M5, headless Chromium on ANGLE/Metal, 1280×720 at pixel ratio 1, in the full demo scene with the post pass. The GPU was shared with other jobs while measuring, so treat them as lower bounds. npm test prints the numbers for your machine.

#Where the time goes

WorkWhenScales with
Cloud marchEvery frame, at marchScale of the drawing buffermarchSteps, lightSteps, cloud cover
Temporal resolveEvery frame, at historyScaleResolution
Sky-view LUTThrottledCheap
Cloud shadow mapEvery shadowInterval s (dynamic)shadowSize, shadowSteps
Env bakeEvery envInterval s, spread over envSlices framesenvSize, envSteps
PMREMWith each env refreshpmremSamples (reduced from three's 256)
Post pass (optional)Every frameraySteps, msaa, bloom

#Levers, cheapest first

#Drop a tier

js
sky.setQualityLevel('medium');

#Override the expensive fields

js
sky.setQualityLevel('high', { marchScale: 0.4, marchSteps: 44, raySteps: 8 });

#Freeze the weather

js
sky.setCloudRenderingMode('static');   // re-bakes only when something changes

#Cap the pixel ratio

js
renderer.setPixelRatio(Math.min(devicePixelRatio, 1.5));
sky.resize();

The march resolution is relative to the drawing buffer, so a 2× retina display costs four times as much as 1×.

#Adaptive quality

The library doesn't change tiers on its own. That's deliberate, because a tier change resets history and re-bakes. For adaptive quality, watch your own frame time and step down with hysteresis:

js
let slow = 0;
renderer.setAnimationLoop(t => {
  const dt = timer.update(t).getDelta();
  slow = dt > 1 / 45 ? slow + 1 : Math.max(0, slow - 2);
  if (slow > 120 && sky.quality !== 'low') {
    sky.setQualityLevel({ ultra: 'high', high: 'medium', medium: 'low' }[sky.quality]);
    slow = 0;
  }
  sky.update(dt);
  renderer.render(scene, camera);
});