Phaser AE

3D Sky and Day Cycle

Pro packv3.0.0-23DBackdrop

A sky for outdoor 3D scenes in which one number, the time of day, drives everything: the colours of the sky, the sun's position and light, the ambient, the fog and the reflections in the water. It is one full-screen pass on WebGPU and WebGL2, light enough for low-end devices.

This is a Pro pack. Upgrade to Phaser Pro to use it in your games.See Phaser Pro →

Released October 14, 2026 · updated October 19, 2026

3D Sky and Day Cycle: a screenshot of its demo
A note about these graphics

Expansion packs don't come with graphics. This screenshot is one way of drawing what the pack does, made for its demo: none of the sprites, tiles or backgrounds in it are in the pack.

That's by design. A pack is a game's logic, mechanics and effects, and it works with whatever art you give it. Some packs draw effects, backdrops or 3D scenes of their own, but none carries a game's sprites.

So the look is yours. Make the art on phaser.io, bring your own, or let your agent draw it in code: the same pack can look like anything.

The pack

sky3d

function sky3d(opts: Sky3dOptions = {}): Pack<{ sky: Sky3dApi }>

The sky-3d pack: a procedural sky drawn per pixel before the meshes, a day/night cycle that drives the scene's own sun, ambient and fog, and an env map baked from the same palette for anything that reflects. Requires a 3D world and supports WebGPU and WebGL2.

Helpers

bakeEnv

function bakeEnv(s: SkyState, o: EnvBakeOptions): EnvLevel[]

Bake the sky to an equirect field, mip chain included. u = atan2(x, z) / 2π + 0.5 and v = 0.5 − asin(y) / π, which is the convention every sampler here uses.

The gradient is the same one the dome shader draws — horizon to zenith on a 0.55 power, the sun-weighted glow band, and the fog floor below the horizon — so a reflection and the sky it reflects agree.

constellationDirs

function constellationDirs(): Array<{ x: number; y: number; z: number; b: number }>

The constellation stars as unit directions with a brightness each. Pure; baked once at module load.

ENV_SIZE

const ENV_SIZE: { width: number; height: number; mips: number; }

How many mip levels bakeEnv returns, and the size of level 0.

resolveSunPath

function resolveSunPath(path: SunPath = {}): Required<SunPath>

A sun path with every field filled in, or a thrown error naming what is out of range.

SKY_KEYS

const SKY_KEYS: readonly SkyKey[]

The default palette: ten hand-tuned keys across an Earth-like day, interpolated linearly in rgb, which is good enough at these saturations.

