This matchup turned on a single but revealing test: generating a complete, runnable Three.js scene. Kimi K3 didn’t produce a masterpiece, but it delivered a legible, functioning interpretation of the brief; Claude Opus 4.8 largely disappeared into the dark.
With only one scored task and a 75% confidence read, this is a **lean**, not a blowout. But the result is still straightforward: Kimi K3 won the only thing these models were asked to do, and it won for concrete reasons visible in the output.
In the Three.js challenge, Kimi K3 produced a scene that actually reads as the assignment: a rain-soaked nighttime gas station with a visible store interior, pumps, a sedan, reflections, and a coherent forward-moving camera. It was hardly elegant — the geometry was blocky, the scene sparse, and the car became too dominant late in the framing — but it was complete, stable, and recognizably on brief.
Claude Opus 4.8, by contrast, failed on the basics of visual communication. Parts of the theme were present, but the scene was so underlit and poorly composed that sampled frames were near unreadable, including an effectively black final image. That is not a minor aesthetic miss; it breaks the cinematic brief and undercuts the technical claim of a "complete, runnable" result.
The score gap, 6.5 to 2.1, reflects that difference in usefulness. Kimi K3 gave you something you could inspect, iterate on, and plausibly ship to a next draft. Claude Opus 4.8 gave you a scene that too often hid its own work.
**Final call: Kimi K3 wins, narrowly in statistical confidence but decisively on the evidence we actually have. When one model delivers a readable gas-station sequence and the other fades into near-black frames, the editorial verdict is simple: Kimi K3 was the only model that really completed the task.**
Create a complete, runnable Three.js
Create a complete, runnable Three.js scene designed specifically for a silent 10-second evaluation recording. The scene must begin instantly, animate on its own, and require zero keyboard, mouse, touch, or camera input.
Build a highly realistic rain-soaked roadside gas station at night, seen through one carefully directed cinematic camera shot.
### First 10 seconds
The opening frame must already look finished and visually striking. No loading screen, fade-in, setup view, camera teleport, or empty geometry.
Direct the camera automatically:
* **0–3 seconds:** Slow forward dolly toward the station from street level. Keep the dark-red sedan prominent in the foreground and the illuminated convenience store visible behind it.
* **3–7 seconds:** Gently arc around the sedan, revealing wet bodywork, detailed fuel pumps, the person under the canopy, and the store interior through the glass.
* **7–10 seconds:** Ease toward a close three-quarter composition as lightning briefly illuminates the clouds, station roof, rain, and puddles. End on a strong frame.
* Use smooth spline-based motion with eased acceleration. Avoid sudden turns, clipping, excessive camera motion, or a generic orbit around the scene.
* Automatically aim the camera at changing points of interest so the composition feels deliberately directed.
* Continue with a subtle looping cinematic camera path after 10 seconds.
Kimi K3: // ===== Rain-Soaked Night Gas Station — cinematic 12s loop =====
const TAU = Math.PI * 2;
const W = TAU / 12; // all periodic motion uses integer multiples of W so t=0 === t=12
// ---------- deterministic RNG ----------
function mulberry32(a) {
return function () {
a |= 0; a = (a + 0x6D2B79F5) | 0;
let t = Math.imul(a ^ (a >>> 15), 1 | a);
t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
};
}
const rnd = mulberry32(1337);
// ---------- helpers ----------
function box(w, h, d, mat, x, y, z, ry) {
const m = new THREE.Mesh(new THREE.BoxGeometry(w, h, d), mat);
m.position.set(x, y, z);
if (ry) m.rotation.y = ry;
return m;
}
function cyl(rt, rb, h, seg, mat, x, y, z) {
const m = new THREE.Mesh(new THREE.CylinderGeometry(rt, rb, h, seg), mat);
m.position.set(x, y, z);
return m;
}
// ---------- atmosphere ----------
scene.background = new THREE.Color(0x04060b);
scene.fog = new THREE.FogExp2(0x05070c, 0.026);
const baseBG = new THREE.Color(0x04060b);
const flashBG = new THREE.Color(0x2a3550);
// ---------- lights ----------
const ambient = new THREE.AmbientLight(0x223044, 0.55);
scene.add(ambient);
const moonFill = new THREE.DirectionalLight(0x33415e, 0.35);
moonFill.position.set(-8, 20, 10);
scene.add(moonFill);
const lightning = new THREE.DirectionalLight(0xd8e6ff, 0);
lightning.position.set(6, 22, -16);
scene.add(lightning);
scene.add(lightning.target);
const canopyLightA = new THREE.PointLight(0xfff0d6, 55, 16, 2);
canopyLightA.position.set(-2.8, 4.2, -2.5);
scene.add(canopyLightA);
const canopyLightB = new THREE.PointLight(0xfff0d6, 55, 16, 2);
canopyLightB.position.set(2.8, 4.2, -2.5);
scene.add(canopyLightB);
const storeLight = new THREE.PointLight(0xffd9a2, 40, 13, 2);
storeLight.position.set(0, 2.4, -8.4);
scene.add(storeLight);
const streetSpot = new THREE.SpotLight(0xffc182, 90, 22, 0.55, 0.55, 1.7);
streetSpot.position.set(-9, 6.3, 8);
streetSpot.target.position.set(-8.3, 0, 7.2);
scene.add(streetSpot, streetSpot.target);
// ---------- ground / road / apron ----------
const asphalt = new THREE.MeshStandardMaterial({ color: 0x0b0c10, roughness: 0.45, metalness: 0.25 });
const ground = new THREE.Mesh(new THREE.PlaneGeometry(140, 140), asphalt);
ground.rotation.x = -Math.PI / 2;
scene.add(ground);
const apron = new THREE.Mesh(new THREE.PlaneGeometry(26, 11),
new THREE.MeshStandardMaterial({ color: 0x17181d, roughness: 0.35, metalness: 0.3 }));
apron.rotation.x = -Math.PI / 2;
apron.position.set(0, 0.012, -2.5);
scene.add(apron);
const curb = box(60, 0.22, 0.3, new THREE.MeshStandardMaterial({ color: 0x2a2c33, roughness: 0.8 }), 0, 0.11, 3.1);
scene.add(curb);
// dashed lane line on the road
const dashMat = new THREE.MeshStandardMaterial({ color: 0x8a8f7a, roughness: 0.6, emissive: 0x23241c, emissiveIntensity: 0.4 });
for (let i = -6; i <= 6; i++) scene.add(box(1.6, 0.02, 0.14, dashMat, i * 3.4, 0.02, 9.6));
// ---------- puddles (glossy dark) + reflection smears ----------
const puddleMat = new THREE.MeshStandardMaterial({ color: 0x05070a, roughness: 0.04, metalness: 0.95 });
const puddles = [];
const puddleSpots = [[-2.5, 1.2, 2.6, 1.1], [3.4, 2.2, 3.0, 1.4], [-0.5, -0.2, 2.2, 0.9], [6.5, 5.0, 3.4, 1.6], [-6.2, 6.4, 2.8, 1.2], [0.8, 5.6, 4.2, 1.5]];
for (const [px, pz, sx, sz] of puddleSpots) {
const p = new THREE.Mesh(new THREE.CircleGeometry(1, 40), puddleMat);
p.rotation.x = -Math.PI / 2;
p.scale.set(sx, sz, 1);
p.position.set(px, 0.018, pz);
scene.add(p);
puddles.push(p);
}
function smear(w, d, color, op, x, z) {
const m = new THREE.Mesh(new THREE.PlaneGeometry(w, d),
new THREE.MeshBasicMaterial({ color, transparent: true, opacity: op, blending: THREE.AdditiveBlending, depthWrite: false }));
m.rotation.x = -Math.PI / 2;
m.position.set(x, 0.025, z);
scene.add(m);
return m;
}
smear(11, 4.5, 0xffe6bf, 0.10, 0, 0.4); // canopy glow on wet concrete
smear(12, 3.0, 0xffd2a0, 0.07, 0, -4.8); // store glow
smear(2.6, 1.4, 0xff3020, 0.16, -7.2, 4.4); // totem red
smear(3.2, 2.6, 0xffc182, 0.12, -8.4, 7.3); // streetlight pool
// ---------- canopy ----------
const canopyGroup = new THREE.Group();
scene.add(canopyGroup);
const roofMat = new THREE.MeshStandardMaterial({ color: 0x272a31, roughness: 0.6, metalness: 0.4 });
canopyGroup.add(box(11.5, 0.4, 6.8, roofMat, 0, 4.75, -2.5));
// red fascia stripe
const fasciaMat = new THREE.MeshStandardMaterial({ color: 0x8f1218, roughness: 0.4, emissive: 0x5a080c, emissiveIntensity: 0.9 });
canopyGroup.add(box(11.6, 0.55, 0.12, fasciaMat, 0, 4.75, 0.96));
canopyGroup.add(box(11.6, 0.55, 0.12, fasciaMat, 0, 4.75, -5.96));
canopyGroup.add(box(0.12, 0.55, 6.9, fasciaMat, -5.8, 4.75, -2.5));
canopyGroup.add(box(0.12, 0.55, 6.9, fasciaMat, 5.8, 4.75, -2.5));
// glowing underside
const underMat = new THREE.MeshBasicMaterial({ color: 0xfff3dc });
const under = new THREE.Mesh(new THREE.PlaneGeometry(10.6, 5.9), underMat);
under.rotation.x = Math.PI / 2;
under.position.set(0, 4.53, -2.5);
canopyGroup.add(under);
// columns
const colMat = new THREE.MeshStandardMaterial({ color: 0x9ba0a8, roughness: 0.35, metalness: 0.7 });
for (const [cx, cz] of [[-4.8, -4.9], [4.8, -4.9], [-4.8, -0.1], [4.8, -0.1]]) {
canopyGroup.add(cyl(0.16, 0.16, 4.6, 12, colMat, cx, 2.3, cz));
}
// ---------- fuel pumps ----------
function makePump(x, z) {
const g = new THREE.Group();
g.position.set(x, 0, z);
const bodyMat = new THREE.MeshStandardMaterial({ color: 0xd8dade, roughness: 0.35, metalness: 0.3 });
const redMat = new THREE.MeshStandardMaterial({ color: 0x8f1218, roughness: 0.4 });
g.add(box(1.5, 0.18, 1.0, new THREE.MeshStandardMaterial({ color: 0x3a3d44, roughness: 0.7 }), 0, 0.09, 0));
g.add(box(0.72, 1.5, 0.5, bodyMat, 0, 0.93, 0));
g.add(box(0.74, 0.22, 0.52, redMat, 0, 1.55, 0));
const screen = new THREE.Mesh(new THREE.PlaneGeometry(0.4, 0.28),
new THREE.MeshBasicMaterial({ color: 0x9fe0ff }));
screen.position.set(0, 1.2, 0.26);
g.add(screen);
const screen2 = screen.clone();
screen2.rotation.y = Math.PI;
screen2.position.z = -0.26;
g.add(screen2);
// hose
const curve = new THREE.QuadraticBezierCurve3(
new THREE.Vector3(0.38, 1.1, 0), new THREE.Vector3(0.62, 0.55, 0), new THREE.Vector3(0.42, 0.35, 0));
g.add(new THREE.Mesh(new THREE.TubeGeometry(curve, 12, 0.03, 6),
new THREE.MeshStandardMaterial({ color: 0x111214, roughness: 0.8 })));
g.add(box(0.09, 0.22, 0.09, new THREE.MeshStandardMaterial({ color: 0x22242a, roughness: 0.5, metalness: 0.6 }), 0.42, 0.42, 0));
scene.add(g);
return g;
}
makePump(-2.2, -2.5);
makePump(2.2, -2.5);
// ---------- dark-red sedan ----------
const sedan = new THREE.Group();
sedan.position.set(1.35, 0, 1.35);
sedan.rotation.y = -0.52;
scene.add(sedan);
const paint = new THREE.MeshPhysicalMaterial({
color: 0x4d0e10, metalness: 0.75, roughness: 0.22,
clearcoat: 1.0, clearcoatRoughness: 0.08
});
const glassMat = new THREE.MeshPhysicalMaterial({ color: 0x0a0e14, metalness: 0.9, roughness: 0.06 });
sedan.add(box(4.3, 0.6, 1.78, paint, 0, 0.62, 0));
sedan.add(box(4.32, 0.1, 1.8, new THREE.MeshStandardMaterial({ color: 0x1a0506, roughness: 0.4, metalness: 0.6 }), 0, 0.34, 0));
sedan.add(box(2.05, 0.52, 1.58, glassMat, -0.15, 1.14, 0));
sedan.add(box(2.0, 0.08, 1.5, paint, -0.15, 1.44, 0)); // roof
// chrome side strip
sedan.add(box(4.32, 0.045, 0.02, new THREE.MeshStandardMaterial({ color: 0xc8ccd4, metalness: 1, roughness: 0.15 }), 0, 0.62, 0.9));
sedan.add(box(4.32, 0.045, 0.02, new THREE.MeshStandardMaterial({ color: 0xc8ccd4, metalness: 1, roughness: 0.15 }), 0, 0.62, -0.9));
// wheels
const tireMat = new THREE.MeshStandardMaterial({ color: 0x0c0d0f, roughness: 0.9 });
const hubMat = new THREE.MeshStandardMaterial({ color: 0x9aa0aa, metalness: 0.9, roughness: 0.25 });
for (const [wx, wz] of [[1.45, 0.82], [1.45, -0.82], [-1.45, 0.82], [-1.45, -0.82]]) {
const tire = cyl(0.34, 0.34, 0.24, 20, tireMat, wx, 0.34, wz);
tire.rotation.x = Math.PI / 2;
sedan.add(tire);
const hub = cyl(0.16, 0.16, 0.26, 12, hubMat, wx, 0.34, wz);
hub.rotation.x = Math.PI / 2;
sedan.add(hub);
}
// taillights (rear is -x) and headlights (+x, off but glossy)
const tailMat = new THREE.MeshBasicMaterial({ color: 0xff2a18 });
sedan.add(box(0.06, 0.14, 0.42, tailMat, -2.17, 0.72, 0.55));
sedan.add(box(0.06, 0.14, 0.42, tailMat, -2.17, 0.72, -0.55));
const headMat = new THREE.MeshStandardMaterial({ color: 0xdfe8ee, metalness: 0.9, roughness: 0.1, emissive: 0x181c14, emissiveIntensity: 0.6 });
sedan.add(box(0.06, 0.16, 0.4, headMat, 2.17, 0.74, 0.55));
sedan.add(box(0.06, 0.16, 0.4, headMat, 2.17, 0.74, -0.55));
// taillight reflection on wet ground
const tailSmear = new THREE.Mesh(new THREE.PlaneGeometry(0.5, 2.2),
new THREE.MeshBasicMaterial({ color: 0xff2010, transparent: true, opacity: 0.18, blending: THREE.AdditiveBlending, depthWrite: false }));
tailSmear.rotation.x = -Math.PI / 2;
tailSmear.position.set(-2.6, 0.02 - 1.35, 0);
sedan.add(tailSmear);
tailSmear.position.set(-2.6, 0, 0);
tailSmear.rotation.z = 0;
sedan.remove(tailSmear);
scene.add(tailSmear);
tailSmear.position.set(
1.35 + Math.cos(-0.52) * -2.6, 0.03,
1.35 - Math.sin(-0.52) * -2.6);
// ---------- person under canopy ----------
const person = new THREE.Group();
person.position.set(2.75, 0, -0.85);
person.rotation.y = 2.4;
scene.add(person);
const jacketMat = new THREE.MeshStandardMaterial({ color: 0x1d2637, roughness: 0.7 });
const pantsMat = new THREE.MeshStandardMaterial({ color: 0x14161c, roughness: 0.8 });
person.add(cyl(0.07, 0.08, 0.82, 8, pantsMat, -0.09, 0.41, 0));
person.add(cyl(0.07, 0.08, 0.82, 8, pantsMat, 0.09, 0.41, 0));
const torso = new THREE.Mesh(new THREE.CapsuleGeometry(0.19, 0.5, 6, 12), jacketMat);
torso.position.set(0, 1.18, 0);
person.add(torso);
const head = new THREE.Mesh(new THREE.SphereGeometry(0.13, 14, 12),
new THREE.MeshStandardMaterial({ color: 0x8a6f5c, roughness: 0.6 }));
head.position.set(0, 1.72, 0);
person.add(head);
const armL = cyl(0.05, 0.05, 0.55, 8, jacketMat, -0.27, 1.15, 0.05);
armL.rotation.z = 0.25;
person.add(armL);
const armR = cyl(0.05, 0.05, 0.55, 8, jacketMat, 0.28, 1.12, 0.08);
armR.rotation.z = -0.7;
armR.rotation.x = -0.5;
person.add(armR);
// ---------- convenience store ----------
const store = new THREE.Group();
store.position.set(0, 0, -8.5);
scene.add(store);
const wallMat = new THREE.MeshStandardMaterial({ color: 0x2b2d34, roughness: 0.7 });
store.add(box(13, 0.5, 4.2, wallMat, 0, 3.2, 0)); // parapet
store.add(box(13, 0.25, 4.4, wallMat, 0, 0.12, 0)); // base
store.add(box(0.5, 3.0, 4.2, wallMat, -6.25, 1.6, 0)); // side pillars
store.add(box(0.5, 3.0, 4.2, wallMat, 6.25, 1.6, 0));
store.add(box(0.35, 3.0, 0.25, wallMat, -2.1, 1.6, 2.0)); // front mullion pillars
store.add(box(0.35, 3.0, 0.25, wallMat, 2.1, 1.6, 2.0));
// warm interior back wall
const backGlow = new THREE.Mesh(new THREE.PlaneGeometry(12, 3.0),
new THREE.MeshBasicMaterial({ color: 0xffe2b8 }));
backGlow.position.set(0, 1.6, -1.9);
store.add(backGlow);
// ceiling light strips inside
for (const lx of [-3.5, 0, 3.5]) {
const strip = new THREE.Mesh(new THREE.PlaneGeometry(2.6, 0.35),
new THREE.MeshBasicMaterial({ color: 0xffffff }));
strip.rotation.x = Math.PI / 2;
strip.position.set(lx, 2.95, 0.4);
store.add(strip);
}
// shelves with colorful products
const shelfMat = new THREE.MeshStandardMaterial({ color: 0x5a5347, roughness: 0.6 });
const prodColors = [0xd94f3d, 0x3da5d9, 0xe8c53a, 0x53b867, 0xd97fb0, 0xf0f0ea, 0x7a5fd0];
for (const sx of [-3.6, 0.2, 3.8]) {
for (let row = 0; row < 3; row++) {
const sy = 0.75 + row * 0.62;
store.add(box(2.7, 0.06, 0.5, shelfMat, sx, sy, -1.2));
for (let p = 0; p < 8; p++) {
const cm = new THREE.MeshStandardMaterial({
color: prodColors[Math.floor(rnd() * prodColors.length)],
roughness: 0.5, emissive: prodColors[Math.floor(rnd() * prodColors.length)], emissiveIntensity: 0.25
});
store.add(box(0.2, 0.3 + rnd() * 0.12, 0.18, cm, sx - 1.15 + p * 0.33, sy + 0.2, -1.2));
}
}
}
// counter
store.add(box(2.6, 0.95, 0.7, new THREE.MeshStandardMaterial({ color: 0x3c3f47, roughness: 0.5 }), 4.6, 0.5, 0.6));
// glass front
const glass = new THREE.Mesh(new THREE.PlaneGeometry(12.0, 2.9),
new THREE.MeshPhongMaterial({ color: 0x9fc4d8, transparent: true, opacity: 0.13, shininess: 160, specular: 0xffffff }));
glass.position.set(0, 1.62, 2.06);
store.add(glass);
// door frame
store.add(box(1.3, 2.6, 0.1, new THREE.MeshStandardMaterial({ color: 0x555a63, metalness: 0.7, roughness: 0.3 }), 0, 1.35, 2.02));
// sign band with fake glowing letters (bars)
const bandMat = new THREE.MeshStandardMaterial({ color: 0x7c0f14, roughness: 0.4, emissive: 0x3f0508, emissiveIntensity: 1.2 });
store.add(box(13.2, 1.0, 0.25, bandMat, 0, 3.95, 2.05));
const letterMat = new THREE.MeshBasicMaterial({ color: 0xffe9c9 });
// abstract letter bars spelling a glowing mark
const letterBars = [
[-2.2, 0, 0.12, 0.5], [-1.9, 0.19, 0.5, 0.1], [-1.9, -0.19, 0.5, 0.1],
[-1.2, 0, 0.12, 0.5], [-0.9, 0, 0.12, 0.5], [-1.05, 0.19, 0.35, 0.1],
[-0.3, 0, 0.12, 0.5], [0.0, 0.19, 0.45, 0.1], [0.0, 0, 0.4, 0.1], [0.0, -0.19, 0.45, 0.1],
[0.7, 0, 0.12, 0.5], [1.0, 0.19, 0.45, 0.1], [1.0, -0.19, 0.45, 0.1],
[1.7, 0, 0.12, 0.5], [2.0, 0, 0.12, 0.5], [1.85, 0.19, 0.35, 0.1], [1.85, -0.19, 0.35, 0.1]
];
for (const [lx, ly, lw, lh] of letterBars) {
const b = new THREE.Mesh(new THREE.PlaneGeometry(lw, lh), letterMat);
b.position.set(lx, 3.95 + ly, 2.19);
store.add(b);
}
// ---------- roadside price totem with 7-seg digits ----------
const SEGMAP = { "0": "abcedf".replace("e", "e") + "", "3": "abcdg", "8": "abcdefg", "9": "abcdfg", "2": "abdeg", "4": "bcfg" };
const SEGPOS = { a: [0, 0.5, 0.5, 0.1], b: [0.26, 0.26, 0.1, 0.44], c: [0.26, -0.26, 0.1, 0.44], d: [0, -0.5, 0.5, 0.1], e: [-0.26, -0.26, 0.1, 0.44], f: [-0.26, 0.26, 0.1, 0.44], g: [0, 0, 0.5, 0.1] };
const digitMat = new THREE.MeshBasicMaterial({ color: 0xffd75e });
function makeDigit(ch, scale) {
const g = new THREE.Group();
const segs = SEGMAP[ch] || "";
for (const s of segs) {
const [sx, sy, sw, sh] = SEGPOS[s];
const m = new THREE.Mesh(new THREE.PlaneGeometry(sw * scale, sh * scale), digitMat);
m.position.set(sx * scale, sy * scale, 0);
g.add(m);
}
return g;
}
const totem = new THREE.Group();
totem.position.set(-7.4, 0, 3.6);
totem.rotation.y = 0.35;
scene.add(totem);
totem.add(cyl(0.12, 0.12, 3.6, 10, new THREE.MeshStandardMaterial({ color: 0x3a3d44, roughness: 0.5, metalness: 0.6 }), 0, 1.8, 0));
totem.add(box(2.0, 2.4, 0.28, new THREE.MeshStandardMaterial({ color: 0x14161c, roughness: 0.4 }), 0, 4.6, 0));
totem.add(box(2.02, 0.5, 0.3, new THREE.MeshStandardMaterial({ color: 0x8f1218, emissive: 0x6a0a0e, emissiveIntensity: 1.4 }), 0, 5.6, 0));
const d1 = makeDigit("3", 0.8); d1.position.set(-0.62, 4.75, 0.16); totem.add(d1);
const d2 = makeDigit("8", 0.8); d2.position.set(0.0, 4.75, 0.16); totem.add(d2);
const dot = new THREE.Mesh(new THREE.CircleGeometry(0.05, 8), digitMat); dot.position.set(0.3, 4.32, 0.16); totem.add(dot);
const d3 = makeDigit("9", 0.8); d3.position.set(0.62, 4.75, 0.16); totem.add(d3);
const totemGlow = new THREE.PointLight(0xffd75e, 8, 7, 2);
totemGlow.position.set(-7.4, 4.6, 4.4);
scene.add(totemGlow);
// ---------- streetlight ----------
const poleMat = new THREE.MeshStandardMaterial({ color: 0x2e3138, roughness: 0.5, metalness: 0.6 });
scene.add(cyl(0.09, 0.11, 6.2, 10, poleMat, -9, 3.1, 8));
const arm = cyl(0.06, 0.06, 1.6, 8, poleMat, -8.5, 6.2, 7.8);
arm.rotation.z = Math.PI / 2 - 0.25;
scene.add(arm);
scene.add(box(0.5, 0.14, 0.3, new THREE.MeshBasicMaterial({ color: 0xffd9a0 }), -8.05, 6.32, 7.72));
// visible light cone
const coneMat = new THREE.MeshBasicMaterial({ color: 0xffc182, transparent: true, opacity: 0.05, blending: THREE.AdditiveBlending, depthWrite: false, side: THREE.DoubleSide });
const cone = new THREE.Mesh(new THREE.ConeGeometry(2.4, 6.2, 24, 1, true), coneMat);
cone.position.set(-8.2, 3.1, 7.5);
scene.add(cone);
// ---------- background silhouettes + clouds ----------
const bldgMat = new THREE.MeshStandardMaterial({ color: 0x080a10, roughness: 0.9 });
const winMat = new THREE.MeshBasicMaterial({ color: 0xffd9a0 });
const winMat2 = new THREE.MeshBasicMaterial({ color: 0x9fc4e8 });
for (let i = 0; i < 9; i++) {
const bx = -28 + i * 7 + rnd() * 2;
const bh = 5 + rnd() * 9;
const bz = -24 - rnd() * 8;
scene.add(box(5 + rnd() * 2, bh, 4, bldgMat, bx, bh / 2, bz));
for (let wn = 0; wn < 5; wn++) {
if (rnd() < 0.45) {
const wmesh = new THREE.Mesh(new THREE.PlaneGeometry(0.4, 0.5), rnd() < 0.7 ? winMat : winMat2);
wmesh.position.set(bx - 1.5 + rnd() * 3, 1.5 + rnd() * (bh - 2), bz + 2.05);
scene.add(wmesh);
}
}
}
const cloudMats = [];
for (let i = 0; i < 6; i++) {
const cm = new THREE.MeshBasicMaterial({ color: 0x0a0e18 });
cloudMats.push(cm);
const c = new THREE.Mesh(new THREE.SphereGeometry(1, 16, 12), cm);
c.scale.set(6 + rnd() * 6, 1.6 + rnd() * 1.4, 3 + rnd() * 2);
c.position.set(-20 + i * 8 + rnd() * 3, 17 + rnd() * 7, -20 - rnd() * 8);
scene.add(c);
}
// ---------- rain (line streaks) ----------
const RAIN = 950;
const rainPos = new Float32Array(RAIN * 6);
const drops = [];
for (let i = 0; i < RAIN; i++) {
const n = 9 + Math.floor(rnd() * 5); // integer cycles per loop -> seamless
drops.push({
x0: -16 + rnd() * 32,
y0: rnd() * 14,
z0: -16 + rnd() * 30,
speed: 14 * n / 12,
len: 0.28 + rnd() * 0.22
});
}
const rainGeo = new THREE.BufferGeometry();
rainGeo.setAttribute("position", new THREE.BufferAttribute(rainPos, 3));
const rainMat = new THREE.LineBasicMaterial({ color: 0x9fb4cc, transparent: true, opacity: 0.32 });
scene.add(new THREE.LineSegments(rainGeo, rainMat));
// ---------- puddle ripples ----------
const ripples = [];
const rippleMatBase = new THREE.MeshBasicMaterial({ color: 0xaebdc9, transparent: true, opacity: 0.3, side: THREE.DoubleSide, depthWrite: false });
for (let i = 0; i < 14; i++) {
const rm = rippleMatBase.clone();
const r = new THREE.Mesh(new THREE.RingGeometry(0.85, 1.0, 24), rm);
r.rotation.x = -Math.PI / 2;
const spot = puddleSpots[i % puddleSpots.length];
r.position.set(spot[0] + (rnd() - 0.5) * spot[2] * 0.8, 0.028, spot[1] + (rnd() - 0.5) * spot[3] * 0.8);
scene.add(r);
ripples.push({ mesh: r, mat: rm, phase: rnd() });
}
// ---------- lightning schedule (deterministic) ----------
function flashAt(t) {
const pulses = [[7.70, 0.05, 0.85], [7.86, 0.05, 0.5], [8.04, 0.08, 1.0], [9.10, 0.12, 0.4], [1.40, 0.18, 0.1]];
let f = 0;
for (let i = 0; i < pulses.length; i++) {
const dt = t - pulses[i][0];
if (dt < 0) continue;
const v = dt < pulses[i][1] ? dt / pulses[i][1] : Math.exp(-(dt - pulses[i][1]) * 4.2);
f += pulses[i][2] * v;
}
return Math.min(f, 1.25);
}
// ---------- cinematic camera (keyframed catmull-rom, exact loop) ----------
const camKeys = [
{ t: 0.0, p: [-2.7, 1.55, 13.6], l: [0.9, 1.25, -1.2] },
{ t: 3.0, p: [-1.3, 1.45, 8.7], l: [1.2, 1.05, -0.6] },
{ t: 5.0, p: [2.6, 1.6, 6.4], l: [1.7, 0.95, -1.3] },
{ t: 7.0, p: [4.7, 1.75, 2.3], l: [1.3, 1.05, -2.3] },
{ t: 9.0, p: [3.7, 1.5, 0.35], l: [1.4, 0.85, -0.9] },
{ t: 10.5, p: [0.9, 1.7, 5.6], l: [0.9, 1.1, -1.6] },
{ t: 12.0, p: [-2.7, 1.55, 13.6], l: [0.9, 1.25, -1.2] }
];
function catmull(p0, p1, p2, p3, u) {
const u2 = u * u, u3 = u2 * u;
return 0.5 * ((2 * p1) + (-p0 + p2) * u + (2 * p0 - 5 * p1 + 4 * p2 - p3) * u2 + (-p0 + 3 * p1 - 3 * p2 + p3) * u3);
}
function sampleKeys(t, field) {
let i = 0;
while (i < camKeys.length - 2 && t > camKeys[i + 1].t) i++;
const k0 = camKeys[Math.max(0, i - 1)], k1 = camKeys[i], k2 = camKeys[i + 1], k3 = camKeys[Math.min(camKeys.length - 1, i + 2)];
let u = (t - k1.t) / (k2.t - k1.t);
u = u * u * (3 - 2 * u);
return [
catmull(k0[field][0], k1[field][0], k2[field][0], k3[field][0], u),
catmull(k0[field][1], k1[field][1], k2[field][1], k3[field][1], u),
catmull(k0[field][2], k1[field][2], k2[field][2], k3[field][2], u)
];
}
camera.fov = 47;
camera.updateProjectionMatrix();
const lookTarget = new THREE.Vector3();
const tmpColor = new THREE.Color();
// ---------- per-frame update ----------
__update = (t) => {
// camera
const cp = sampleKeys(t, "p");
const cl = sampleKeys(t, "l");
// subtle handheld drift (loop-safe integer frequencies)
const hx = 0.035 * Math.sin(3 * W * t + 1.1) + 0.02 * Math.sin(7 * W * t);
const hy = 0.025 * Math.sin(5 * W * t + 2.3);
camera.position.set(cp[0] + hx, cp[1] + hy, cp[2]);
lookTarget.set(cl[0] + hx * 0.5, cl[1] + hy * 0.5, cl[2]);
camera.lookAt(lookTarget);
// lightning
const f = flashAt(t);
lightning.intensity = f * 4.5;
ambient.intensity = 0.55 + f * 0.9;
tmpColor.copy(baseBG).lerp(flashBG, Math.min(1, f * 0.9));
scene.background.copy(tmpColor);
scene.fog.color.copy(tmpColor);
for (let i = 0; i < cloudMats.length; i++) {
cloudMats[i].color.setRGB(0.04 + f * 0.35, 0.055 + f * 0.4, 0.09 + f * 0.5);
}
rainMat.opacity = 0.32 + f * 0.45;
underMat.color.setRGB(1 + f * 0.4, 0.95 + f * 0.35, 0.86 + f * 0.3);
// rain fall
for (let i = 0; i < RAIN; i++) {
const d = drops[i];
let y = (d.y0 - d.speed * t) % 14;
if (y < 0) y += 14;
const drift = (14 - y) * 0.055; // wind slant
const x = d.x0 - drift;
const o = i * 6;
rainPos[o] = x; rainPos[o + 1] = y; rainPos[o + 2] = d.z0;
rainPos[o + 3] = x + 0.02 + d.len * 0.055; rainPos[o + 4] = y + d.len; rainPos[o + 5] = d.z0;
}
rainGeo.attributes.position.needsUpdate = true;
// ripples: 0.5 cycles/sec = 6 per loop -> seamless
for (const rp of ripples) {
const s = (t * 0.5 + rp.phase) % 1;
const sc = 0.12 + s * 0.75;
rp.mesh.scale.set(sc, sc, 1);
rp.mat.opacity = (1 - s) * 0.32 + f * 0.15;
}
// person idle sway
person.rotation.y = 2.4 + 0.1 * Math.sin(2 * W * t + 0.6);
head.position.y = 1.72 + 0.015 * Math.sin(4 * W * t);
torso.rotation.x = 0.03 * Math.sin(2 * W * t);
// sedan taillight pulse (very subtle, wet glow)
tailMat.color.setRGB(1 + f * 0.5, 0.16 + f * 0.2, 0.1);
tailSmear.material.opacity = 0.16 + 0.05 * Math.sin(6 * W * t + 1.0) + f * 0.2;
// canopy lights flicker almost imperceptibly
const fl = 1 + 0.03 * Math.sin(14 * W * t) * Math.sin(3 * W * t);
canopyLightA.intensity = 55 * fl;
canopyLightB.intensity = 55 * (2 - fl) * 0.5 + 27.5 * fl * 0.5;
// streetlight cone shimmer
coneMat.opacity = 0.045 + 0.012 * Math.sin(8 * W * t + 0.4) + f * 0.05;
};
Anthropic: Claude Opus 4.8: // ============================================================
// Rain-soaked roadside gas station at night — directed cinematic shot
// ============================================================
// ---- renderer / camera tuning -----------------------------------
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
renderer.toneMapping = THREE.ACESFilmicToneMapping;
renderer.toneMappingExposure = 1.12;
camera.fov = 42;
camera.near = 0.05;
camera.far = 400;
camera.updateProjectionMatrix();
// ---- deterministic rng ------------------------------------------
let _seed = 20240517;
const rnd = () => { _seed = (_seed * 1664525 + 1013904223) >>> 0; return _seed / 4294967296; };
const rr = (a, b) => a + (b - a) * rnd();
// ---- atmosphere -------------------------------------------------
scene.background = new THREE.Color(0x070a12);
scene.fog = new THREE.FogExp2(0x0a1220, 0.030);
// ---- procedural environment map (wet reflections) ---------------
const pmrem = new THREE.PMREMGenerator(renderer);
pmrem.compileEquirectangularShader();
const envSceneG = new THREE.Scene();
const envMat = new THREE.ShaderMaterial({
side: THREE.BackSide,
vertexShader: `
varying vec3 vDir;
void main(){ vDir = normalize(position); gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0); }
`,
fragmentShader: `
varying vec3 vDir;
void main(){
float h = clamp(vDir.y*0.5+0.5, 0.0, 1.0);
vec3 top = vec3(0.010,0.016,0.035);
vec3 mid = vec3(0.030,0.045,0.080);
vec3 hor = vec3(0.120,0.090,0.070); // warm sodium-lit haze near horizon
vec3 grnd= vec3(0.020,0.022,0.030);
vec3 col = mix(hor, mid, smoothstep(0.02,0.28,h));
col = mix(col, top, smoothstep(0.30,0.85,h));
col = mix(grnd, col, smoothstep(-0.05,0.03,vDir.y));
gl_FragColor = vec4(col,1.0);
}
`
});
const envSphere = new THREE.Mesh(new THREE.SphereGeometry(60, 32, 24), envMat);
envSceneG.add(envSphere);
const envTex = pmrem.fromScene(envSceneG, 0.05).texture;
scene.environment = envTex;
pmrem.dispose();
// ---- sky dome ---------------------------------------------------
const skyMat = new THREE.ShaderMaterial({
side: THREE.BackSide, depthWrite: false, fog: false,
vertexShader: `varying vec3 vD; void main(){ vD=normalize(position); gl_Position=projectionMatrix*modelViewMatrix*vec4(position,1.0);} `,
fragmentShader: `
varying vec3 vD;
void main(){
float h = clamp(vD.y*0.5+0.5,0.0,1.0);
vec3 top=vec3(0.010,0.015,0.032);
vec3 hor=vec3(0.075,0.060,0.065);
vec3 col=mix(hor,top,smoothstep(0.10,0.75,h));
gl_FragColor=vec4(col,1.0);
}`
});
scene.add(new THREE.Mesh(new THREE.SphereGeometry(180, 32, 24), skyMat));
// ---- storm cloud dome (lightning reacts here) -------------------
const cloudMat = new THREE.ShaderMaterial({
side: THREE.BackSide, transparent: true, depthWrite: false, fog: false,
uniforms: { uLoop: { value: 0 }, uFlash: { value: 0 } },
vertexShader: `varying vec3 vD; void main(){ vD=normalize(position); gl_Position=projectionMatrix*modelViewMatrix*vec4(position,1.0);} `,
fragmentShader: `
varying vec3 vD;
uniform float uLoop; uniform float uFlash;
float hash(vec2 p){ return fract(sin(dot(p,vec2(127.1,311.7)))*43758.5453); }
float noise(vec2 p){ vec2 i=floor(p),f=fract(p); f=f*f*(3.0-2.0*f);
float a=hash(i),b=hash(i+vec2(1,0)),c=hash(i+vec2(0,1)),d=hash(i+vec2(1,1));
return mix(mix(a,b,f.x),mix(c,d,f.x),f.y); }
float fbm(vec2 p){ float v=0.0,a=0.5; for(int i=0;i<5;i++){ v+=a*noise(p); p*=2.02; a*=0.5; } return v; }
void main(){
float up = smoothstep(0.02,0.55,vD.y);
vec2 uv = vD.xz/(vD.y+0.35);
float ang = uLoop*6.28318;
vec2 drift = vec2(sin(ang),cos(ang))*0.12;
float d = fbm(uv*1.6 + drift);
d = smoothstep(0.45,1.05,d);
float dens = d*up;
vec3 base = vec3(0.045,0.055,0.080);
vec3 lit = vec3(0.85,0.88,1.00);
vec3 col = mix(base, lit, clamp(uFlash*1.3,0.0,1.0));
float a = dens*(0.55 + 0.45*clamp(uFlash,0.0,1.0));
gl_FragColor = vec4(col, a);
}`
});
scene.add(new THREE.Mesh(new THREE.SphereGeometry(150, 48, 32), cloudMat));
// ---- lighting ---------------------------------------------------
scene.add(new THREE.HemisphereLight(0x33405c, 0x05070c, 0.35));
const moon = new THREE.DirectionalLight(0x9fb4d8, 0.28);
moon.position.set(-18, 26, -6);
moon.castShadow = true;
moon.shadow.mapSize.set(1024, 1024);
moon.shadow.camera.near = 1; moon.shadow.camera.far = 80;
moon.shadow.camera.left = -20; moon.shadow.camera.right = 20;
moon.shadow.camera.top = 20; moon.shadow.camera.bottom = -20;
moon.shadow.bias = -0.0004;
scene.add(moon);
// lightning flash light (driven each frame)
const flashLight = new THREE.DirectionalLight(0xcfe0ff, 0.0);
flashLight.position.set(-24, 34, -20);
scene.add(flashLight);
const flashFill = new THREE.PointLight(0xdfeaff, 0.0, 120, 2.0);
flashFill.position.set(0, 22, -6);
scene.add(flashFill);
// canopy under-lights (warm pooling)
const canopyLights = [];
for (let i = -1; i <= 1; i++) {
const pl = new THREE.PointLight(0xffe6bf, 6.0, 16, 2.0);
pl.position.set(i * 3.0, 4.0, -2.5);
scene.add(pl);
canopyLights.push(pl);
}
// store interior glow
const storeGlow = new THREE.PointLight(0xd8f0ff, 10.0, 22, 2.0);
storeGlow.position.set(0, 2.3, -8.6);
scene.add(storeGlow);
const storeGlow2 = new THREE.PointLight(0xbfe0ff, 6.0, 18, 2.0);
storeGlow2.position.set(-3.4, 2.0, -8.6);
scene.add(storeGlow2);
// ---- helpers ----------------------------------------------------
const G = (x, y, z) => { const g = new THREE.Group(); g.position.set(x, y, z); return g; };
const box = (w, h, d, mat) => new THREE.Mesh(new THREE.BoxGeometry(w, h, d), mat);
const cyl = (rt, rb, h, s, mat) => new THREE.Mesh(new THREE.CylinderGeometry(rt, rb, h, s), mat);
const matAsphalt = new THREE.MeshStandardMaterial({ color: 0x0c0f14, roughness: 0.42, metalness: 0.55, envMapIntensity: 1.1 });
const matPuddle = new THREE.MeshStandardMaterial({ color: 0x0a0d13, roughness: 0.04, metalness: 1.0, envMapIntensity: 1.5 });
const matConc = new THREE.MeshStandardMaterial({ color: 0x1a1d22, roughness: 0.55, metalness: 0.3, envMapIntensity: 0.8 });
const matSteel = new THREE.MeshStandardMaterial({ color: 0x30343c, roughness: 0.3, metalness: 0.85, envMapIntensity: 1.1 });
const matWhitePanel = new THREE.MeshStandardMaterial({ color: 0xdfe3e8, roughness: 0.5, metalness: 0.2 });
// ---- ground + puddles ------------------------------------------
const ground = new THREE.Mesh(new THREE.PlaneGeometry(240, 240), matAsphalt);
ground.rotation.x = -Math.PI / 2;
ground.receiveShadow = true;
scene.add(ground);
// road markings
const lineMat = new THREE.MeshStandardMaterial({ color: 0x8a8f7a, roughness: 0.6, metalness: 0.1, emissive: 0x1a1a12, emissiveIntensity: 0.3 });
for (let i = 0; i < 6; i++) {
const m = box(0.22, 0.02, 1.6, lineMat);
m.position.set(-6.2, 0.011, 6 - i * 3.2);
scene.add(m);
}
// reflective puddles scattered in view
const puddleGroup = new THREE.Group();
for (let i = 0; i < 26; i++) {
const s = rr(0.6, 2.6);
const p = new THREE.Mesh(new THREE.CircleGeometry(s, 20), matPuddle);
p.rotation.x = -Math.PI / 2;
p.scale.y = rr(0.5, 1.0);
p.position.set(rr(-9, 9), 0.012 + rnd() * 0.004, rr(-6, 8));
puddleGroup.add(p);
}
scene.add(puddleGroup);
// ---- convenience store -----------------------------------------
const store = G(0, 0, -9);
scene.add(store);
const wallMat = new THREE.MeshStandardMaterial({ color: 0x252a30, roughness: 0.7, metalness: 0.2 });
// shell (back + sides + roof)
const backW = box(13, 4.4, 0.3, wallMat); backW.position.set(0, 2.2, -1.9); store.add(backW);
const leftW = box(0.3, 4.4, 4.0, wallMat); leftW.position.set(-6.35, 2.2, 0.1); store.add(leftW);
const rightW = box(0.3, 4.4, 4.0, wallMat); rightW.position.set(6.35, 2.2, 0.1); store.add(rightW);
const roof = box(13.4, 0.35, 4.4, new THREE.MeshStandardMaterial({ color: 0x14171c, roughness: 0.6, metalness: 0.3 }));
roof.position.set(0, 4.45, 0.1); roof.castShadow = true; store.add(roof);
const floorS = box(13, 0.1, 4, new THREE.MeshStandardMaterial({ color: 0x30343a, roughness: 0.35, metalness: 0.4 }));
floorS.position.set(0, 0.05, 0.1); store.add(floorS);
// front pillars/frames
const frameMat = matSteel;
const fL = box(0.35, 4.4, 0.35, frameMat); fL.position.set(-6.0, 2.2, 2.0); store.add(fL);
const fR = box(0.35, 4.4, 0.35, frameMat); fR.position.set(6.0, 2.2, 2.0); store.add(fR);
const fT = box(12.6, 0.4, 0.35, frameMat); fT.position.set(0, 4.15, 2.0); store.add(fT);
const fB = box(12.6, 0.25, 0.35, frameMat); fB.position.set(0, 0.2, 2.0); store.add(fB);
const fMid = box(0.2, 4.0, 0.3, frameMat); fMid.position.set(1.2, 2.1, 2.0); store.add(fMid);
// glass front
const glassMat = new THREE.MeshStandardMaterial({ color: 0x9fdfff, roughness: 0.06, metalness: 0.0, transparent: true, opacity: 0.22, emissive: 0x122430, emissiveIntensity: 0.5, envMapIntensity: 1.4 });
const glass = box(12.4, 3.8, 0.06, glassMat); glass.position.set(0, 2.15, 2.02); store.add(glass);
// interior emissive shelves / fridges
const shelfCols = [0xffe9b0, 0xbfe8ff, 0xffd0d0, 0xd8ffcf];
for (let i = 0; i < 5; i++) {
const em = new THREE.MeshStandardMaterial({ color: 0x20242a, roughness: 0.5, metalness: 0.2, emissive: shelfCols[i % shelfCols.length], emissiveIntensity: 0.9 });
const sh = box(1.7, 2.6, 0.5, em);
sh.position.set(-5 + i * 2.5, 1.5, -1.4); store.add(sh);
}
const counter = box(3.2, 1.1, 0.9, new THREE.MeshStandardMaterial({ color: 0x2b2f36, roughness: 0.5, metalness: 0.3, emissive: 0x223, emissiveIntensity: 0.4 }));
counter.position.set(3.4, 0.6, 0.6); store.add(counter);
// ceiling strip lights inside
const ceilEmis = new THREE.MeshStandardMaterial({ color: 0xffffff, emissive: 0xdff0ff, emissiveIntensity: 2.2 });
for (let i = -1; i <= 1; i++) { const c = box(5, 0.08, 0.25, ceilEmis); c.position.set(i * 3.5, 3.9, 0.2); store.add(c); }
// store brand sign (glowing)
const signMat = new THREE.MeshStandardMaterial({ color: 0x101418, emissive: 0xff5533, emissiveIntensity: 2.6 });
const signBar = box(6.5, 0.9, 0.25, signMat); signBar.position.set(0, 5.0, 2.0); store.add(signBar);
const signMat2 = new THREE.MeshStandardMaterial({ color: 0x101418, emissive: 0x33bbff, emissiveIntensity: 2.2 });
const signBar2 = box(6.7, 0.18, 0.26, signMat2); signBar2.position.set(0, 4.45, 2.05); store.add(signBar2);
// ---- fuel canopy ------------------------------------------------
const canopy = G(0, 0, -2.5);
scene.add(canopy);
const canTop = box(9.4, 0.5, 5.4, new THREE.MeshStandardMaterial({ color: 0x1b1f26, roughness: 0.5, metalness: 0.4 }));
canTop.position.set(0, 4.7, 0); canTop.castShadow = true; canopy.add(canTop);
const canFascia = box(9.6, 0.7, 5.6, new THREE.MeshStandardMaterial({ color: 0xe8ebef, roughness: 0.4, metalness: 0.2, emissive: 0x223040, emissiveIntensity: 0.5 }));
canFascia.position.set(0, 4.3, 0); canopy.add(canFascia);
const canStripe = box(9.7, 0.16, 5.7, new THREE.MeshStandardMaterial({ color: 0x102030, emissive: 0x2aa4ff, emissiveIntensity: 2.4 }));
canStripe.position.set(0, 4.02, 0); canopy.add(canStripe);
// underside light panels
const panelMat = new THREE.MeshStandardMaterial({ color: 0xffffff, emissive: 0xffe9c8, emissiveIntensity: 2.6 });
for (let x = -1; x <= 1; x++) for (let z = -1; z <= 1; z++) {
const pnl = box(2.3, 0.06, 1.3, panelMat); pnl.position.set(x * 2.9, 4.42, z * 1.7); canopy.add(pnl);
}
// pillars
[[-4.2, -2.2], [4.2, -2.2], [-4.2, 2.2], [4.2, 2.2]].forEach(([x, z]) => {
const p = cyl(0.28, 0.28, 4.4, 16, matSteel); p.position.set(x, 2.2, z); p.castShadow = true; canopy.add(p);
});
// ---- fuel pumps -------------------------------------------------
function makePump(px, pz) {
const g = G(px, 0, pz);
const base = box(1.3, 0.25, 0.9, matConc); base.position.y = 0.125; g.add(base);
const body = box(0.9, 1.9, 0.6, new THREE.MeshStandardMaterial({ color: 0xc9cdd2, roughness: 0.45, metalness: 0.4 }));
body.position.y = 1.2; body.castShadow = true; g.add(body);
const screen = box(0.62, 0.5, 0.05, new THREE.MeshStandardMaterial({ color: 0x081018, emissive: 0x36ff9a, emissiveIntensity: 1.8 }));
screen.position.set(0, 1.55, 0.31); g.add(screen);
const top = box(0.95, 0.35, 0.65, new THREE.MeshStandardMaterial({ color: 0xe23c3c, roughness: 0.4, metalness: 0.3, emissive: 0x3a0808, emissiveIntensity: 0.6 }));
top.position.y = 2.28; g.add(top);
const nozzle = box(0.14, 0.5, 0.14, matSteel); nozzle.position.set(0.55, 1.3, 0.2); g.add(nozzle);
const hose = cyl(0.05, 0.05, 0.8, 8, new THREE.MeshStandardMaterial({ color: 0x0a0a0a, roughness: 0.8 }));
hose.position.set(0.5, 0.9, 0.25); hose.rotation.z = 0.3; g.add(hose);
return g;
}
scene.add(makePump(-2.7, -2.8));
scene.add(makePump(2.7, -2.8));
// ---- person under canopy ---------------------------------------
const person = G(-1.75, 0, -2.2);
person.rotation.y = 0.6;
const skin = new THREE.MeshStandardMaterial({ color: 0x8a5a44, roughness: 0.7 });
const jacket = new THREE.MeshStandardMaterial({ color: 0x223344, roughness: 0.6, metalness: 0.2 });
const pants = new THREE.MeshStandardMaterial({ color: 0x14161b, roughness: 0.8 });
const torso = box(0.42, 0.7, 0.26, jacket); torso.position.y = 1.28; torso.castShadow = true; person.add(torso);
const hips = box(0.4, 0.28, 0.26, pants); hips.position.y = 0.92; person.add(hips);
const legL = box(0.16, 0.8, 0.2, pants); legL.position.set(-0.11, 0.42, 0); person.add(legL);
const legR = box(0.16, 0.8, 0.2, pants); legR.position.set(0.11, 0.42, 0); person.add(legR);
const armL = box(0.14, 0.62, 0.16, jacket); armL.position.set(-0.31, 1.28, 0.06); armL.rotation.z = 0.18; person.add(armL);
const armR = box(0.14, 0.62, 0.16, jacket); armR.position.set(0.31, 1.24, 0.14); armR.rotation.z = -0.35; person.add(armR);
const head = new THREE.Mesh(new THREE.SphereGeometry(0.17, 16, 16), skin); head.position.y = 1.78; person.add(head);
const hood = box(0.28, 0.2, 0.3, jacket); hood.position.set(0, 1.9, -0.02); person.add(hood);
scene.add(person);
// ---- dark-red sedan (foreground) --------------------------------
const car = G(1.0, 0, 1.4);
car.rotation.y = -0.62;
scene.add(car);
const carRed = new THREE.MeshStandardMaterial({ color: 0x3d0c12, roughness: 0.14, metalness: 0.9, envMapIntensity: 1.6 });
const carGlass = new THREE.MeshStandardMaterial({ color: 0x0a1016, roughness: 0.05, metalness: 0.6, envMapIntensity: 1.6, transparent: true, opacity: 0.55 });
const carTrim = new THREE.MeshStandardMaterial({ color: 0x0c0c0e, roughness: 0.35, metalness: 0.7 });
const lowerBody = box(2.15, 0.55, 4.5, carRed); lowerBody.position.y = 0.62; lowerBody.castShadow = true; car.add(lowerBody);
const midBody = box(2.1, 0.42, 3.5, carRed); midBody.position.y = 1.02; midBody.castShadow = true; car.add(midBody);
// cabin (tapered via scaled box)
const cabin = box(1.9, 0.62, 2.05, carRed); cabin.position.set(0, 1.42, -0.15); cabin.castShadow = true; car.add(cabin);
const cabinTop = box(1.72, 0.12, 1.85, carRed); cabinTop.position.set(0, 1.75, -0.15); car.add(cabinTop);
// windows
const wsF = box(1.7, 0.55, 0.06, carGlass); wsF.position.set(0, 1.45, 0.9); wsF.rotation.x = 0.5; car.add(wsF);
const wsB = box(1.7, 0.5, 0.06, carGlass); wsB.position.set(0, 1.45, -1.2); wsB.rotation.x = -0.5; car.add(wsB);
const winL = box(0.06, 0.5, 1.7, carGlass); winL.position.set(-0.94, 1.45, -0.15); car.add(winL);
const winR = box(0.06, 0.5, 1.7, carGlass); winR.position.set(0.94, 1.45, -0.15); car.add(winR);
// bumpers / trim
const bumpF = box(2.1, 0.3, 0.25, carTrim); bumpF.position.set(0, 0.5, 2.28); car.add(bumpF);
const bumpB = box(2.1, 0.3, 0.25, carTrim); bumpB.position.set(0, 0.5, -2.28); car.add(bumpB);
const grille = box(1.3, 0.22, 0.1, carTrim); grille.position.set(0, 0.78, 2.28); car.add(grille);
// lights
const headMat = new THREE.MeshStandardMaterial({ color: 0xffffff, emissive: 0xfff3d0, emissiveIntensity: 3.0 });
const tailMat = new THREE.MeshStandardMaterial({ color: 0xff2020, emissive: 0xff1010, emissiveIntensity: 2.6 });
[-0.72, 0.72].forEach(x => { const h = box(0.5, 0.2, 0.1, headMat); h.position.set(x, 0.82, 2.3); car.add(h); });
[-0.75, 0.75].forEach(x => { const tl = box(0.45, 0.22, 0.1, tailMat); tl.position.set(x, 0.85, -2.3); car.add(tl); });
// wheels
const wheelMat = new THREE.MeshStandardMaterial({ color: 0x0a0a0c, roughness: 0.75, metalness: 0.2 });
const hubMat = new THREE.MeshStandardMaterial({ color: 0x9a9ea6, roughness: 0.35, metalness: 0.85 });
[[-1.02, 1.5], [1.02, 1.5], [-1.02, -1.5], [1.02, -1.5]].forEach(([x, z]) => {
const w = cyl(0.44, 0.44, 0.32, 20, wheelMat); w.rotation.z = Math.PI / 2; w.position.set(x, 0.44, z); w.castShadow = true; car.add(w);
const hub = cyl(0.2, 0.2, 0.34, 12, hubMat); hub.rotation.z = Math.PI / 2; hub.position.set(x, 0.44, z); car.add(hub);
});
// car light pools on wet ground
const headPool = new THREE.PointLight(0xfff0d0, 2.2, 8, 2.0); headPool.position.set(1.0, 0.5, 3.6); scene.add(headPool);
const tailPool = new THREE.PointLight(0xff2020, 1.4, 5, 2.0); tailPool.position.set(0.4, 0.5, -0.4); scene.add(tailPool);
// ---- rain (GPU streaks, pure function of time) ------------------
const RAIN = 5200, RH = 18.0, RSPEED = 18.0, STREAK = 0.34;
const rp = new Float32Array(RAIN * 2 * 3);
const rl = new Float32Array(RAIN * 2);
for (let i = 0; i < RAIN; i++) {
const x = rr(-16, 16), z = rr(-16, 12), y = rr(0, RH);
const o = i * 6;
rp[o] = x; rp[o + 1] = y; rp[o + 2] = z;
rp[o + 3] = x; rp[o + 4] = y; rp[o + 5] = z;
rl[i * 2] = 0.0; rl[i * 2 + 1] = STREAK;
}
const rainGeo = new THREE.BufferGeometry();
rainGeo.setAttribute('position', new THREE.BufferAttribute(rp, 3));
rainGeo.setAttribute('aLen', new THREE.BufferAttribute(rl, 1));
const rainMat = new THREE.ShaderMaterial({
transparent: true, depthWrite: false, blending: THREE.AdditiveBlending, fog: false,
uniforms: { uTime: { value: 0 }, uH: { value: RH }, uSpeed: { value: RSPEED } },
vertexShader: `
attribute float aLen;
uniform float uTime, uH, uSpeed;
varying float vA;
void main(){
float y = mod(position.y - uSpeed*uTime, uH);
float wy = y - aLen;
vec3 pos = vec3(position.x + wy*0.14, wy, position.z);
vA = smoothstep(0.0,2.5,wy) * (1.0 - smoothstep(9.0,15.0,wy));
gl_Position = projectionMatrix * modelViewMatrix * vec4(pos,1.0);
}`,
fragmentShader: `
varying float vA;
void main(){ gl_FragColor = vec4(vec3(0.62,0.72,0.9)*0.9, vA*0.42); }`
});
const rain = new THREE.LineSegments(rainGeo, rainMat);
rain.frustumCulled = false;
scene.add(rain);
// ---- cinematic camera path (closed spline) ----------------------
const camPts = [
new THREE.Vector3( 0.0, 1.15, 7.2),
new THREE.Vector3( 0.7, 1.30, 5.2),
new THREE.Vector3( 1.7, 1.45, 3.5),
new THREE.Vector3( 3.1, 1.60, 1.7),
new THREE.Vector3( 3.5, 1.72, -0.5),
new THREE.Vector3( 2.2, 1.95, -1.3),
new THREE.Vector3( 0.9, 1.55, 0.7),
new THREE.Vector3(-0.5, 1.35, 2.7)
];
const lookPts = [
new THREE.Vector3( 0.2, 1.00, -2.0),
new THREE.Vector3( 0.6, 0.90, -1.0),
new THREE.Vector3( 1.0, 0.75, 1.2),
new THREE.Vector3( 1.0, 0.85, 0.9),
new THREE.Vector3( 0.0, 1.00, -1.6),
new THREE.Vector3(-1.4, 1.35, -6.0),
new THREE.Vector3( 1.0, 0.75, 1.4),
new THREE.Vector3( 0.3, 1.00, -2.0)
];
const camCurve = new THREE.CatmullRomCurve3(camPts, true, 'catmullrom', 0.5);
const lookCurve = new THREE.CatmullRomCurve3(lookPts, true, 'catmullrom', 0.5);
const _p = new THREE.Vector3(), _l = new THREE.Vector3();
// lightning envelope (0 at loop ends, flashes near t=8)
const lightning = (t) => {
let f = 0.0;
f += 1.00 * Math.exp(-Math.pow((t - 7.85) / 0.07, 2));
f += 0.70 * Math.exp(-Math.pow((t - 8.12) / 0.05, 2));
f += 0.55 * Math.exp(-Math.pow((t - 8.55) / 0.13, 2));
return f;
};
// ---- update -----------------------------------------------------
__update = (t) => {
const u = (t / DURATION) % 1.0;
camCurve.getPoint(u, _p);
lookCurve.getPoint(u, _l);
// subtle handheld breathing (loops exactly)
const br = t * (Math.PI * 2 / DURATION);
_p.x += Math.sin(br * 2.0) * 0.03;
_p.y += Math.sin(br * 3.0) * 0.025;
camera.position.copy(_p);
camera.lookAt(_l);
rainMat.uniforms.uTime.value = t;
const f = lightning(t);
cloudMat.uniforms.uLoop.value = u;
cloudMat.uniforms.uFlash.value = f;
flashLight.intensity = 0.02 + f * 5.5;
flashFill.intensity = f * 4.0;
// faint canopy/neon flicker (loops)
const flick = 0.92 + 0.08 * Math.sin(t * (Math.PI * 2 / DURATION) * 9.0);
canStripe.material.emissiveIntensity = 2.4 * flick + f * 1.5;
signBar.material.emissiveIntensity = 2.6 * (0.88 + 0.12 * Math.sin(t * (Math.PI * 2 / DURATION) * 6.0));
};
Model A clearly reads as a rain-soaked nighttime gas station with visible store interior, pumps, sedan, reflections, and a coherent forward-moving camera, though it remains quite blocky and the car dominates too much of the later framing. Model B matches parts of the theme but is severely underlit and poorly composed, with near-unreadable frames and an effectively empty final image that undermines both the cinematic brief and technical polish. (Second judge pass, order swapped — scores are the average of both: Model A clearly reads as a rain-soaked nighttime gas station with a sedan, pump, person, lit storefront, and coherent camera progression, while Model B is extremely dark, partially unreadable, and even includes a near-black frame that breaks the brief. Model A is still stylized and somewhat sparse rather than highly realistic, but it is far more complete, purposeful, and technically stable across the sampled frames.)