Skip to content

Basic Example

Create a Three.js scene with an animated procedural sky. Choose a setup:


TypeScript + Vite

Step 1: Create a Vite Project

bash
npm create vite@latest my-sky-project -- --template vanilla-ts
cd my-sky-project

Step 2: Install Three.js

bash
npm install three@^0.185.0
npm install --save-dev @types/three@^0.185.0

Step 3: Add the Library

  1. Unzip threejs-sky-pro.zip into your root directory.

  2. Create a threejs-sky-pro sub-directory within your src directory.

  3. Copy the contents of the build directory into the directory you just created. Copy everything, including data/ — the cloud-noise volumes load from there at runtime.

  4. Confirm the following file structure:

my-sky-project/
├── src/
│   ├── threejs-sky-pro/
│   │   ├── index.js      ← Main library bundle
│   │   ├── index.js.map  ← Source map
│   │   ├── index.d.ts    ← TypeScript declarations
│   │   └── data/         ← Cloud-noise volumes
│   └── main.ts
├── index.html
└── package.json

Step 4: Update index.html

Replace the contents of index.html:

html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Three.js Sky Pro</title>
    <style>
      * {
        margin: 0;
        padding: 0;
        box-sizing: border-box;
      }
      body {
        overflow: hidden;
        background: #000;
      }
      canvas {
        display: block;
      }
    </style>
  </head>
  <body>
    <script type="module" src="/src/main.ts"></script>
  </body>
</html>

Step 5: Add the Application Code

Replace src/main.ts with:

typescript
import * as THREE from "three/webgpu";
import { pass, vec4 } from "three/tsl";
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
import { SkySystem, PRESETS } from "./threejs-sky-pro";

async function main() {
  // Create renderer
  const renderer = new THREE.WebGPURenderer({ antialias: true });
  renderer.setSize(window.innerWidth, window.innerHeight);
  renderer.toneMapping = THREE.ACESFilmicToneMapping;
  document.body.appendChild(renderer.domElement);
  await renderer.init();

  // Create scene and camera
  const scene = new THREE.Scene();
  const camera = new THREE.PerspectiveCamera(
    60,
    window.innerWidth / window.innerHeight,
    0.1,
    1_000_000,
  );
  camera.position.set(0, 100, 0);

  // Drag to orbit, scroll to zoom. Target a point on the horizon so the
  // initial view looks out at the sky rather than straight down.
  const controls = new OrbitControls(camera, renderer.domElement);
  controls.target.set(0, 100, -100);
  controls.enableDamping = true;
  controls.update();

  // Create the sky. Its backdrop meshes are added to the scene for you.
  const sky = await SkySystem.create({
    renderer,
    camera,
    scene,
    quality: "high",
  });

  // Apply a bundled look preset (pure look — doesn't change render cost).
  await sky.applyPreset(PRESETS.partlyCloudy);

  // Sun position: elevation and compass azimuth, in degrees.
  sky.sun.setFromAngles(35, 120);

  // Post chain: scene → fog/god rays → exposure → tone map → canvas.
  const scenePass = pass(scene, camera);
  let output = scenePass.getTextureNode("output");
  output = sky.applyTo(output, scenePass);
  output = vec4(output.rgb.mul(sky.atmosphere.exposure), output.a);
  const postProcessing = new THREE.RenderPipeline(renderer, output);

  // Handle window resize
  window.addEventListener("resize", () => {
    const w = window.innerWidth;
    const h = window.innerHeight;
    renderer.setSize(w, h);
    camera.aspect = w / h;
    camera.updateProjectionMatrix();
    sky.resize(w, h);
  });

  // Compile shaders before starting animation
  await renderer.compileAsync(scene, camera);

  // Animation loop
  const clock = new THREE.Clock();
  function animate() {
    requestAnimationFrame(animate);
    controls.update();
    sky.update(clock.getDelta());
    postProcessing.render();
  }

  animate();
}

main();

Night sky

The star panorama is not bundled. Without one, stars are disabled and the moon uses a flat tinted disc. This example keeps the sun up, so no starmap is needed. To add stars to a day/night cycle, load an equirectangular starmap and pass it to SkySystem.create as nightSky: { texture }. See Day/Night Cycle for setup and properly licensed starmaps.

Step 6: Run the Project

bash
npm run dev

Open the URL shown in the terminal, usually http://localhost:5173.


Plain JavaScript + CDN

This setup loads Three.js from a CDN through an import map.

Step 1: Create Project Files

Create a folder with the following structure:

my-sky-project/
├── lib/
│   └── threejs-sky-pro/
├── src/
│   └── main.js
└── index.html

