🎮ArcadeLab

Cube Rush 3D

by AtomicBolt46
514 lines11.3 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>Cube Rush 3D</title>
<style>
* { box-sizing: border-box; }
body {
  margin: 0;
  overflow: hidden;
  background: #080b20;
  color: white;
  font-family: Arial, sans-serif;
}
canvas { display: block; }
#hud {
  position: absolute;
  top: 15px;
  left: 15px;
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
  z-index: 2;
}
.stat {
  background: #10152ddd;
  border: 2px solid #00eaff;
  border-radius: 10px;
  padding: 10px 14px;
  font-weight: bold;
}
#overlay {
  position: absolute;
  inset: 0;
  background: #050817bb;
  display: flex;
  justify-content: center;
  align-items: center;
  z-index: 5;
  text-align: center;
}
.panel {
  width: min(90%, 430px);
  padding: 28px;
  border: 3px solid #00eaff;
  border-radius: 20px;
  background: #111a35;
  box-shadow: 0 0 35px #00d9ff55;
}
h1 {
  color: #00eaff;
  font-size: 38px;
  margin: 0 0 10px;
}
button {
  background: #00eaff;
  border: none;
  color: #06101e;
  padding: 14px 25px;
  font-size: 18px;
  font-weight: bold;
  border-radius: 12px;
  cursor: pointer;
  margin-top: 12px;
}
button:hover { background: white; }
#jumpButton {
  position: absolute;
  bottom: 25px;
  right: 25px;
  z-index: 3;
  width: 85px;
  height: 85px;
  border-radius: 50%;
  font-size: 30px;
  border: 3px solid white;
  background: #00d9ffcc;
  touch-action: none;
}
.hint {
  color: #cbd5e1;
  line-height: 1.6;
  font-size: 14px;
}
</style>
</head>
<body>

<div id="hud">
  <div class="stat">⭐ Score: <span id="score">0</span></div>
  <div class="stat">💎 Orbs: <span id="orbs">0</span></div>
  <div class="stat">⚡ Speed: <span id="speed">1.0x</span></div>
</div>

<div id="overlay">
  <div class="panel">
    <h1 id="heading">CUBE RUSH 3D</h1>
    <p id="description">
      Jump over spikes, dodge blocks and collect glowing orbs!
    </p>
    <p class="hint">
      SPACE / UP ARROW / CLICK = JUMP<br>
      On mobile, tap the jump button.<br>
      Time your jumps carefully!
    </p>
    <button id="start">START GAME</button>
  </div>
</div>

<button id="jumpButton">↑</button>

<script type="importmap">
{
  "imports": {
    "three": "https://cdn.jsdelivr.net/npm/three@0.160.0/build/three.module.js"
  }
}
</script>

<script type="module">
import * as THREE from "three";

// ---------------- SCENE ----------------
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x090d2b);
scene.fog = new THREE.Fog(0x090d2b, 25, 75);

const camera = new THREE.PerspectiveCamera(
  60, innerWidth / innerHeight, 0.1, 150
);
camera.position.set(0, 7, 17);
camera.lookAt(0, 2, 0);

const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(innerWidth, innerHeight);
renderer.setPixelRatio(Math.min(devicePixelRatio, 2));
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
document.body.appendChild(renderer.domElement);

scene.add(new THREE.HemisphereLight(0xb8d9ff, 0x252044, 2));

const light = new THREE.DirectionalLight(0xffffff, 2);
light.position.set(-5, 12, 8);
light.castShadow = true;
scene.add(light);

// ---------------- MATERIALS ----------------
const mat = (color, emissive = 0x000000) =>
  new THREE.MeshStandardMaterial({
    color,
    emissive,
    roughness: 0.4,
    metalness: 0.15
  });

const floorMat = mat(0x171e42);
const cubeMat = mat(0x00eaff, 0x006c99);
const spikeMat = mat(0xff315d, 0x77001c);
const blockMat = mat(0xffa52f, 0x713400);
const orbMat = new THREE.MeshStandardMaterial({
  color: 0xffe34d,
  emissive: 0xffa500,
  emissiveIntensity: 1.5,
  metalness: 0.6,
  roughness: 0.2
});

// ---------------- GROUND ----------------
const ground = new THREE.Mesh(
  new THREE.BoxGeometry(200, 0.6, 12),
  floorMat
);
ground.position.set(0, -0.35, 0);
ground.receiveShadow = true;
scene.add(ground);

