🎮ArcadeLab

3D Highway Traffic Racer

by BraveBuilder54
479 lines16.4 KB🛠️ Three.js (3D graphics)
▶ Play
<!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.