3D Highway Traffic Racer
by BraveBuilder54479 lines16.4 KB🛠️ Three.js (3D graphics)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>3D Highway Traffic Racer</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
user-select: none;
-webkit-user-select: none;
}
body {
background-color: #050505;
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-between;
height: 100vh;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
color: #fff;
overflow: hidden;
touch-action: none;
}
#game-view {
position: relative;
width: 100%;
max-width: 420px;
height: 65vh;
margin-top: 10px;
border: 2px solid #333;
border-radius: 12px;
overflow: hidden;
box-shadow: 0 0 25px rgba(0,0,0,0.8);
}
canvas {
width: 100%;
height: 100%;
display: block;
}
#ui-layer {
position: absolute;
top: 10px;
left: 10px;
right: 10px;
display: flex;
justify-content: space-between;
pointer-events: none;
z-index: 10;
}
.badge {
background: rgba(0, 0, 0, 0.75);
padding: 6px 12px;
border-radius: 6px;
font-size: 13px;
font-weight: bold;
border: 1px solid rgba(255, 255, 255, 0.2);
backdrop-filter: blur(4px);
}
#game-over {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.88);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
z-index: 20;
}
#game-over h1 {
color: #ff3838;
font-size: 32px;
margin-bottom: 8px;
text-shadow: 0 0 10px rgba(255,0,0,0.5);
}
#game-over button {
margin-top: 18px;
padding: 12px 28px;
font-size: 18px;
font-weight: bold;
color: #fff;
background: linear-gradient(45deg, #10ac84, #1dd1a1);
border: none;
border-radius: 25px;
box-shadow: 0 4px 15px rgba(29, 209, 161, 0.4);
cursor: pointer;
}
#controls {
width: 100%;
max-width: 420px;
height: 30vh;
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: repeat(2, 1fr);
gap: 10px;
padding: 10px;
background-color: #111;
}
.btn {
background-color: #222;
color: #fff;
font-size: 26px;
font-weight: bold;
border: 1px solid #444;
border-radius: 14px;
display: flex;
align-items: center;
justify-content: center;
touch-action: none;
box-shadow: 0 5px #000;
}
.btn:active {
background-color: #ff3838;
transform: translateY(3px);
box-shadow: 0 2px #000;
}
#btn-up { grid-column: 2; grid-row: 1; }
#btn-left { grid-column: 1; grid-row: 2; }
#btn-down { grid-column: 2; grid-row: 2; }
#btn-right { grid-column: 3; grid-row: 2; }
.hide { display: none !important; }
</style>
</head>
<body>
<div id="game-view">
<div id="ui-layer">
<div class="badge">Score: <span id="score">0</span></div>
<div class="badge" style="color: #1dd1a1;">Speed: <span id="speed-txt">Slow</span></div>
<div class="badge" style="color: #feca57;"><span id="env-txt">River Bridge</span></div>
</div>
<div id="game-over" class="hide">
<h1>CRASHED!</h1>
<p style="font-size: 18px;">Score: <span id="final-score">0</span></p>
<button onclick="restartGame()">Drive Again</button>
</div>
</div>
<!-- Mobile Touch Controls -->
<div id="controls">
<div class="btn" id="btn-up">▲</div>
<div class="btn" id="btn-left">◀</div>
<div class="btn" id="btn-down">▼</div>
<div class="btn" id="btn-right">▶</div>
</div>
<!-- Three.js 3D Library -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<script>
const viewContainer = document.getElementById('game-view');
const scoreEl = document.getElementById('score');
const speedTxtEl = document.getElementById('speed-txt');
const envTxtEl = document.getElementById('env-txt');
const finalScoreEl = document.getElementById('final-score');
const gameOverScreen = document.getElementById('game-over');
// Scene, Camera, Renderer
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(55, viewContainer.clientWidth / viewContainer.clientHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(viewContainer.clientWidth, viewContainer.clientHeight);
renderer.shadowMap.enabled = true;
viewContainer.appendChild(renderer.domElement);
// Lighting
const ambientLight = new THREE.AmbientLight(0xffffff, 0.7);
scene.add(ambientLight);
const dirLight = new THREE.DirectionalLight(0xffffff, 0.8);
dirLight.position.set(30, 50, 20);
dirLight.castShadow = true;
scene.add(dirLight);
// Road Creation
const roadWidth = 10;
const roadLength = 200;
const roadGeo = new THREE.PlaneGeometry(roadWidth, roadLength);
const roadMat = new THREE.MeshStandardMaterial({ color: 0x444444, roughness: 0.6 });
const road = new THREE.Mesh(roadGeo, roadMat);
road.rotation.x = -Math.PI / 2;
scene.add(road);
// Water/Terrain Planes (Sides of the bridge)
const sideTerrainGeo = new THREE.PlaneGeometry(80, roadLength);
const sideTerrainMat = new THREE.MeshStandardMaterial({ color: 0x0077be, roughness: 0.1 }); // Blue Water by default
const leftSide = new THREE.Mesh(sideTerrainGeo, sideTerrainMat);
leftSide.position.set(-45, -0.5, 0);
leftSide.rotation.x = -Math.PI / 2;
scene.add(leftSide);
const rightSide = new THREE.Mesh(sideTerrainGeo, sideTerrainMat);
rightSide.position.set(45, -0.5, 0);
rightSide.rotation.x = -Math.PI / 2;
scene.add(rightSide);
// Guardrails / Side Jangla (Metallic Barriers)
function createGuardrail(xPos) {
const group = new THREE.Group();
// Horizontal Bar
const barGeo = new THREE.BoxGeometry(0.3, 0.5, roadLength);
const barMat = new THREE.MeshStandardMaterial({ color: 0xcccccc, metalness: 0.8, roughness: 0.2 });
const bar = new THREE.Mesh(barGeo, barMat);
bar.position.set(xPos, 0.8, 0);
group.add(bar);
// Vertical Pillars
const pillarGeo = new THREE.BoxGeometry(0.2, 1.2, 0.2);
for (let z = -100; z < 100; z += 6) {
const pillar = new THREE.Mesh(pillarGeo, barMat);
pillar.position.set(xPos, 0.4, z);
group.add(pillar);
}
return group;
}
const leftRail = createGuardrail(-5.1);
const rightRail = createGuardrail(5.1);
scene.add(leftRail);
scene.add(rightRail);
// Animated Center White Road Lines
const lineGeo = new THREE.PlaneGeometry(0.25, 4);
const lineMat = new THREE.MeshBasicMaterial({ color: 0xffffff });
const roadLines = [];
for (let i = -100; i < 100; i += 8) {
const line = new THREE.Mesh(lineGeo, lineMat);
line.rotation.x = -Math.PI / 2;
line.position.set(0, 0.02, i);
scene.add(line);
roadLines.push(line);
}
// Environments Configurations
const environments = [
{ name: "River Bridge", skyColor: 0x87ceeb, sideColor: 0x0077be },
{ name: "Desert Highway", skyColor: 0xe6a15c, sideColor: 0xc2a15f },
{ name: "Mountain Pass", skyColor: 0x4b6584, sideColor: 0x2d3436 },
{ name: "Greenery Valley", skyColor: 0x7bed9f, sideColor: 0x2ed573 }
];
let currentEnvIndex = 0;
scene.background = new THREE.Color(environments[0].skyColor);
// 3D Player Red Sports Car (Lamborghini Style)
function createPlayerCar() {
const group = new THREE.Group();
const bodyGeo = new THREE.BoxGeometry(2.1, 0.65, 4.2);
const bodyMat = new THREE.MeshStandardMaterial({ color: 0xcc0000, metalness: 0.5, roughness: 0.3 });
const body = new THREE.Mesh(bodyGeo, bodyMat);
body.position.y = 0.5;
group.add(body);
const cabinGeo = new THREE.BoxGeometry(1.6, 0.5, 2.0);
const cabinMat = new THREE.MeshStandardMaterial({ color: 0x111111 });
const cabin = new THREE.Mesh(cabinGeo, cabinMat);
cabin.position.set(0, 0.9, -0.1);
group.add(cabin);
// Wheels
const wheelGeo = new THREE.CylinderGeometry(0.35, 0.35, 0.3, 16);
const wheelMat = new THREE.MeshBasicMaterial({ color: 0x111111 });
const positions = [[-1.1, 0.35, 1.3], [1.1, 0.35, 1.3], [-1.1, 0.35, -1.3], [1.1, 0.35, -1.3]];
positions.forEach(p => {
const wheel = new THREE.Mesh(wheelGeo, wheelMat);
wheel.rotation.z = Math.PI / 2;
wheel.position.set(...p);
group.add(wheel);
});
return group;
}
// 3D Traffic Cars
function createTrafficCar(colorHex) {
const group = new THREE.Group();
const bodyGeo = new THREE.BoxGeometry(2.0, 0.75, 4.0);
const bodyMat = new THREE.MeshStandardMaterial({ color: colorHex, metalness: 0.3 });
const body = new THREE.Mesh(bodyGeo, bodyMat);
body.position.y = 0.5;
group.add(body);
const cabinGeo = new THREE.BoxGeometry(1.5, 0.55, 1.8);
const cabinMat = new THREE.MeshStandardMaterial({ color: 0x222222 });
const cabin = new THREE.Mesh(cabinGeo, cabinMat);
cabin.position.set(0, 0.95, -0.2);
group.add(cabin);
return group;
}
const playerCar = createPlayerCar();
playerCar.position.set(1.5, 0, 0);
scene.add(playerCar);
// Camera View (Behind-and-Above Perspective like Screenshot)
camera.position.set(0, 6, 9);
camera.lookAt(0, 1, -12);
// Traffic Array
const carColors = [0xf1c40f, 0x3498db, 0xe67e22, 0xffffff, 0x2d3436];
const enemyCars = [];
function spawnEnemy(zPos) {
const randomColor = carColors[Math.floor(Math.random() * carColors.length)];
const car = createTrafficCar(randomColor);
const laneX = Math.random() > 0.5 ? 2.2 : -2.2;
car.position.set(laneX, 0, zPos);
scene.add(car);
enemyCars.push(car);
}
spawnEnemy(-35);
spawnEnemy(-70);
// Gameplay Variables
let speed = 0.35;
let score = 0;
let isGameOver = false;
let envInterval;
const moveState = { left: false, right: false, up: false, down: false };
function setupButton(id, key) {
const btn = document.getElementById(id);
const start = (e) => { if (e.cancelable) e.preventDefault(); moveState[key] = true; };
const stop = (e) => { if (e.cancelable) e.preventDefault(); moveState[key] = false; };
btn.addEventListener('touchstart', start, { passive: false });
btn.addEventListener('touchend', stop, { passive: false });
btn.addEventListener('touchcancel', stop, { passive: false });
btn.addEventListener('mousedown', start);
btn.addEventListener('mouseup', stop);
btn.addEventListener('mouseleave', stop);
}
setupButton('btn-left', 'left');
setupButton('btn-right', 'right');
setupButton('btn-up', 'up');
setupButton('btn-down', 'down');
function updateSpeed() {
if (score < 100) {
speed = 0.35;
speedTxtEl.innerText = "Slow";
speedTxtEl.style.color = "#1dd1a1";
} else if (score >= 100 && score < 200) {
speed = 0.55;
speedTxtEl.innerText = "Medium";
speedTxtEl.style.color = "#feca57";
} else if (score >= 200 && score < 300) {
speed = 0.75;
speedTxtEl.innerText = "Fast";
speedTxtEl.style.color = "#ff9f43";
} else {
speed = 0.95 + Math.floor((score - 300) / 100) * 0.1;
speedTxtEl.innerText = "Super Fast";
speedTxtEl.style.color = "#ff3838";
}
}
function switchRoadEnv() {
currentEnvIndex = (currentEnvIndex + 1) % environments.length;
const env = environments[currentEnvIndex];
scene.background = new THREE.Color(env.skyColor);
sideTerrainMat.color = new THREE.Color(env.sideColor);
envTxtEl.innerText = env.name;
}
function checkCollision() {
const playerBox = new THREE.Box3().setFromObject(playerCar);
for (let i = 0; i < enemyCars.length; i++) {
const enemyBox = new THREE.Box3().setFromObject(enemyCars[i]);
if (playerBox.intersectsBox(enemyBox)) {
return true;
}
}
return false;
}
function animate() {
if (isGameOver) return;
requestAnimationFrame(animate);
updateSpeed();
// Road Lines Motion
roadLines.forEach(line => {
line.position.z += speed * 2;
if (line.position.z > 15) {
line.position.z = -100;
}
});
// Player Steering (Boundaries inside Guardrails)
if (moveState.left && playerCar.position.x > -3.8) playerCar.position.x -= 0.14;
if (moveState.right && playerCar.position.x < 3.8) playerCar.position.x += 0.14;
if (moveState.up && playerCar.position.z > -12) playerCar.position.z -= 0.1;
if (moveState.down && playerCar.position.z < 2) playerCar.position.z += 0.1;
// Traffic Movement
enemyCars.forEach(car => {
car.position.z += speed;
if (car.position.z > 10) {
car.position.z = -75 - (Math.random() * 25);
car.position.x = Math.random() > 0.5 ? 2.2 : -2.2;
score += 10;
scoreEl.innerText = score;
}
});
if (checkCollision()) {
endGame();
}
renderer.render(scene, camera);
}
function endGame() {
isGameOver = true;
clearInterval(envInterval);
finalScoreEl.innerText = score;
gameOverScreen.classList.remove('hide');
}
function restartGame() {
isGameOver = false;
score = 0;
scoreEl.innerText = score;
playerCar.position.set(1.5, 0, 0);
enemyCars[0].position.set(-2.2, 0, -35);
enemyCars[1].position.set(2.2, 0, -70);
gameOverScreen.classList.add('hide');
envInterval = setInterval(switchRoadEnv, 12000);
animate();
}
window.onload = () => {
envInterval = setInterval(switchRoadEnv, 12000);
animate();
};
window.addEventListener('resize', () => {
camera.aspect = viewContainer.clientWidth / viewContainer.clientHeight;
camera.updateProjectionMatrix();
renderer.setSize(viewContainer.clientWidth, viewContainer.clientHeight);
});
</script>
</body>
</html>Game Source: 3D Highway Traffic Racer
Creator: BraveBuilder54
Libraries: three
Complexity: complex (479 lines, 16.4 KB)
The full source code is displayed above on this page.
Remix Instructions
To remix this game, copy the source code above and modify it. Add a ARCADELAB header at the top with "remix_of: 3d-highway-traffic-racer-bravebuilder54" to link back to the original. Then publish at arcadelab.ai/publish.