🎮ArcadeLab

Desi Open World 3D

by CosmicFlare41
527 lines11.5 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">
<title>Desi Open World 3D</title>

<style>
body {
    margin: 0;
    overflow: hidden;
    font-family: Arial, sans-serif;
    background: #87ceeb;
}

canvas {
    display: block;
}

#info {
    position: absolute;
    top: 10px;
    left: 10px;
    color: white;
    background: rgba(0,0,0,0.75);
    padding: 12px;
    border-radius: 10px;
    line-height: 1.5;
}

#controls {
    position: absolute;
    bottom: 20px;
    left: 10px;
    color: white;
    background: rgba(0,0,0,0.65);
    padding: 10px;
    border-radius: 10px;
}
</style>
</head>

<body>

<div id="info">
    <b>DESI OPEN WORLD</b><br>
    3D Indian Driving Game<br>
    <span id="speed">Speed: 0 km/h</span><br>
    WASD / Arrow keys = Drive<br>
    C = Change Camera<br>
    R = Reset Car
</div>

<div id="controls">
    Explore the Indian countryside!
</div>

<script type="module">
import * as THREE from
'https://cdn.jsdelivr.net/npm/three@0.160.0/build/three.module.js';

const scene = new THREE.Scene();

scene.background = new THREE.Color(0x87ceeb);
scene.fog = new THREE.Fog(0x87ceeb, 150, 900);

const camera = new THREE.PerspectiveCamera(
    70,
    window.innerWidth / window.innerHeight,
    0.1,
    1500
);

const renderer = new THREE.WebGLRenderer({
    antialias: true
});

renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
renderer.toneMapping = THREE.ACESFilmicToneMapping;

document.body.appendChild(renderer.domElement);

// Lighting
scene.add(new THREE.HemisphereLight(0xffffff, 0x607044, 2));

const sunlight = new THREE.DirectionalLight(0xfff1d0, 3);
sunlight.position.set(-100, 150, 100);
sunlight.castShadow = true;
scene.add(sunlight);

// Materials
function material(color, roughness = 0.9) {
    return new THREE.MeshStandardMaterial({
        color,
        roughness
    });
}

// Ground
const ground = new THREE.Mesh(
    new THREE.PlaneGeometry(1400, 1400),
    material(0x629447)
);

ground.rotation.x = -Math.PI / 2;
ground.receiveShadow = true;
scene.add(ground);

// Mountains and hills
function makeHill(x, z, width, height, depth, color) {
    const hill = new THREE.Mesh(
        new THREE.SphereGeometry(1, 24, 16),
        material(color)
    );

    hill.position.set(x, height * 0.25, z);
    hill.scale.set(width, height, depth);
    hill.castShadow = true;
    scene.add(hill);
}

for (let i = 0; i < 28; i++) {
    const angle = i / 28 * Math.PI * 2;
    const radius = 420 + Math.random() * 100;

    makeHill(
        Math.cos(angle) * radius,
        Math.sin(angle) * radius,
        65 + Math.random() * 50,
        35 + Math.random() * 35,
        70 + Math.random() * 40,
        0x748c55
    );
}

for (let i = 0; i < 14; i++) {
    const mountain = new THREE.Mesh(
        new THREE.ConeGeometry(55 + Math.random() * 35, 150, 5),
        material(0x82918b)
    );

    mountain.position.set(-500 + i * 75, 65, -480);
    mountain.rotation.y = Math.random() * Math.PI;
    scene.add(mountain);
}

// Roads
function makeRoad(x, z, width, length) {
    const road = new THREE.Mesh(
        new THREE.PlaneGeometry(width, length),
        material(0x303337)
    );

    road.rotation.x = -Math.PI / 2;
    road.position.set(x, 0.04, z);
    road.receiveShadow = true;
    scene.add(road);
}

makeRoad(0, 0, 24, 1100);
makeRoad(0, 0, 1100, 24);

// Road markings
for (let z = -530; z < 530; z += 22) {
    const line = new THREE.Mesh(
        new THREE.PlaneGeometry(0.4, 9),
        material(0xffffff)
    );

    line.rotation.x = -Math.PI / 2;
    line.position.set(0, 0.07, z);
    scene.add(line);
}

for (let x = -530; x < 530; x += 22) {
    const line = new THREE.Mesh(
        new THREE.PlaneGeometry(9, 0.4),
        material(0xffffff)
    );

    line.rotation.x = -Math.PI / 2;
    line.position.set(x, 0.07, 0);
    scene.add(line);
}

