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.
The Dream Car vehicle is a THREE.Group assembled entirely from primitive geometries — no .gltf, .obj, or any external 3D model file is loaded. Every panel, window, wheel, and headlight is a BoxGeometry, CylinderGeometry, or SphereGeometry, combined into a single group and added to the scene at the world origin. The illusion of forward motion comes not from moving the car but from scrolling the road and scenery past it — the car itself never changes its world-space position on the Z axis.
Car Group Structure
car (THREE.Group) — world position (0, 0, 0)
├── body BoxGeometry(1.3, 0.55, 2.2)
├── cabin BoxGeometry(1.0, 0.45, 1.1)
├── wheel_FL CylinderGeometry(0.38, 0.38, 0.45, 24)
├── wheel_FR CylinderGeometry(0.38, 0.38, 0.45, 24)
├── wheel_RL CylinderGeometry(0.38, 0.38, 0.45, 24)
├── wheel_RR CylinderGeometry(0.38, 0.38, 0.45, 24)
├── headlightModel SphereGeometry(0.22, 32, 32)
├── headlightSpot SpotLight
├── headlightPoint PointLight
└── particles Points (exhaust system)
Body
The main chassis is a red box mesh with metallic shading.
const bodyMat = new THREE.MeshStandardMaterial({ color: 0xe34234, metalness: 0.7 });
const body = new THREE.Mesh(new THREE.BoxGeometry(1.3, 0.55, 2.2), bodyMat);
body.position.y = 0.3;
body.castShadow = true;
car.add(body);
| Property | Value |
|---|
| Geometry | BoxGeometry(1.3, 0.55, 2.2) |
| Color | 0xe34234 (red) |
| Metalness | 0.7 |
| Position Y | 0.3 |
castShadow | true |
Cabin (Glass)
A semi-transparent blue box sits on top of the body, representing the passenger cabin and windshield glass.
const cabinMat = new THREE.MeshStandardMaterial({
color: 0x88aaff,
transparent: true,
opacity: 0.7
});
const cabin = new THREE.Mesh(new THREE.BoxGeometry(1.0, 0.45, 1.1), cabinMat);
cabin.position.set(0, 0.55, -0.4);
car.add(cabin);
| Property | Value |
|---|
| Geometry | BoxGeometry(1.0, 0.45, 1.1) |
| Color | 0x88aaff (light blue) |
transparent | true |
opacity | 0.7 |
| Position | (0, 0.55, -0.4) |
Wheels
All four wheels share the same CylinderGeometry and material. They are rotated 90° on the Z axis so the cylinder’s axis is horizontal (pointing left–right), and placed at the four corners of the chassis.
const wheelGeo = new THREE.CylinderGeometry(0.38, 0.38, 0.45, 24);
const wheelMatCar = new THREE.MeshStandardMaterial({ color: 0x222222 });
const wheels = [];
[
[-0.75, 0.15, 0.85], // front-left
[ 0.75, 0.15, 0.85], // front-right
[-0.75, 0.15, -0.85], // rear-left
[ 0.75, 0.15, -0.85], // rear-right
].forEach(p => {
const w = new THREE.Mesh(wheelGeo, wheelMatCar);
w.rotation.z = Math.PI / 2;
w.position.set(p[0], p[1], p[2]);
w.castShadow = true;
car.add(w);
wheels.push(w);
});
| Wheel | Position |
|---|
| Front-left | (-0.75, 0.15, 0.85) |
| Front-right | ( 0.75, 0.15, 0.85) |
| Rear-left | (-0.75, 0.15, -0.85) |
| Rear-right | ( 0.75, 0.15, -0.85) |
The wheels array is kept in module scope so animateCar() can update all four rotation values every frame.
Headlight Model
A small orange-tinted sphere on the front of the car provides the visible headlight lens. Its emissiveIntensity is toggled by the headlights button.
const headlightModel = new THREE.Mesh(
new THREE.SphereGeometry(0.22, 32, 32),
new THREE.MeshStandardMaterial({
color: 0xffaa66,
emissive: 0xff4422,
emissiveIntensity: 0 // set to 1.2 when lights are on
})
);
headlightModel.position.set(0, 0.25, 1.15);
car.add(headlightModel);
Two THREE.Light objects also attach to the car group and move with it:
// SpotLight — tight forward beam
const headlightSpot = new THREE.SpotLight(0xffaa66, 0);
headlightSpot.angle = 0.6;
headlightSpot.penumbra = 0.2;
headlightSpot.distance = 20;
headlightSpot.castShadow = true;
headlightSpot.position.set(0, 0.3, 1.3);
car.add(headlightSpot);
// PointLight — ambient glow around the front of the car
const headlightPoint = new THREE.PointLight(0xffaa66, 0, 15);
headlightPoint.castShadow = true;
headlightPoint.position.set(0, 0.3, 1.2);
car.add(headlightPoint);
When headlights are switched on by the user, the click handler sets:
headlightModel.material.emissiveIntensity = 1.2;
headlightSpot.intensity = 1.5;
headlightPoint.intensity = 0.8;
When switched off all three are set to 0.
Exhaust Particle System
The exhaust effect uses THREE.Points with a BufferGeometry containing 200 point positions managed manually each frame.
const particleCount = 200;
const particleGeo = new THREE.BufferGeometry();
const posArray = new Float32Array(particleCount * 3);
particleGeo.setAttribute('position', new THREE.BufferAttribute(posArray, 3));
const particleMat = new THREE.PointsMaterial({
color: 0xccccaa,
size: 0.08,
transparent: true
});
const particles = new THREE.Points(particleGeo, particleMat);
car.add(particles);
The updateExhaust() loop runs every frame. When exhaustOnState is false, particles.visible is set to false and the loop idles cheaply. When true, each particle drifts backward along Z and is reset to the exhaust origin (z = -1.2) when it passes z = -2.5:
function updateExhaust() {
if (!exhaustOnState) {
particles.visible = false;
requestAnimationFrame(updateExhaust);
return;
}
particles.visible = true;
const positions = particles.geometry.attributes.position.array;
for (let i = 0; i < particleCount; i++) {
if (Math.random() < 0.3) {
// Spawn a new particle at exhaust pipe
positions[i * 3] = (Math.random() - 0.5) * 0.4;
positions[i * 3 + 1] = Math.random() * 0.3;
positions[i * 3 + 2] = -1.2 - Math.random() * 0.5;
} else {
// Drift backward
positions[i * 3 + 2] -= 0.07;
if (positions[i * 3 + 2] < -2.5) {
positions[i * 3] = (Math.random() - 0.5) * 0.4;
positions[i * 3 + 1] = Math.random() * 0.3;
positions[i * 3 + 2] = -1.2;
}
}
}
particles.geometry.attributes.position.needsUpdate = true;
requestAnimationFrame(updateExhaust);
}
updateExhaust();
Wheel Animation and Suspension Bounce
animateCar() is the third requestAnimationFrame loop. It increments a shared wheelAngle counter and applies it as an X-axis rotation to every wheel so they roll forward. A tiny Math.sin offset on car.position.y simulates suspension movement over the road surface.
let wheelAngle = 0;
function animateCar() {
requestAnimationFrame(animateCar);
wheelAngle += 0.12;
wheels.forEach(w => w.rotation.x = wheelAngle);
car.position.y = Math.sin(Date.now() * 0.01) * 0.01;
}
animateCar();
| Value | Role |
|---|
wheelAngle += 0.12 | ~6.9° per frame; matches the speed = 0.12 scroll rate |
w.rotation.x | Rolls each wheel around its own axle |
Math.sin(Date.now() * 0.01) * 0.01 | ±1 cm vertical bounce at ~0.6 Hz |
The car group itself is placed at (0, 0, 0) in world space and never translated along Z. All forward-motion visuals come from the road lines and landscape objects scrolling toward negative Z each frame — see Infinite Road for details.