Documentation Index
Fetch the complete documentation index at: https://mintlify.com/JustADev1024/threejs-car/llms.txt
Use this file to discover all available pages before exploring further.
Dream Car simulates the full arc of a day entirely in the browser. Dragging a single range slider advances or rewinds time from midnight through noon and back again, continuously repainting the sky, repositioning the sun and moon, and dimming or brightening every light in the scene — all in real time without a page reload.
The Time Slider
The slider is a plain HTML <input type="range"> wired directly to the Three.js scene:
<input type="range" id="timeSlider" min="0" max="24" step="0.1" value="14" class="slider">
<span id="timeDisplay">14:00</span>
| Attribute | Value | Meaning |
|---|
min | 0 | Midnight |
max | 24 | End of day (same as midnight) |
step | 0.1 | ~6-minute increments |
value | 14 | Starts at 2:00 PM |
Every input event on the slider calls updateTime(hour) with the new floating-point hour value:
timeSlider.addEventListener('input', e => updateTime(parseFloat(e.target.value)));
updateTime(14); // also called once at startup
updateTime(hour) in Detail
updateTime is the single function that drives the entire day/night transition. Here is the full implementation from the source:
function updateTime(hour) {
const h = Math.floor(hour);
const m = Math.floor((hour % 1) * 60);
timeDisplay.textContent = `${h.toString().padStart(2,'0')}:${m.toString().padStart(2,'0')}`;
const angle = (hour / 12) * Math.PI;
const radius = 18;
const sunY = Math.sin(angle) * radius;
const sunX = Math.cos(angle) * radius;
const sunVisible = Math.sin(angle) > 0;
const moonVisible = Math.sin(angle) < 0;
sunMesh.visible = sunVisible;
moonMesh.visible = moonVisible;
if (sunVisible) {
sunMesh.position.set(sunX, sunY + 2, -8);
sunMesh.material.emissiveIntensity = Math.sin(angle) * 1.5;
}
if (moonVisible) {
moonMesh.position.set(-sunX, sunY + 1, -6);
moonMesh.material.emissiveIntensity = -Math.sin(angle) * 0.5;
}
let intensity = (1 - Math.cos(angle)) / 2;
intensity = Math.max(0.1, intensity * 1.2);
dirLight.intensity = intensity;
ambientLight.intensity = 0.3 + intensity * 0.3;
const nightColor = new THREE.Color(0x0a1030);
const dayColor = new THREE.Color(0x87CEEB);
const skyColor = nightColor.clone().lerp(dayColor, intensity);
scene.background = skyColor;
scene.fog.color = skyColor;
}
Step-by-step breakdown
1. Time display formatting
The fractional hour is split into whole hours and minutes, both zero-padded to two digits and written into #timeDisplay — for example 14.5 → "14:30".
2. Sun/moon angle
const angle = (hour / 12) * Math.PI;
This maps the 0–24 hour range onto a full 0–2π revolution. At hour 0 the angle is 0; at hour 12 it is π; at hour 24 it wraps back to 2π (≡ 0). The sun and moon travel opposite arcs of the same circle.
3. Visibility gating
Math.sin(angle) is positive between hours 0 and 12 (daytime arc) and negative between hours 12 and 24 (night arc).
sunVisible = Math.sin(angle) > 0 — sun shown during the day half
moonVisible = Math.sin(angle) < 0 — moon shown during the night half
Each mesh’s .visible flag is set accordingly, so the inactive body is simply skipped by the renderer.
4. 3D position with radius 18
Both bodies orbit in a circle of radius 18 world-units centred near the origin, offset slightly in Z to give depth:
sunMesh.position.set(sunX, sunY + 2, -8); // 2 units above the mathematical arc
moonMesh.position.set(-sunX, sunY + 1, -6); // mirrored X, 1 unit up, closer
The +2 / +1 Y offsets prevent the sun and moon from dipping below the horizon at exactly 0°/180°.
5. Directional light intensity
let intensity = (1 - Math.cos(angle)) / 2; // 0 at midnight, 1 at noon
intensity = Math.max(0.1, intensity * 1.2); // floor of 0.1, boosted by 20%
dirLight.intensity = intensity;
ambientLight.intensity = 0.3 + intensity * 0.3;
(1 - cos θ) / 2 is a smooth [0, 1] wave that peaks exactly at solar noon (hour 12). The floor of 0.1 means the scene is never completely dark — a faint bluish ambient light always remains.
6. Sky colour lerp
const nightColor = new THREE.Color(0x0a1030); // deep navy
const dayColor = new THREE.Color(0x87CEEB); // sky blue
const skyColor = nightColor.clone().lerp(dayColor, intensity);
scene.background = skyColor;
scene.fog.color = skyColor;
THREE.Color.lerp blends linearly between the two hex values using intensity as the weight. Setting scene.fog.color to the same value ensures that distant geometry fades into the correct sky hue rather than a mismatched horizon.
Lighting Setup
Three lights are added to the scene at startup and remain fixed throughout the session. updateTime modulates their intensities but does not move them.
// Soft blue-toned ambient fill
const ambientLight = new THREE.AmbientLight(0x404060);
scene.add(ambientLight);
// Primary warm directional light (sun simulation)
const dirLight = new THREE.DirectionalLight(0xfff5e6, 1.2);
dirLight.position.set(5, 10, 7);
dirLight.castShadow = true;
scene.add(dirLight);
// Cool blue-white fill to simulate sky bounce
const fillLight = new THREE.PointLight(0x88aaff, 0.3);
fillLight.position.set(0, 2, 0);
scene.add(fillLight);
| Light | Color | Notes |
|---|
AmbientLight(0x404060) | Desaturated blue-grey | Base fill; intensity scaled by updateTime |
DirectionalLight(0xfff5e6, 1.2) | Warm white | Main shadow-caster; intensity driven by time |
PointLight(0x88aaff, 0.3) | Cool blue-white | Constant sky-bounce fill; not time-driven |
Sun and Moon Meshes
Both celestial bodies are simple SphereGeometry meshes with MeshStandardMaterial. Their emissive channel makes them glow independently of scene lighting.
// Sun — large warm sphere
const sunMat = new THREE.MeshStandardMaterial({
color: 0xffaa66,
emissive: 0xff4411,
emissiveIntensity: 0 // driven by updateTime
});
const sunMesh = new THREE.Mesh(new THREE.SphereGeometry(1.2, 64, 64), sunMat);
scene.add(sunMesh);
// Moon — smaller cool sphere
const moonMat = new THREE.MeshStandardMaterial({
color: 0xdddddd,
emissive: 0x88aaff,
emissiveIntensity: 0 // driven by updateTime
});
const moonMesh = new THREE.Mesh(new THREE.SphereGeometry(0.9, 64, 64), moonMat);
scene.add(moonMesh);
| Body | Geometry | Base color | Emissive color |
|---|
| Sun | SphereGeometry(1.2, 64, 64) | 0xffaa66 (amber) | 0xff4411 (orange-red) |
| Moon | SphereGeometry(0.9, 64, 64) | 0xdddddd (light grey) | 0x88aaff (blue-white) |
At noon (hour=12), Math.sin(π) ≈ 0 and the emissive intensity drops to zero. At hour 6 (angle = π/2), sin(π/2) = 1 and the sun reaches peak brightness — the warm orange glow that gives midday its distinctive look. After hour 12 the sun is no longer visible and the moon takes over the night arc.
The sun tracks a circular arc driven by const angle = (hour / 12) * Math.PI, which maps the full 24-hour day onto a complete revolution (0 → 2π). At hour 6 (angle = π/2) sin is 1 and the sun sits at the peak of its arc. At hour 12 (angle = π) sin returns to 0, so sunVisible becomes false and the moon takes over. Hours 12–24 form the night arc (π → 2π), during which Math.sin(angle) < 0 and only the moon is rendered.