// Trees
function makeTree(x, z, scale = 1) {
    const tree = new THREE.Group();

    const trunk = new THREE.Mesh(
        new THREE.CylinderGeometry(0.3, 0.5, 4, 8),
        material(0x654321)
    );

    trunk.position.y = 2;
    tree.add(trunk);

    const leaves = new THREE.Mesh(
        new THREE.SphereGeometry(2.5, 12, 10),
        material(0x237a36)
    );

    leaves.position.y = 5;
    leaves.castShadow = true;
    tree.add(leaves);

    const top = new THREE.Mesh(
        new THREE.SphereGeometry(1.8, 12, 10),
        material(0x368c42)
    );

    top.position.y = 6.3;
    tree.add(top);

    tree.position.set(x, 0, z);
    tree.scale.setScalar(scale);
    scene.add(tree);
}

for (let i = 0; i < 260; i++) {
    const x = (Math.random() - 0.5) * 1100;
    const z = (Math.random() - 0.5) * 1100;

    if (Math.abs(x) < 18 || Math.abs(z) < 18) continue;

    makeTree(x, z, 0.7 + Math.random() * 1.1);
}

// Village buildings
function makeHouse(x, z, color) {
    const house = new THREE.Group();

    const walls = new THREE.Mesh(
        new THREE.BoxGeometry(10, 7, 9),
        material(color)
    );

    walls.position.y = 3.5;
    walls.castShadow = true;
    house.add(walls);

    const roof = new THREE.Mesh(
        new THREE.ConeGeometry(7.5, 3, 4),
        material(0x984d31)
    );

    roof.position.y = 8.5;
    roof.rotation.y = Math.PI / 4;
    house.add(roof);

    const door = new THREE.Mesh(
        new THREE.BoxGeometry(1.5, 3.4, 0.15),
        material(0x49301e)
    );

    door.position.set(0, 1.7, 4.6);
    house.add(door);

    house.position.set(x, 0, z);
    scene.add(house);
}

const houseColors = [
    0xe8d1a5,
    0xd5c8ad,
    0xc4b69c,
    0xe0d7c6
];

for (let i = 0; i < 30; i++) {
    makeHouse(
        140 + (i % 6) * 18,
        120 + Math.floor(i / 6) * 20,
        houseColors[i % houseColors.length]
    );
}

// Pond
const pond = new THREE.Mesh(
    new THREE.CircleGeometry(42, 40),
    new THREE.MeshStandardMaterial({
        color: 0x168fc0,
        roughness: 0.25
    })
);

pond.rotation.x = -Math.PI / 2;
pond.position.set(280, 0.08, -250);
scene.add(pond);

// Animals
function makeAnimal(x, z, color, size = 1) {
    const animal = new THREE.Group();

    const body = new THREE.Mesh(
        new THREE.BoxGeometry(2.3, 1.1, 1),
        material(color)
    );

    body.position.y = 1.2;
    animal.add(body);

    const head = new THREE.Mesh(
        new THREE.BoxGeometry(0.65, 0.7, 0.65),
        material(color)
    );

    head.position.set(1.2, 1.7, 0);
    animal.add(head);

    for (let i = 0; i < 4; i++) {
        const leg = new THREE.Mesh(
            new THREE.CylinderGeometry(0.1, 0.12, 0.8, 6),
            material(0x51402f)
        );

        leg.position.set(
            i < 2 ? -0.75 : 0.75,
            0.4,
            i % 2 ? -0.3 : 0.3
        );

        animal.add(leg);
    }

    animal.position.set(x, 0, z);
    animal.scale.setScalar(size);
    animal.rotation.y = Math.random() * Math.PI * 2;

    scene.add(animal);
}

for (let i = 0; i < 12; i++) {
    makeAnimal(
        (Math.random() - 0.5) * 650,
        (Math.random() - 0.5) * 650,
        i % 2 ? 0xeee6d4 : 0x99724f,
        0.8 + Math.random() * 0.4
    );
}