const groundLine = new THREE.Mesh(
  new THREE.BoxGeometry(200, 0.08, 12.1),
  mat(0x00bfff, 0x0055ff)
);
groundLine.position.set(0, -0.02, 0);
scene.add(groundLine);

// Background grid
const grid = new THREE.GridHelper(100, 50, 0x174a85, 0x18264b);
grid.position.y = -0.02;
grid.position.z = -5;
scene.add(grid);

// Decorative glowing pillars
const decorations = [];
for (let i = 0; i < 35; i++) {
  const h = 2 + Math.random() * 9;
  const pillar = new THREE.Mesh(
    new THREE.BoxGeometry(0.5, h, 0.5),
    mat(0x202d67, 0x101c4b)
  );
  pillar.position.set(
    -20 + i * 2.5,
    h / 2 - 0.1,
    -5 - Math.random() * 8
  );
  scene.add(pillar);
  decorations.push(pillar);
}

// ---------------- PLAYER ----------------
const player = new THREE.Group();
scene.add(player);

const cube = new THREE.Mesh(
  new THREE.BoxGeometry(1.25, 1.25, 1.25),
  cubeMat
);
cube.castShadow = true;
player.add(cube);

// Bright front face decoration
const face = new THREE.Mesh(
  new THREE.BoxGeometry(0.7, 0.7, 0.08),
  mat(0xffffff, 0x0099aa)
);
face.position.set(0, 0, 0.66);
player.add(face);

const playerX = -5;
player.position.set(playerX, 0.65, 0);

// ---------------- GAME STATE ----------------
let playing = false;
let dead = false;
let score = 0;
let orbs = 0;
let speed = 13;
let gravity = 28;
let velocityY = 0;
let grounded = true;
let spawnTimer = 0;
let nextSpawn = 1.7;
let objects = [];
let elapsed = 0;

const scoreText = document.getElementById("score");
const orbText = document.getElementById("orbs");
const speedText = document.getElementById("speed");
const overlay = document.getElementById("overlay");

// ---------------- OBSTACLE CREATION ----------------
function addSpike(x) {
  const geometry = new THREE.ConeGeometry(0.75, 1.6, 4);
  const spike = new THREE.Mesh(geometry, spikeMat);
  spike.rotation.y = Math.PI / 4;
  spike.position.set(x, 0.8, 0);
  spike.castShadow = true;
  scene.add(spike);

  objects.push({
    mesh: spike,
    type: "spike",
    x,
    collected: false
  });
}

function addBlock(x) {
  const block = new THREE.Mesh(
    new THREE.BoxGeometry(1.35, 1.7, 1.5),
    blockMat
  );
  block.position.set(x, 0.85, 0);
  block.castShadow = true;
  scene.add(block);

  // Glowing top
  const top = new THREE.Mesh(
    new THREE.BoxGeometry(1.4, 0.12, 1.55),
    mat(0xffe36e, 0xff8a00)
  );
  top.position.y = 0.91;
  block.add(top);

  objects.push({
    mesh: block,
    type: "block",
    x,
    collected: false
  });
}

function addOrb(x, y = 2) {
  const orb = new THREE.Mesh(
    new THREE.OctahedronGeometry(0.38),
    orbMat
  );
  orb.position.set(x, y, 0);
  scene.add(orb);

  objects.push({
    mesh: orb,
    type: "orb",
    x,
    baseY: y,
    collected: false
  });
}

function spawnPattern() {
  const choice = Math.random();

  if (choice < 0.42) {
    addSpike(22);
    if (Math.random() < 0.7) addOrb(22, 2.5);
  } else if (choice < 0.72) {
    addBlock(22);
    addOrb(22, 3);
  } else {
    addSpike(22);
    addSpike(23.4);
    addOrb(22.7, 3.4);
  }
}

// ---------------- JUMPING ----------------
function jump() {
  if (!playing || dead || !grounded) return;

  velocityY = 12;
  grounded = false;
}

document.addEventListener("keydown", (event) => {
  if (
    event.code === "Space" ||
    event.code === "ArrowUp" ||
    event.code === "KeyW"
  ) {
    event.preventDefault();
    if (!event.repeat) jump();
  }
});

renderer.domElement.addEventListener("pointerdown", jump);

const jumpButton = document.getElementById("jumpButton");
jumpButton.addEventListener("pointerdown", (e) => {
  e.preventDefault();
  jump();
});