The palette CLOCK must agree with the sun GEOMETRY (with SUN_PATH the sun's elevation is 0 at exactly 6:00 and 18:00): the golden hour sits just BEFORE 18:00 and dusk just after. A "sunset" palette while the sun is already 20° under the horizon lights the world from below and the land goes black, so a palette written for another sun path moves its keys with it.

skyState

function skyState(hour: number, opts: SkyStateOptions = {}): SkyState

The day/night core: an hour 0..24 in (it wraps), the full palette and the sun's geometry out.

With the defaults the sun rises toward +x at 6:00 and sets toward −x at 18:00, standing about 78° high at noon with a lean toward −z so noon shadows are never degenerate, and it keeps sweeping below the horizon at night with the moon on the antipode. opts.palette and opts.sun replace the keyframes and the path; the palette is not validated here (it runs every step), so check an authored one once with validatePalette.

SUN_PATH

const SUN_PATH: Readonly<Required<SunPath>>

The default sun path: up at 6:00, down at 18:00, a 65° arc with a 0.45 lean (the noon sun at about 78°).

validatePalette

function validatePalette(palette: readonly SkyKey[]): void

Throw unless a palette can be interpolated: at least one key, hours in 0..24 in strictly ascending order, finite numbers and css colours. The message names the key and the field.

Types

Env3dBag

interface Env3dBag

What sky-3d publishes each frame under ctx.backend['env3d']: the sky, baked, for anything reflective.

  • view: GPUTextureView — The equirect env map, mipped. u = atan2(x, z) / 2π + 0.5, v = 0.5 − asin(y) / π.
  • sampler: GPUSampler — A linear, mipped, u-repeating sampler for it.
  • strength: number — How much of it a reflection should take, 0..1 — it falls with the storm dial.
  • mips: number — How many mip levels it carries, so a rough reflection can pick one.

EnvBakeOptions

interface EnvBakeOptions

What the bake needs beyond the day's palette.

  • coverage: number — Cloud sky-fraction 0..1 — a covered sky reflects flatter and brighter.
  • density: number — Cloud opacity 0..1.
  • storm: number — Storm dial 0..1 — darkens everything.
  • clouds: boolean — Are the clouds drawn at all.

EnvLevel

interface EnvLevel

One baked level of the env map.

  • w: number — Texel width.
  • h: number — Texel height.
  • data: Uint8Array — rgba8, row major.

Rgb

type Rgb = [number, number, number]

A colour as linear-ish rgb, 0..1.

Sky3dApi

interface Sky3dApi

game.sky. Every field is live.

  • enabled: boolean — Draw the sky and its environment map. Lighting control remains independently governed by driveLighting.
  • time: number — Time of day, 0..1: 0 midnight, 0.25 sunrise, 0.5 noon, 0.75 sunset. Wraps.
  • hour: number — The same clock in hours, 0..24. Setting either moves the other.
  • daySeconds: number — Seconds a full day takes; 0 stops the clock. Default 0.
  • northOffset: number — Which way the sun rises from, degrees. Default 90.
  • brightness: number — Overall brightness. Default 1.
  • haze: number — How hazy the horizon is, 0..1. Default 0.35.
  • stars: number — Star and moon brightness at night, 0..1; 0 is an empty sky. Default 1.
  • clouds: number — Cloud cover, 0..1; 0 is clear and skips the cloud work entirely. Default 0.45.
  • cloudDensity: number — Cloud opacity, 0..1. Default 0.65.
  • cloudSpeed: number — How fast the clouds drift, on their own clock. Default 1.
  • wind: { x: number; z: number } — Which way they drift. Default a gentle westerly.
  • storm: number — The storm dial, 0..1: more cloud, darker cloud, dimmer sun. Default 0.
  • driveLighting: boolean — Drive render3d.lighting — sun, ambient and fog — from the sky. Default true.
  • palette: readonly SkyKey[] — The day's keyframes, SKY_KEYS by default. Assign a new array to restyle the whole day (an alien sky, a stylised one); it is validated and copied, and reading returns the live copy. Throws on a palette validatePalette refuses.
  • sunPath: SunPath — The sun's path: rise and set hours, peak and depth elevations, lean. Assign to change it; reading gives every field. Throws on a path out of range.
  • cloudScale: number — Cloud pattern scale: higher is smaller, busier clouds. Default 0.85.
  • cycle(seconds: number): void — Set the clock running: one full day every seconds. 0 stops it.
  • readonly state: SkyState — The whole palette and sun geometry for right now.
  • readonly sunDirection: { x: number; y: number; z: number } — The direction the light TRAVELS, normalised: from the sun toward the ground by day (so y is negative), and the moon's light, down from the antipode, at deep night. It is what render3d.lighting.sun is set to. skymax's sunDirection() points the other way, toward the sun: use sunPosition for that.
  • readonly sunPosition: { x: number; y: number; z: number } — The unit vector TOWARD the sun, compass turn included: what a lens flare or a sun-facing sprite wants. Below the horizon at night.
  • readonly sunHeight: number — How high the sun is, the y of sunPosition: −1 straight down to 1 straight up (about 0.98 at the default noon).

Sky3dOptions

interface Sky3dOptions

Manifest options — the same fields as the API, all optional.

  • time?: number — Starting time of day, 0..1. Default 0.3.
  • hour?: number — Starting time in hours, 0..24. Overrides time.
  • daySeconds?: number — Seconds a full day takes; 0 holds it still. Default 0.
  • northOffset?: number — Which way the sun rises from, degrees. Default 90.
  • brightness?: number — Overall brightness. Default 1.
  • haze?: number — Horizon haze, 0..1. Default 0.35.
  • stars?: number — Star brightness, 0..1. Default 1.
  • clouds?: number — Cloud cover, 0..1. Default 0.45.
  • cloudDensity?: number — Cloud opacity, 0..1. Default 0.65.
  • cloudSpeed?: number — Cloud drift speed. Default 1.
  • wind?: { x: number; z: number } — Cloud drift direction. Default { x: 1, z: 0.35 }.
  • storm?: number — Storm dial, 0..1. Default 0.
  • driveLighting?: boolean — Drive the scene lighting from the sky. Default true.
  • palette?: SkyKey[] — The day's keyframes, by hour. Default SKY_KEYS, an Earth-like day.
  • sunPath?: SunPath — The sun's path. Default SUN_PATH: up at 6:00, down at 18:00, 65° at noon.
  • cloudScale?: number — Cloud pattern scale: higher is smaller, busier clouds. Default 0.85.

SkyKey

interface SkyKey

One keyframe of the day's palette, at an hour. skyState interpolates linearly between the two keys either side of the hour, wrapping from the last key round to the first. Colours are css strings.

  • t: number — The hour this key sits at, 0 (inclusive) to 24 (exclusive). Keys run in ascending order.
  • zenith: string — The sky colour straight up.
  • horizon: string — The sky colour at the horizon.
  • glow: string — The glow band around the sun near the horizon.
  • sun: string — The colour of the direct light.
  • sunI: number — Direct light strength, about 1 at noon and near 0 at night.
  • ambient: number — Ambient strength.
  • ambSky: string — Ambient arriving from above.
  • ambGround: string — Ambient bouncing from below.
  • fog: string — The fog colour.
  • stars: number — Star opacity, 0 by day to 1 at night.
  • moon: number — Moon opacity, 0..1.

SkyState

interface SkyState

The whole sky for one moment of the day. Returned by skyState.

  • sunPos: Rgb — Unit vector pointing TOWARD the sun. Below the horizon at night.
  • sunDir: Rgb — The direction the light TRAVELS — what a renderer's sun wants: −sunPos by day, and the moon's light (down from the antipode) at deep night.
  • sunColor: Rgb — The colour of the direct sunlight.
  • sunI: number — Direct sun strength: about 1 at noon, near 0 at night.
  • zenith: Rgb — The sky colour straight up.
  • horizon: Rgb — The sky colour at the horizon.
  • glow: Rgb — The glow around the sun near the horizon.
  • ambient: number — Ambient strength.
  • ambSky: Rgb — Ambient arriving from above.
  • ambGround: Rgb — Ambient bouncing from below.
  • fog: Rgb — The fog colour for this hour.
  • stars: number — Star opacity — 0 by day.
  • moon: number — Moon opacity.

SkyStateOptions

interface SkyStateOptions

What skyState reads beyond the hour: the palette and the sun's path.

  • palette?: readonly SkyKey[] — The day's keyframes. Default SKY_KEYS.
  • sun?: SunPath — The sun's path. Default SUN_PATH; unset fields take its values.

SunPath

interface SunPath

The sun's daily path. The sun rises at rise, climbs to its highest halfway to set, sets at set, and keeps sweeping below the horizon through the night to its lowest halfway to the next rise, with the moon on the antipode. Before the compass turn (northOffset) it rises toward +x and sets toward −x, leaning toward −z.

peak and lean shape the arc together: the sun climbs as if to peak, and the lean tips the path toward −z, which raises the mid-day sun to atan(tan(peak) / lean) (about 78° with the defaults, 65° and 0.45). A small peak with a large lean is a low winter sun; lean 0 passes straight overhead.

  • rise?: number — The hour the sun rises, 0 (inclusive) to 24 (exclusive). Default 6.
  • set?: number — The hour it sets: after rise, and less than a day after it (it may pass midnight). Default 18.
  • peak?: number — How high the arc climbs by day, radians, before the lean tips it; above 0, at most π/2. Default 65° (about 1.134).
  • depth?: number — How far it sinks at night, radians, before the lean; 0 to π/2. Default the same as peak.
  • lean?: number — How far the arc leans off the east-west line toward −z, as a horizontal ratio: 0 passes straight overhead (at peak π/2), 0.45 is a temperate lean that keeps noon shadows from degenerating. Default 0.45.