// Indian-style vehicles
function makeVehicle(color, type = "car") {
    const vehicle = new THREE.Group();

    const body = new THREE.Mesh(
        new THREE.BoxGeometry(
            type === "jeep" ? 3.1 : 2.6,
            0.85,
            type === "auto" ? 3.1 : 4.2
        ),
        material(color, 0.4)
    );

    body.position.y = 1;
    body.castShadow = true;
    vehicle.add(body);

    if (type !== "auto") {
        const cabin = new THREE.Mesh(
            new THREE.BoxGeometry(2, 0.9, 2.1),
            material(0x274455, 0.25)
        );

        cabin.position.set(0, 1.7, -0.15);
        vehicle.add(cabin);

        const roof = new THREE.Mesh(
            new THREE.BoxGeometry(2.1, 0.15, 2.2),
            material(color)
        );

        roof.position.set(0, 2.2, -0.15);
        vehicle.add(roof);
    } else {
        const roof = new THREE.Mesh(
            new THREE.BoxGeometry(1.8, 0.2, 1.8),
            material(0x252525)
        );

        roof.position.y = 2;
        vehicle.add(roof);
    }

    const wheels = [];

    for (const x of [-1.25, 1.25]) {
        for (const z of [-1.35, 1.35]) {
            const wheel = new THREE.Mesh(
                new THREE.CylinderGeometry(0.42, 0.42, 0.28, 16),
                material(0x151515)
            );

            wheel.rotation.z = Math.PI / 2;
            wheel.position.set(x, 0.45, z);
            wheel.castShadow = true;
            vehicle.add(wheel);
            wheels.push(wheel);
        }
    }

    return { vehicle, wheels };
}

// Player car
const playerData = makeVehicle(0xd62828);
const car = playerData.vehicle;
scene.add(car);

// Parked vehicles
const suv = makeVehicle(0xe9e9e9, "jeep");
suv.vehicle.position.set(15, 0, 4);
scene.add(suv.vehicle);

const auto = makeVehicle(0x1da36c, "auto");
auto.vehicle.position.set(22, 0, -5);
scene.add(auto.vehicle);

const blueCar = makeVehicle(0x2469d8);
blueCar.vehicle.position.set(-12, 0, 5);
scene.add(blueCar.vehicle);

// Driving
const keys = {};
let speed = 0;
let heading = 0;
let cameraMode = 0;

window.addEventListener("keydown", event => {
    keys[event.key.toLowerCase()] = true;

    if (event.key.toLowerCase() === "c") {
        cameraMode = (cameraMode + 1) % 2;
    }

    if (event.key.toLowerCase() === "r") {
        car.position.set(0, 0, 0);
        speed = 0;
        heading = 0;
    }
});

window.addEventListener("keyup", event => {
    keys[event.key.toLowerCase()] = false;
});

function animate() {
    requestAnimationFrame(animate);

    if (keys["w"] || keys["arrowup"]) {
        speed = Math.min(speed + 0.015, 0.7);
    } else if (keys["s"] || keys["arrowdown"]) {
        speed = Math.max(speed - 0.02, -0.3);
    } else {
        speed *= 0.985;
    }

    if (keys[" "]) speed *= 0.92;

    if (Math.abs(speed) > 0.005) {
        if (keys["a"] || keys["arrowleft"]) {
            heading += 0.035 * Math.sign(speed);
        }

        if (keys["d"] || keys["arrowright"]) {
            heading -= 0.035 * Math.sign(speed);
        }
    }

    car.rotation.y = heading;

    car.position.x += Math.sin(heading) * speed;
    car.position.z += Math.cos(heading) * speed;

    car.position.x = THREE.MathUtils.clamp(car.position.x, -650, 650);
    car.position.z = THREE.MathUtils.clamp(car.position.z, -650, 650);

    playerData.wheels.forEach(wheel => {
        wheel.rotation.x += speed * 1.5;
    });

    const forward = new THREE.Vector3(
        Math.sin(heading), 0, Math.cos(heading)
    );

    if (cameraMode === 0) {
        const desired = car.position.clone()
            .addScaledVector(forward, -10)
            .add(new THREE.Vector3(0, 5, 0));

        camera.position.lerp(desired, 0.1);

        camera.lookAt(
            car.position.clone().add(new THREE.Vector3(0, 1.5, 0))
        );
    } else {
        camera.position.copy(car.position)
            .add(new THREE.Vector3(0, 2.4, 0))
            .addScaledVector(forward, 1.1);

        camera.lookAt(
            car.position.clone()
                .addScaledVector(forward, 20)
                .add(new THREE.Vector3(0, 1.5, 0))
        );
    }

    document.getElementById("speed").textContent =
        "Speed: " + Math.round(Math.abs(speed) * 180) + " km/h";

    renderer.render(scene, camera);
}

animate();

window.addEventListener("resize", () => {
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();

    renderer.setSize(window.innerWidth, window.innerHeight);
});
</script>
</body>
</html>

Game Source: Desi Open World 3D

Creator: CosmicFlare41

Libraries: three

Complexity: complex (527 lines, 11.5 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: desi-open-world-3d-cosmicflare41" to link back to the original. Then publish at arcadelab.ai/publish.