Step 2: Add the Library

  1. Unzip threejs-sky-pro.zip

  2. Copy the contents of the build directory into lib/threejs-sky-pro. Copy everything, including data/ — the cloud-noise volumes load from there at runtime.

Step 3: Create index.html

Use an import map to load Three.js and its addons from the same CDN version.

html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Three.js Sky Pro</title>
    <style>
      * {
        margin: 0;
        padding: 0;
        box-sizing: border-box;
      }
      body {
        overflow: hidden;
        background: #000;
      }
      canvas {
        display: block;
      }
    </style>

    <!-- Import map for Three.js CDN -->
    <script type="importmap">
      {
        "imports": {
          "three": "https://cdn.jsdelivr.net/npm/three@0.185.0/build/three.webgpu.min.js",
          "three/webgpu": "https://cdn.jsdelivr.net/npm/three@0.185.0/build/three.webgpu.min.js",
          "three/tsl": "https://cdn.jsdelivr.net/npm/three@0.185.0/build/three.tsl.min.js",
          "three/addons/": "https://cdn.jsdelivr.net/npm/three@0.185.0/examples/jsm/"
        }
      }
    </script>
  </head>
  <body>
    <script type="module" src="./src/main.js"></script>
  </body>
</html>

Step 4: Create main.js

javascript
import * as THREE from "three/webgpu";
import { pass, vec4 } from "three/tsl";
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
import { SkySystem, PRESETS } from "../lib/threejs-sky-pro/index.js";

async function main() {
  // Create renderer
  const renderer = new THREE.WebGPURenderer({ antialias: true });
  renderer.setSize(window.innerWidth, window.innerHeight);
  renderer.toneMapping = THREE.ACESFilmicToneMapping;
  document.body.appendChild(renderer.domElement);
  await renderer.init();

  // Create scene and camera
  const scene = new THREE.Scene();
  const camera = new THREE.PerspectiveCamera(
    60,
    window.innerWidth / window.innerHeight,
    0.1,
    1_000_000,
  );
  camera.position.set(0, 100, 0);

  // Drag to orbit, scroll to zoom. Target a point on the horizon so the
  // initial view looks out at the sky rather than straight down.
  const controls = new OrbitControls(camera, renderer.domElement);
  controls.target.set(0, 100, -100);
  controls.enableDamping = true;
  controls.update();

  // Create the sky. Its backdrop meshes are added to the scene for you.
  const sky = await SkySystem.create({
    renderer,
    camera,
    scene,
    quality: "high",
  });

  // Apply a bundled look preset (pure look — doesn't change render cost).
  await sky.applyPreset(PRESETS.partlyCloudy);

  // Sun position: elevation and compass azimuth, in degrees.
  sky.sun.setFromAngles(35, 120);

  // Post chain: scene → fog/god rays → exposure → tone map → canvas.
  const scenePass = pass(scene, camera);
  let output = scenePass.getTextureNode("output");
  output = sky.applyTo(output, scenePass);
  output = vec4(output.rgb.mul(sky.atmosphere.exposure), output.a);
  const postProcessing = new THREE.RenderPipeline(renderer, output);

  // Handle window resize
  window.addEventListener("resize", () => {
    const w = window.innerWidth;
    const h = window.innerHeight;
    renderer.setSize(w, h);
    camera.aspect = w / h;
    camera.updateProjectionMatrix();
    sky.resize(w, h);
  });

  // Compile shaders before starting animation
  await renderer.compileAsync(scene, camera);

  // Animation loop
  const clock = new THREE.Clock();
  function animate() {
    requestAnimationFrame(animate);
    controls.update();
    sky.update(clock.getDelta());
    postProcessing.render();
  }

  animate();
}

main();

Night sky

The star panorama is not bundled. Without one, stars are disabled and the moon uses a flat tinted disc. This example keeps the sun up, so no starmap is needed. To add stars to a day/night cycle, load an equirectangular starmap and pass it to SkySystem.create as nightSky: { texture }. See Day/Night Cycle for setup and properly licensed starmaps.

Step 5: Serve the Files

Serve the files over HTTP because browsers block ES modules loaded through file://. Use one of these commands:

bash
# Using Python
python3 -m http.server 8080

# Using Node.js (npx)
npx serve .

# Using PHP
php -S localhost:8080

Open http://localhost:8080 in your browser.

You can also use a development server such as the VS Code Live Server extension.

CORS Note

Import maps with CDN URLs require the page to be served over HTTP/HTTPS. Opening index.html directly as a file won't work.


Result

The result is a daytime sky with drifting volumetric clouds. Drag to orbit and scroll to zoom.

Next Steps

Commercial License - All Rights Reserved.