Skip to main content

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 runs entirely inside a single HTML file. On load, it bootstraps a Three.js scene, attaches a full-window WebGL renderer to document.body, and starts three independent requestAnimationFrame loops — one for the render pass, one for road/scenery recycling, and one for wheel animation. Everything from fog colour to light positions is coded inline with exact numeric constants, making the file self-contained with no build step and no external assets beyond the Three.js CDN bundle.

CDN Import Map

Three.js and its OrbitControls add-on are loaded from jsDelivr via a native importmap. No bundler or node_modules is required.
<script type="importmap">
{
    "imports": {
        "three": "https://cdn.jsdelivr.net/npm/three@0.128.0/build/three.module.js",
        "three/addons/": "https://cdn.jsdelivr.net/npm/three@0.128.0/examples/jsm/"
    }
}
</script>
import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
Three.js r128 is pinned. Upgrading to a newer release may require adjusting the OrbitControls import path and verifying that FogExp2, MeshStandardMaterial, and PointsMaterial APIs remain compatible.

Renderer

A WebGLRenderer with hardware anti-aliasing is created, sized to the full browser viewport, and appended directly to document.body. Shadow mapping is enabled globally so any mesh with castShadow = true participates in the shadow pass.
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
document.body.appendChild(renderer.domElement);
A resize listener keeps the renderer and camera projection matrix in sync with the viewport:
window.addEventListener('resize', () => {
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
    renderer.setSize(window.innerWidth, window.innerHeight);
});

Scene

The scene background is a deep night-sky blue (0x0a1030) and uses exponential fog of the same colour to fade distant objects smoothly.
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x0a1030);
scene.fog = new THREE.FogExp2(0x0a1030, 0.005);
The updateTime() function mutates both scene.background and scene.fog.color every frame so they interpolate from night blue to sky blue as the time-of-day slider moves.

Camera

A perspective camera with a 45° vertical field of view is placed slightly above and behind the car to give a classic third-person driving view.
const camera = new THREE.PerspectiveCamera(
    45,                                    // fov
    window.innerWidth / window.innerHeight, // aspect
    0.1,                                   // near
    1000                                   // far
);
camera.position.set(5, 3.5, 9);
OrbitControls allow the user to orbit, pan, and zoom freely. Damping is enabled for smooth deceleration.
const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.target.set(0, 0.5, 0);

Lighting Setup

Three lights provide the base illumination for the scene.

Ambient Light

const ambientLight = new THREE.AmbientLight(0x404060);
scene.add(ambientLight);
A cool blue-grey fill (0x404060) that lifts shadows uniformly across all surfaces. Its intensity is dynamically adjusted by updateTime() — it rises to 0.6 at noon and drops to 0.3 at midnight.

Directional Light — Sun

const dirLight = new THREE.DirectionalLight(0xfff5e6, 1.2);
dirLight.position.set(5, 10, 7);
dirLight.castShadow = true;
scene.add(dirLight);
A warm white (0xfff5e6) directional light simulating sunlight at intensity 1.2. It is the primary shadow caster; castShadow = true activates the shadow map pass for this light. updateTime() scales its intensity with the sun angle.

Point Light — Blue Fill

const fillLight = new THREE.PointLight(0x88aaff, 0.3);
fillLight.position.set(0, 2, 0);
scene.add(fillLight);
A subtle light-blue point light (0x88aaff) centered just above the road surface. It adds cool specular highlights to the car body at night and is never toggled — it is always on at intensity 0.3.

Sun and Moon Meshes

The sun and moon are rendered as SphereGeometry meshes with MeshStandardMaterial. Their positions and emissiveIntensity are updated every time updateTime() is called.
// Sun
const sunMat = new THREE.MeshStandardMaterial({
    color: 0xffaa66,
    emissive: 0xff4411,
    emissiveIntensity: 0
});
const sunMesh = new THREE.Mesh(new THREE.SphereGeometry(1.2, 64, 64), sunMat);
scene.add(sunMesh);

// Moon
const moonMat = new THREE.MeshStandardMaterial({
    color: 0xdddddd,
    emissive: 0x88aaff,
    emissiveIntensity: 0
});
const moonMesh = new THREE.Mesh(new THREE.SphereGeometry(0.9, 64, 64), moonMat);
scene.add(moonMesh);
BodyGeometryColorEmissive
SunSphereGeometry(1.2, 64, 64)0xffaa660xff4411
MoonSphereGeometry(0.9, 64, 64)0xdddddd0x88aaff
updateTime() calculates the arc position from the hour value and toggles sunMesh.visible / moonMesh.visible depending on which body is above the horizon:
const angle = (hour / 12) * Math.PI;
const radius = 18;
const sunY = Math.sin(angle) * radius;
const sunX = Math.cos(angle) * radius;

sunMesh.visible  = Math.sin(angle) > 0;
moonMesh.visible = Math.sin(angle) < 0;

if (sunMesh.visible) {
    sunMesh.position.set(sunX, sunY + 2, -8);
    sunMesh.material.emissiveIntensity = Math.sin(angle) * 1.5;
}
if (moonMesh.visible) {
    moonMesh.position.set(-sunX, sunY + 1, -6);
    moonMesh.material.emissiveIntensity = -Math.sin(angle) * 0.5;
}

Render Loop

The main render loop calls controls.update() every frame (required for enableDamping) and then issues the draw call.
function renderLoop() {
    requestAnimationFrame(renderLoop);
    controls.update();
    renderer.render(scene, camera);
}
renderLoop();
Dream Car runs three separate requestAnimationFrame loops: renderLoop() for drawing, updateScenery() for moving road lines and landscape objects, and animateCar() for wheel rotation. They are independent and started in sequence at the bottom of the script.

Build docs developers (and LLMs) love