Giza
Three pyramids on a desert horizon, running a 90-second day/night cycle with a deterministic weather system on top. Every cycle rolls a hashed die to pick clear, lightning storm, or dust storm weather — the same cycle index always produces the same weather, so the scene is reproducible.
The dawn/day/dusk/night phases are explicit tent-functions of the cycle
fract, not a sine wrap, so the plateau of full daylight and the long
slow sunset have hand-set durations. Golden-hour lighting peaks during
the transitions and drives the sunset gradient, cloud color, horizon
haze, and pyramid edge highlights.
The matrix rain reappears here as a ghost layer in the sky — dim at midday, prominent at night, and reversing flow direction from falling-at-day to rising-at-night. Lightning strikes are their own timeline: each 3-second window either rolls a strike (bolt + flash) or doesn’t; dust storms boost the FBM sand layers, tint the sky sandy, and apply a whole-screen haze.
// Three pyramids on a desert horizon with 90s day/night cycle and
// deterministic weather (clear / lightning / dust). Ghost matrix rain
// in the sky. Palette = iAccent + three fixed hues.
// @slider tempo 0.1 3.0 1.0 0.05
uniform float tempo;
float hash(vec2 p){
return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453);
}
float valueNoise(vec2 p){
vec2 i = floor(p);
vec2 f = fract(p);
f = f * f * (3.0 - 2.0 * f);
return mix(mix(hash(i), hash(i + vec2(1.0, 0.0)), f.x),
mix(hash(i + vec2(0.0, 1.0)), hash(i + vec2(1.0, 1.0)), f.x), f.y);
}
float fbm(vec2 p){
float v = 0.0;
float a = 0.5;
vec2 shift = vec2(100.0);
for(int i = 0; i < 5; i++){
v += a * valueNoise(p);
p = p * 2.0 + shift;
a *= 0.5;
}
return v;
}
// signed distance to a triangular pyramid silhouette (2D)
float pyramidShape(vec2 uv, vec2 base, float halfWidth, float height){
vec2 p = uv - base;
p.x = abs(p.x);
if(p.y < 0.0) return 1.0;
if(p.y > height) return 1.0;
float edge = halfWidth * (1.0 - p.y / height);
return p.x - edge;
}
float strokeLine(float d, float w){
return 1.0 - smoothstep(w - 0.02, w + 0.03, d);
}
// smaller (20-shape) subset of the matrix procedural font
float glyph(vec2 cellUV, float seed){
vec2 p = cellUV - 0.5;
float w = 0.065;
int tt = int(floor(fract(seed * 7.31) * 20.0));
float s = 0.0;
if(tt == 0){
s = strokeLine(abs(p.x), w);
} else if(tt == 1){
s = strokeLine(abs(p.y), w);
} else if(tt == 2){
s = strokeLine(min(abs(p.x), abs(p.y)), w);
} else if(tt == 3){
s = strokeLine(min(abs(p.x - p.y), abs(p.x + p.y)) * 0.7071, w);
} else if(tt == 4){
s = strokeLine(abs(length(p) - 0.28), w);
} else if(tt == 5){
s = 1.0 - smoothstep(0.15, 0.19, length(p));
} else if(tt == 6){
float bx = max(abs(p.x), abs(p.y));
s = strokeLine(abs(bx - 0.28), w);
} else if(tt == 7){
float d = min(min(abs(p.y - 0.22), abs(p.y)), abs(p.y + 0.22));
s = strokeLine(d, w * 0.55);
} else if(tt == 8){
float dd = abs(p.x) + abs(p.y);
s = strokeLine(abs(dd - 0.32), w);
} else if(tt == 9){
float d1 = min(abs(p.x), abs(p.y));
float d2 = min(abs(p.x - p.y), abs(p.x + p.y)) * 0.7071;
s = strokeLine(min(d1, d2), w * 0.75);
} else if(tt == 10){
float cd = abs(length(p) - 0.3);
s = strokeLine(cd, w) * smoothstep(-0.05, 0.05, p.y);
} else if(tt == 11){
float vert = strokeLine(abs(p.x + 0.15), w) * step(0.0, p.y);
float horiz = strokeLine(abs(p.y), w) * step(-0.15, p.x);
s = max(vert, horiz);
} else if(tt == 12){
vec2 tp = vec2(abs(p.x), p.y + 0.05);
float tri = max(-tp.y - 0.2, tp.x * 0.866 + tp.y * 0.5 - 0.22);
s = strokeLine(abs(tri), w);
} else if(tt == 13){
vec2 tp = vec2(abs(p.x), -p.y + 0.05);
float tri = max(-tp.y - 0.2, tp.x * 0.866 + tp.y * 0.5 - 0.22);
s = strokeLine(abs(tri), w);
} else if(tt == 14){
float stem = strokeLine(abs(p.x), w) * step(p.y, 0.15);
float chevron = strokeLine(abs(abs(p.x) + p.y - 0.35) * 0.707, w)
* step(0.12, p.y) * step(p.y, 0.4);
s = max(stem, chevron);
} else if(tt == 15){
float circ = strokeLine(abs(length(p) - 0.28), w * 0.7);
float pl = strokeLine(min(abs(p.x), abs(p.y)), w * 0.5);
s = max(circ, pl);
} else if(tt == 16){
float vert = strokeLine(abs(p.x), w) * step(p.y, 0.25);
float horiz = strokeLine(abs(p.y - 0.25), w);
s = max(vert, horiz);
} else if(tt == 17){
float wave = abs(p.y - 0.15 * sin(p.x * 10.0));
s = strokeLine(wave, w);
} else if(tt == 18){
float cd = abs(length(p) - 0.3);
s = strokeLine(cd, w) * smoothstep(0.05, -0.05, p.y);
} else {
vec2 g = floor(cellUV * vec2(3.0, 5.0));
float on = step(0.45, hash(g + seed * 17.3));
vec2 f = fract(cellUV * vec2(3.0, 5.0));
float mask = smoothstep(0.0, 0.15, f.x) * smoothstep(1.0, 0.85, f.x)
* smoothstep(0.0, 0.15, f.y) * smoothstep(1.0, 0.85, f.y);
s = on * mask;
}
float margin = smoothstep(0.0, 0.08, cellUV.x) * smoothstep(1.0, 0.92, cellUV.x)
* smoothstep(0.0, 0.08, cellUV.y) * smoothstep(1.0, 0.92, cellUV.y);
return s * margin;
}
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
vec2 uv = fragCoord / iResolution.xy;
float aspect = iResolution.x / iResolution.y;
float t = iTime * 0.15 * tempo;
float rawTime = iTime;
float syncTime = iTime * tempo;
vec3 c1 = iAccent;
vec3 c2 = vec3(0.35, 0.75, 1.00);
vec3 c3 = vec3(1.00, 0.35, 0.55);
vec3 c4 = vec3(0.55, 0.25, 0.85);
float horizon = 0.32;
// 90-second day/night cycle with hand-set phase boundaries
float cyclePeriod = 90.0;
float cycle = fract(rawTime / cyclePeriod);
float dawnEnd = 0.18;
float duskStart = 0.50;
float duskEnd = 0.78;
float arcLight;
if(cycle < dawnEnd) arcLight = smoothstep(0.0, dawnEnd, cycle);
else if(cycle < duskStart) arcLight = 1.0;
else if(cycle < duskEnd) arcLight = smoothstep(duskEnd, duskStart, cycle);
else arcLight = 0.0;
float daylight = arcLight;
// golden hour peaks during dawn and dusk transitions
float goldenHour = smoothstep(0.0, 0.4, arcLight) * smoothstep(0.85, 0.4, arcLight);
float nightAmount = 1.0 - daylight;
// deterministic weather per cycle: clear / lightning / dust
float cycleIdx = floor(rawTime / cyclePeriod);
float weatherSeed = hash(vec2(cycleIdx, 42.17));
float isLightning = step(0.33, weatherSeed) * (1.0 - step(0.66, weatherSeed));
float isDustStorm = step(0.66, weatherSeed);
float weatherStr = smoothstep(0.0, 0.3, daylight);
vec3 col = vec3(0.0);
// sky
if(uv.y > horizon){
float skyT = (uv.y - horizon) / (1.0 - horizon);
vec3 nightBot = c4 * 0.2;
vec3 nightTop = c4 * 0.08;
vec3 nightSky = mix(nightBot, nightTop, skyT);
vec3 sunsetBottom = mix(c1, c2, 0.3) * 0.9;
vec3 sunsetMid = mix(c2, c3, 0.5) * 0.5;
vec3 sunsetTop = c4 * 0.4;
vec3 sunsetSky;
if(skyT < 0.3) sunsetSky = mix(sunsetBottom, sunsetMid, skyT / 0.3);
else sunsetSky = mix(sunsetMid, sunsetTop, (skyT - 0.3) / 0.7);
vec3 dayBottom = mix(c1, c2, 0.5) * 1.1;
vec3 dayTop = mix(c3, c4, 0.3) * 0.55;
vec3 daySky = mix(dayBottom, dayTop, skyT);
vec3 sky = nightSky;
sky = mix(sky, sunsetSky, goldenHour + daylight * 0.2);
sky = mix(sky, daySky, daylight * (1.0 - goldenHour * 0.5));
col = sky;
col = mix(col, mix(c1, c2, 0.3) * 0.35, isDustStorm * weatherStr * 0.45 * (1.0 - skyT * 0.4));
col *= 1.0 - isLightning * weatherStr * 0.3;
// FBM cloud layer
vec2 cloudUV = vec2(uv.x * aspect * 1.5 + t * 0.08, skyT * 2.0 + t * 0.02);
float cloud1 = fbm(cloudUV * 2.0 + vec2(0.0, 7.3));
float cloud2 = fbm(cloudUV * 3.0 + vec2(13.0, 0.0) - t * 0.03);
cloud1 = smoothstep(0.40, 0.70, cloud1);
cloud2 = smoothstep(0.45, 0.72, cloud2);
float cloudBand = smoothstep(0.0, 0.08, skyT) * smoothstep(0.5, 0.2, skyT);
float cloudAlpha = (cloud1 * 0.6 + cloud2 * 0.4) * cloudBand;
vec3 cloudLitSunset = mix(c1, c2, cloud2) * 0.5;
vec3 cloudLitDay = mix(c1, c3, 0.3) * 0.3;
vec3 cloudLit = mix(cloudLitSunset, cloudLitDay, daylight * (1.0 - goldenHour));
vec3 cloudDark = c4 * 0.1;
float cloudVis = max(goldenHour, daylight * 0.5);
cloudAlpha *= 1.0 + isLightning * weatherStr * 2.5;
cloudVis = max(cloudVis, isLightning * weatherStr);
col = mix(col, mix(cloudDark, cloudLit, cloud1), cloudAlpha * 0.35 * cloudVis);
// horizon haze glow
float horizGlow = exp(-(skyT * skyT) * 15.0) * mix(0.03, 0.3, max(goldenHour, daylight * 0.3));
col += c1 * horizGlow;
// ghost matrix rain, dim at day / prominent at night, flow reverses
float matrixIntensity = mix(0.08, 0.5, nightAmount);
vec3 matrixCol = vec3(0.0);
for(int layer = 0; layer < 4; layer++){
float fi = float(layer);
float cols = 16.0 + fi * 5.0;
float cellW = iResolution.x / cols;
float cellH = cellW * 1.6;
vec2 fragPos = fragCoord;
vec2 cell = vec2(floor(fragPos.x / cellW), floor(fragPos.y / cellH));
vec2 cellUV = vec2(fract(fragPos.x / cellW), fract(fragPos.y / cellH));
float colSeed = hash(vec2(cell.x, fi * 73.13));
float speed = (0.25 + colSeed * 0.5) * tempo;
float phase = colSeed * 100.0;
float streamLen = 3.0 + colSeed * 6.0;
float gapLen = 16.0 + colSeed * 24.0;
float period = streamLen + gapLen;
float rainDir = mix(-1.0, 1.0, daylight);
float cyclePos = mod(cell.y + rainDir * syncTime * speed + phase, period);
if(cyclePos < streamLen){
float dist = cyclePos;
float charSeed = hash(cell + vec2(fi * 37.0, floor(syncTime * (0.375 + colSeed * 0.5) * tempo)));
float g = glyph(cellUV, charSeed);
float brightness;
if(dist < 1.0){
brightness = 1.0;
} else {
brightness = max(0.05, 1.0 - (dist - 1.0) / (streamLen - 1.0));
brightness *= brightness;
}
float fog = 1.0 / (1.0 + fi * 0.6);
int colorIdx = int(mod(cell.x + fi, 4.0));
vec3 baseColor;
if(colorIdx == 0) baseColor = c1;
else if(colorIdx == 1) baseColor = c2;
else if(colorIdx == 2) baseColor = c3;
else baseColor = c4;
vec3 glyphCol;
if(dist < 1.0) glyphCol = mix(baseColor * 1.5, vec3(1.0), 0.6);
else glyphCol = baseColor * 1.5 * brightness;
matrixCol += glyphCol * g * fog;
}
}
float matrixFade = smoothstep(0.0, 0.15, skyT);
col += matrixCol * matrixIntensity * matrixFade;
// stars
if(nightAmount > 0.05 && skyT > 0.15){
vec2 starUV = floor(uv * vec2(90.0 * aspect, 90.0));
float starHash = hash(starUV);
float twinkle = sin(t * 4.0 + starHash * 6.28) * 0.5 + 0.5;
if(starHash > 0.986){
float starBright = (0.5 + 0.5 * twinkle) * nightAmount;
starBright *= smoothstep(0.15, 0.4, skyT);
col += vec3(starBright);
}
}
}
// desert ground
if(uv.y <= horizon){
float groundT = (horizon - uv.y) / horizon;
vec3 sandNearDay = mix(c1, c3, 0.4) * 0.45;
vec3 sandFarDay = mix(c1, c2, 0.3) * 0.55;
vec3 sandNearNight = c4 * 0.12;
vec3 sandFarNight = c4 * 0.08;
vec3 sandNear = mix(sandNearNight, sandNearDay, max(daylight, goldenHour * 0.7));
vec3 sandFar = mix(sandFarNight, sandFarDay, max(daylight, goldenHour * 0.7));
vec3 sandCol = mix(sandFar, sandNear, groundT);
float sandNoise = fbm(vec2(uv.x * 20.0 * aspect, uv.y * 30.0 + t * 0.5)) * 0.08;
sandCol += sandNoise * c1 * 0.3 * max(daylight, goldenHour * 0.5);
col = sandCol;
float sandHorizGlow = exp(-groundT * 8.0) * mix(0.03, 0.3, max(goldenHour, daylight * 0.4));
col += mix(c1, c2, 0.3) * sandHorizGlow;
}
// three pyramids
float p1 = pyramidShape(uv, vec2(0.55, horizon - 0.005), 0.11, 0.16);
float p2 = pyramidShape(uv, vec2(0.35, horizon - 0.003), 0.09, 0.14);
float p3 = pyramidShape(uv, vec2(0.20, horizon - 0.002), 0.055, 0.08);
float pyramidMask = 1.0;
pyramidMask = min(pyramidMask, smoothstep(-0.003, 0.003, p1));
pyramidMask = min(pyramidMask, smoothstep(-0.003, 0.003, p2));
pyramidMask = min(pyramidMask, smoothstep(-0.003, 0.003, p3));
vec3 pyramidCol = c4 * mix(0.06, 0.15, max(daylight, goldenHour * 0.5));
float lightStr = max(daylight, goldenHour * 0.8);
float edgeHighlight1 = smoothstep(0.006, 0.0, p1) * smoothstep(-0.006, 0.0, p1);
float heightFactor1 = smoothstep(horizon, horizon + 0.14, uv.y);
pyramidCol += c1 * edgeHighlight1 * heightFactor1 * 0.25 * lightStr;
float edgeHighlight2 = smoothstep(0.005, 0.0, p2) * smoothstep(-0.005, 0.0, p2);
float heightFactor2 = smoothstep(horizon, horizon + 0.12, uv.y);
pyramidCol += c1 * edgeHighlight2 * heightFactor2 * 0.2 * lightStr;
col = mix(pyramidCol, col, pyramidMask);
// two-layer FBM dust bank blowing across the base of the pyramids
{
float speed1 = 0.08;
float yBase1 = horizon - 0.03;
float ySpread1 = 0.14;
vec2 dustUV1 = vec2(uv.x * aspect * 2.0 - rawTime * speed1,
(uv.y - yBase1) * 5.0);
float dust1 = fbm(dustUV1 + vec2(0.0, 3.7));
dust1 = smoothstep(0.30, 0.62, dust1);
float yDist1 = (uv.y - yBase1) / ySpread1;
float vertFade1 = exp(-yDist1 * yDist1 * 2.0);
float dustAlpha1 = dust1 * vertFade1 * 0.28;
float speed2 = 0.22;
float yBase2 = horizon - 0.06;
float ySpread2 = 0.10;
vec2 dustUV2 = vec2(uv.x * aspect * 3.5 - rawTime * speed2,
(uv.y - yBase2) * 8.0 + 17.0);
float dust2 = fbm(dustUV2 + vec2(41.0, 0.0));
dust2 = smoothstep(0.35, 0.65, dust2);
float yDist2 = (uv.y - yBase2) / ySpread2;
float vertFade2 = exp(-yDist2 * yDist2 * 2.5);
float dustAlpha2 = dust2 * vertFade2 * 0.22;
float dustBoost = 1.0 + isDustStorm * weatherStr * 4.0;
dustAlpha1 *= dustBoost;
dustAlpha2 *= dustBoost;
float glowStr = mix(0.7, 1.0, max(nightAmount * 0.6, max(daylight, goldenHour * 0.8)));
vec3 dustColDay = mix(c1, c3, 0.3) * 0.6;
vec3 dustColNight = mix(c1, c2, 0.45) * 0.5;
vec3 dustCol = mix(dustColNight, dustColDay, daylight);
col += dustCol * dustAlpha1 * glowStr;
col += dustCol * dustAlpha2 * glowStr;
}
// dust halo pulse around the horizon
float dustPulse = 0.7 + 0.3 * sin(rawTime * 0.4) * sin(rawTime * 0.27 + 1.3);
float dustBand = exp(-pow(abs(uv.y - horizon) / 0.12, 2.0)) * dustPulse;
float dustBandNoise = fbm(vec2(uv.x * aspect * 3.0 - rawTime * 0.1, rawTime * 0.05 + 7.0));
dustBand *= 0.6 + 0.4 * dustBandNoise;
dustBand *= 1.0 + isDustStorm * weatherStr * 2.5;
vec3 dustGlowDay = mix(c1, c2, 0.3) * 0.2;
vec3 dustGlowNight = mix(c1, c2, 0.5) * 0.15;
col += mix(dustGlowNight, dustGlowDay, daylight) * dustBand;
// horizon haze band
float hazeStrength = smoothstep(0.18, 0.0, abs(uv.y - horizon));
hazeStrength *= 1.0 + isDustStorm * weatherStr * 3.0;
vec3 hazeColDay = mix(c1, c2, 0.3) * 0.18;
vec3 hazeColNight = mix(c1, c2, 0.5) * 0.10;
col += mix(hazeColNight, hazeColDay, daylight) * hazeStrength;
// lightning: 3s strike windows with 45% chance while daylight is up
if(isLightning > 0.5 && weatherStr > 0.05){
float strikeWindow = 3.0;
float strikeIdx = floor(rawTime / strikeWindow);
float strikeSeed = hash(vec2(strikeIdx, cycleIdx * 7.3));
float strikeT = fract(rawTime / strikeWindow) * strikeWindow;
float hasStrike = step(0.55, strikeSeed) * weatherStr;
float flash = hasStrike * exp(-strikeT * 10.0);
col += flash * mix(c3, vec3(1.0), 0.6) * 0.5;
if(hasStrike > 0.5 && strikeT < 0.5 && uv.y > horizon){
float boltStartX = 0.15 + hash(vec2(strikeIdx * 3.1, cycleIdx)) * 0.7;
float boltCurX = boltStartX;
float boltD = 1e10;
vec2 prev = vec2(boltCurX, 0.95);
for(int seg = 0; seg < 12; seg++){
float fi = float(seg);
float segSeed = hash(vec2(fi + strikeIdx * 13.0, strikeSeed * 91.0 + cycleIdx));
boltCurX += (segSeed - 0.5) * 0.05;
float ny = 0.95 - (fi + 1.0) / 12.0 * (0.95 - horizon);
vec2 next = vec2(boltCurX, ny);
vec2 uvA = vec2(uv.x * aspect, uv.y);
vec2 prevA = vec2(prev.x * aspect, prev.y);
vec2 nextA = vec2(next.x * aspect, next.y);
vec2 pa = uvA - prevA;
vec2 ba = nextA - prevA;
float h = clamp(dot(pa, ba) / dot(ba, ba), 0.0, 1.0);
float segD = length(pa - ba * h);
boltD = min(boltD, segD);
prev = next;
}
float boltCore = exp(-boltD * boltD * 50000.0) * exp(-strikeT * 8.0);
float boltGlow = exp(-boltD * boltD * 4000.0) * exp(-strikeT * 5.0);
col += boltCore * vec3(1.0) * 0.9;
col += boltGlow * mix(c1, c3, 0.5) * 0.4;
}
}
// whole-screen dust-storm haze
if(isDustStorm > 0.5 && weatherStr > 0.05){
vec3 stormHaze = mix(c1, c2, 0.2) * 0.3;
float stormNoise = fbm(vec2(uv.x * aspect * 1.5 - rawTime * 0.15, uv.y * 2.0 + rawTime * 0.05));
col = mix(col, stormHaze, isDustStorm * weatherStr * 0.3 * (0.5 + 0.5 * stormNoise));
}
float scanline = 0.93 + 0.07 * sin(fragCoord.y * 3.0);
col *= scanline;
vec2 vig = uv - 0.5;
col *= 1.0 - dot(vig, vig) * 0.6;
fragColor = vec4(col * 0.5, 1.0);
}