// ---------------- GAME OVER ----------------
function gameOver() {
  if (dead) return;

  dead = true;
  playing = false;

  document.getElementById("heading").textContent = "GAME OVER!";
  document.getElementById("description").innerHTML =
    "⭐ Score: " + Math.floor(score) +
    "<br>💎 Orbs collected: " + orbs +
    "<br><br>Try again and beat your score!";

  document.getElementById("start").textContent = "RESTART";
  overlay.style.display = "flex";
}

// ---------------- START / RESTART ----------------
function startGame() {
  objects.forEach(obj => scene.remove(obj.mesh));
  objects = [];

  score = 0;
  orbs = 0;
  speed = 13;
  elapsed = 0;
  spawnTimer = 0;
  nextSpawn = 1.7;
  velocityY = 0;
  grounded = true;
  dead = false;
  playing = true;

  player.position.set(playerX, 0.65, 0);
  player.rotation.set(0, 0, 0);

  document.getElementById("heading").textContent = "CUBE RUSH 3D";
  document.getElementById("description").textContent =
    "Jump over spikes, dodge blocks and collect glowing orbs!";
  document.getElementById("start").textContent = "START GAME";

  overlay.style.display = "none";
  updateHUD();
}

document.getElementById("start").addEventListener("click", startGame);

function updateHUD() {
  scoreText.textContent = Math.floor(score);
  orbText.textContent = orbs;
  speedText.textContent = (speed / 13).toFixed(1) + "x";
}

// ---------------- MAIN LOOP ----------------
const clock = new THREE.Clock();

function animate() {
  requestAnimationFrame(animate);

  const dt = Math.min(clock.getDelta(), 0.04);

  if (playing) {
    elapsed += dt;

    // Increase difficulty gradually
    speed = Math.min(26, 13 + elapsed * 0.22);
    score += dt * speed * 2;

    // Jump physics
    velocityY -= gravity * dt;
    player.position.y += velocityY * dt;

    if (player.position.y <= 0.65) {
      player.position.y = 0.65;
      velocityY = 0;
      grounded = true;
    }

    // Rotate the cube while in the air
    if (!grounded) {
      cube.rotation.z -= dt * 5;
    } else {
      cube.rotation.z = Math.round(
        cube.rotation.z / (Math.PI / 2)
      ) * (Math.PI / 2);
    }

    // Spawn obstacles
    spawnTimer += dt;
    if (spawnTimer >= nextSpawn) {
      spawnTimer = 0;
      nextSpawn = Math.max(1.05, 1.8 - elapsed * 0.006);
      spawnPattern();
    }

    // Move obstacles and collect orbs
    for (let i = objects.length - 1; i >= 0; i--) {
      const obj = objects[i];
      obj.x -= speed * dt;
      obj.mesh.position.x = obj.x;

      if (obj.type === "orb") {
        obj.mesh.rotation.y += dt * 3;
        obj.mesh.rotation.x += dt * 2;
        obj.mesh.position.y = obj.baseY + Math.sin(elapsed * 4 + i) * 0.15;

        const closeX = Math.abs(obj.x - playerX) < 1.05;
        const closeY = Math.abs(obj.mesh.position.y - player.position.y) < 1.6;

        if (closeX && closeY && !obj.collected) {
          obj.collected = true;
          orbs++;
          score += 50;
          scene.remove(obj.mesh);
          objects.splice(i, 1);
          continue;
        }
      } else {
        const closeX = Math.abs(obj.x - playerX) < 0.95;
        const playerBottom = player.position.y - 0.625;

        if (closeX && playerBottom < 1.45) {
          gameOver();
        }
      }

      // Remove things behind the player
      if (obj.x < -13) {
        scene.remove(obj.mesh);
        objects.splice(i, 1);
      }
    }

    // Move background decorations
    for (const pillar of decorations) {
      pillar.position.x -= speed * dt * 0.35;
      if (pillar.position.x < -25) {
        pillar.position.x += 87.5;
      }
    }

    updateHUD();
  }

  // Camera movement
  camera.position.y = 7 + (player.position.y - 0.65) * 0.15;
  camera.lookAt(0, 2, 0);

  renderer.render(scene, camera);
}

animate();

// ---------------- RESIZE ----------------
window.addEventListener("resize", () => {
  camera.aspect = innerWidth / innerHeight;
  camera.updateProjectionMatrix();
  renderer.setSize(innerWidth, innerHeight);
});
</script>
</body>
</html>

Game Source: Cube Rush 3D

Creator: AtomicBolt46

Libraries: three

Complexity: complex (514 lines, 11.3 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: cube-rush-3d-atomicbolt46" to link back to the original. Then publish at arcadelab.ai/publish.