🎮ArcadeLab

Neon Rush 3D

by PhantomCobra95
270 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,maximum-scale=1,user-scalable=no">
<title>Neon Rush 3D</title>
<style>
*{box-sizing:border-box}
body{margin:0;overflow:hidden;background:#06101e;font-family:Arial,sans-serif;touch-action:none;color:white}
canvas{display:block}
#hud{position:fixed;inset:0;pointer-events:none}
.top{display:flex;justify-content:space-between;padding:15px;gap:10px}
.box{background:#071525cc;border:1px solid #43dfff88;border-radius:14px;padding:10px 14px;box-shadow:0 0 18px #00bfff33}
.label{font-size:10px;letter-spacing:2px;color:#62eaff}
.num{font-size:23px;font-weight:900}
#brand{position:absolute;top:105px;left:50%;transform:translateX(-50%);font-size:12px;letter-spacing:4px;color:#a3efff;white-space:nowrap;text-shadow:0 0 12px #00bfff}
.controls{position:absolute;bottom:max(20px,env(safe-area-inset-bottom));left:0;right:0;display:flex;justify-content:space-between;padding:0 20px;pointer-events:auto}
.ctrl{width:76px;height:76px;border-radius:23px;border:2px solid #8defff;background:linear-gradient(145deg,#15476be8,#061526ed);color:white;font-size:30px;font-weight:bold;box-shadow:0 0 20px #00cfff55;touch-action:none}
.ctrl:active{background:#087fa6}
#screen{position:fixed;inset:0;display:flex;align-items:center;justify-content:center;padding:22px;background:#030b17aa}
.card{width:min(420px,100%);text-align:center;padding:27px 22px;border:1px solid #4be4ff99;border-radius:25px;background:linear-gradient(145deg,#102e4bf5,#050c19f7);box-shadow:0 0 45px #00c8ff33}
.kicker{font-size:12px;letter-spacing:5px;color:#5ce8ff;font-weight:bold}
h1{font-size:clamp(36px,10vw,58px);line-height:.94;font-style:italic;margin:15px 0;text-shadow:0 0 25px #00bfff99}
p{font-size:14px;line-height:1.5;color:#c6dfed}
#start{width:100%;border:0;border-radius:14px;padding:16px;background:linear-gradient(90deg,#52f0ff,#478dff);color:#04101b;font-size:17px;font-weight:900;margin-top:10px}
.tip{font-size:11px;color:#8daabd;margin-top:14px}
.hide{display:none!important}
</style>
</head>
<body>
<div id="hud">
 <div class="top">
  <div class="box"><div class="label">DISTANCE</div><div class="num"><span id="score">0</span> <small>m</small></div></div>
  <div class="box" style="text-align:right"><div class="label">SPEED</div><div class="num"><span id="speed">120</span> <small>km/h</small></div></div>
 </div>
 <div id="brand">NEON RUSH • HIGHWAY SERIES</div>
 <div class="controls">
  <button class="ctrl" id="left">◀</button>
  <button class="ctrl" id="right">▶</button>
 </div>
</div>
<div id="screen">
 <div class="card">
  <div class="kicker">ULTIMATE STREET RACING</div>
  <h1>NEON<br>RUSH 3D</h1>
  <p id="msg">Take the wheel. Dodge traffic. Race through a glowing futuristic highway and beat your distance record!</p>
  <button id="start">START ENGINE</button>
  <div class="tip">TOUCH CONTROLS • 3D GRAPHICS • ENDLESS RACE</div>
 </div>
</div>
<script>
(function(){
'use strict';
if(!window.THREE){
 document.getElementById('msg').textContent='3D engine could not load. Please reopen this game with an internet connection and try again.';
 return;
}
const T=THREE;
const scene=new T.Scene();
scene.background=new T.Color(0x86c9f0);
scene.fog=new T.Fog(0x86c9f0,100,360);
const camera=new T.PerspectiveCamera(65,innerWidth/innerHeight,.1,600);
camera.position.set(0,5.1,10.5);
camera.lookAt(0,1,-9);
const renderer=new T.WebGLRenderer({antialias:true, powerPreference:'high-performance'});
renderer.setPixelRatio(Math.min(devicePixelRatio,1.7));
renderer.setSize(innerWidth,innerHeight);
renderer.shadowMap.enabled=true;
renderer.shadowMap.type=T.PCFSoftShadowMap;
renderer.outputColorSpace=T.SRGBColorSpace;
renderer.toneMapping=T.ACESFilmicToneMapping;
renderer.toneMappingExposure=1.12;
document.body.appendChild(renderer.domElement);

scene.add(new T.HemisphereLight(0xbbe8ff,0x31402d,2.0));
const sun=new T.DirectionalLight(0xffe4c0,3.0);
sun.position.set(-35,55,-30);sun.castShadow=true;
sun.shadow.mapSize.set(1024,1024);
scene.add(sun);

const mat=(color,roughness=.7,metalness=0)=>new T.MeshStandardMaterial({color,roughness,metalness});
const roadMat=mat(0x242b38,.88), grassMat=mat(0x286443), white=mat(0xffffff);
const neon=new T.MeshStandardMaterial({color:0x16dcff,emissive:0x00aaff,emissiveIntensity:2});
const pink=new T.MeshStandardMaterial({color:0xff398b,emissive:0xff0870,emissiveIntensity:1.5});

function block(parent,geo,material,x,y,z,cast=true){
 const m=new T.Mesh(geo,material);m.position.set(x,y,z);
 m.castShadow=cast;m.receiveShadow=true;parent.add(m);return m;
}
block(scene,new T.PlaneGeometry(800,800),grassMat,0,-.18,-100,false).rotation.x=-Math.PI/2;

// Distant mountain range
for(let i=0;i<28;i++){
 const h=9+Math.random()*24,w=12+Math.random()*23;
 const m=block(scene,new T.ConeGeometry(w,h,5),mat(i%2?0x567d98:0x3f6788),-180+i*14,h/2-1,-145-Math.random()*35,false);
 m.rotation.y=Math.random()*1.5;
}

// Road and shoulders
block(scene,new T.BoxGeometry(13,.18,650),roadMat,0,-.03,-260,false);
block(scene,new T.BoxGeometry(.45,.22,650),mat(0xc7dce6),-6.7,.02,-260,false);
block(scene,new T.BoxGeometry(.45,.22,650),mat(0xc7dce6),6.7,.02,-260,false);
block(scene,new T.BoxGeometry(.09,.03,650),neon,-6.92,.12,-260,false);
block(scene,new T.BoxGeometry(.09,.03,650),pink,6.92,.12,-260,false);

// Dashed road lines
const dashes=[];
for(let i=0;i<54;i++){
 for(const x of [-2.17,2.17]){
  const d=block(scene,new T.BoxGeometry(.095,.035,3.3),white,x,.075,-i*12-2,false);
  dashes.push(d);
 }
}

// City skyline
for(let i=0;i<45;i++){
 const side=i%2?1:-1;
 const z=-25-Math.random()*270;
 const x=side*(11+Math.random()*35);
 const h=5+Math.random()*24,w=2+Math.random()*5;
 const b=block(scene,new T.BoxGeometry(w,h, w+Math.random()*3),
 mat([0x233f59,0x344b67,0x283b54][i%3]),x,h/2-.1,z,false);
 b.material= b.material.clone();
 for(let y=1.8;y<h-1;y+=2.4){
  for(let xx=-w/2+.55;xx<w/2;xx+=1.2){
   const win=block(scene,new T.BoxGeometry(.28,.38,.035),
   mat(Math.random()>.5?0x64dcff:0xffc76b),x+xx,y,z+w/2+.025,false);
  }
 }
}

// Roadside lamps and glowing markers
const lamps=[];
for(let i=0;i<30;i++){
 for(const side of [-1,1]){
  const x=side*8.2,z=-i*18-8;
  const pole=block(scene,new T.CylinderGeometry(.07,.11,3.5,7),mat(0x9eb9c9),x,1.75,z,false);
  block(scene,new T.BoxGeometry(.12,.12,1.8),mat(0x7b9aaa),x,3.35,z-side*.65,false);
  const bulb=block(scene,new T.BoxGeometry(.25,.13,.48),neon,x,3.28,z-side*1.35,false);
  lamps.push({pole,bulb,side,z});
 }
}

// Create detailed low-poly sports cars
function makeCar(color,player){
 const g=new T.Group();
 const bodyMat=new T.MeshPhysicalMaterial({color,metalness:.48,roughness:.24,clearcoat:.9,clearcoatRoughness:.2});
 block(g,new T.BoxGeometry(1.48,.42,2.75),bodyMat,0,.63,0);
 block(g,new T.BoxGeometry(1.25,.18,1.15),bodyMat,0,.91,-.18);
 const glass=new T.MeshPhysicalMaterial({color:0x122b43,metalness:.45,roughness:.15,clearcoat:1});
 block(g,new T.BoxGeometry(1.08,.38,.88),glass,0,1.08,-.19);
 // Hood stripe
 if(player){
  block(g,new T.BoxGeometry(.12,.018,.78),neon,0,.855,.79,false);
 }
 const tireMat=mat(0x11151c,.9);
 const wheelGeo=new T.CylinderGeometry(.27,.27,.18,12);
 for(const x of [-.77,.77]) for(const z of [-.84,.86]){
  const w=block(g,wheelGeo,tireMat,x,.42,z);
  w.rotation.z=Math.PI/2;
  block(g,new T.CylinderGeometry(.13,.13,.19,10),mat(0x9db8c6,.3,.8),x,.42,z).rotation.z=Math.PI/2;
 }
 const lightMat=new T.MeshStandardMaterial({color:player?0x6ff7ff:0xfff0be,emissive:player?0x00dfff:0xffd16a,emissiveIntensity:2});
 for(const x of [-.48,.48]){
  block(g,new T.BoxGeometry(.32,.12,.05),lightMat,x,.69,-1.39,false);
  block(g,new T.BoxGeometry(.3,.1,.05),mat(0xff244f),x,.69,1.39,false);
 }
 if(player){
  const glow=new T.PointLight(0x00cfff,2.2,8);glow.position.set(0,1.1,0);g.add(glow);
 }
 scene.add(g);return g;
}
const player=makeCar(0x07aeea,true);
player.position.set(0,0,4.3);
const traffic=[];
const colors=[0xff365e,0xffa52e,0x9a6cff,0x43d69b,0xf0f4fa,0xf14fd2];
function spawnCar(){
 const lane=Math.floor(Math.random()*3)-1;
 const c=makeCar(colors[Math.floor(Math.random()*colors.length)],false);
 c.scale.setScalar(.9+Math.random()*.12);
 c.position.set(lane*3.1,0,-105-Math.random()*25);
 traffic.push({mesh:c,lane});
}
let running=false,score=0,speed=120,last=0,spawn=0,lane=0,targetLane=0,steer=0;
const scoreEl=document.getElementById('score'),speedEl=document.getElementById('speed');
const screen=document.getElementById('screen'),msg=document.getElementById('msg'),start=document.getElementById('start');
function startGame(){
 traffic.forEach(c=>scene.remove(c.mesh));traffic.length=0;
 score=0;speed=120;spawn=0;lane=0;targetLane=0;steer=0;
 player.position.x=0;player.rotation.z=0;
 running=true;screen.classList.add('hide');last=performance.now();
 for(let i=0;i<5;i++)spawnCar();
 requestAnimationFrame(loop);
}
function finish(){
 running=false;
 msg.textContent='CRASH! You travelled '+Math.floor(score)+' metres. Can you beat your record?';
 start.textContent='RACE AGAIN';screen.classList.remove('hide');
}
function move(d){if(!running)return;targetLane=Math.max(-1,Math.min(1,targetLane+d));}
document.getElementById('left').addEventListener('pointerdown',e=>{e.preventDefault();move(-1)});
document.getElementById('right').addEventListener('pointerdown',e=>{e.preventDefault();move(1)});
let touchX=null;
renderer.domElement.addEventListener('pointerdown',e=>touchX=e.clientX);
renderer.domElement.addEventListener('pointerup',e=>{
 if(touchX===null)return;
 const dx=e.clientX-touchX;if(Math.abs(dx)>35)move(dx<0?-1:1);
 touchX=null;
});
window.addEventListener('keydown',e=>{
 if(e.key==='ArrowLeft'||e.key==='a')move(-1);
 if(e.key==='ArrowRight'||e.key==='d')move(1);
});
start.addEventListener('click',startGame);

function loop(now){
 if(!running)return;
 const dt=Math.min((now-last)/1000,.035);last=now;
 score+=speed*dt/3.6;
 speed=Math.min(290,120+score*.22);
 const travel=speed*dt*.23;
 // Move road markings towards the player
 dashes.forEach(d=>{d.position.z+=travel*7;if(d.position.z>8)d.position.z-=648});
 lamps.forEach(o=>{
  o.pole.position.z+=travel*7;o.bulb.position.z+=travel*7;
  if(o.pole.position.z>10){o.pole.position.z-=540;o.bulb.position.z-=540;}
 });
 // Lane change smoothing
 const desired=targetLane*3.1;
 player.position.x+=(desired-player.position.x)*Math.min(1,dt*9);
 player.rotation.z+=( -(desired-player.position.x)*.045-player.rotation.z)*Math.min(1,dt*7);
 player.position.y=Math.abs(Math.sin(now*.009))*.012;
 // Traffic movement and collision
 spawn+=dt;
 if(spawn>Math.max(.58,1.35-score/1600)){spawn=0;spawnCar();}
 for(let i=traffic.length-1;i>=0;i--){
  const c=traffic[i];
  c.mesh.position.z+=travel*7;
  c.mesh.rotation.y=Math.sin(now*.001+i)*.012;
  if(Math.abs(c.mesh.position.z-player.position.z)<2.05 &&
     Math.abs(c.mesh.position.x-player.position.x)<1.55){finish();return;}
  if(c.mesh.position.z>12){scene.remove(c.mesh);traffic.splice(i,1);}
 }
 // Camera follow and subtle movement
 camera.position.x+=(player.position.x*.24-camera.position.x)*dt*2.2;
 camera.position.y=5.0+Math.sin(now*.0015)*.035;
 camera.lookAt(player.position.x*.12,1,-12);
 scoreEl.textContent=Math.floor(score);
 speedEl.textContent=Math.floor(speed);
 renderer.render(scene,camera);
 requestAnimationFrame(loop);
}
function idle(now){
 if(running)return;
 player.rotation.y=Math.sin(now*.0008)*.025;
 renderer.render(scene,camera);
 requestAnimationFrame(idle);
}
window.addEventListener('resize',()=>{
 camera.aspect=innerWidth/innerHeight;camera.updateProjectionMatrix();
 renderer.setSize(innerWidth,innerHeight);
 renderer.setPixelRatio(Math.min(devicePixelRatio,1.7));
});
requestAnimationFrame(idle);
})();
</script>
</body>
</html>

Game Source: Neon Rush 3D

Creator: PhantomCobra95

Libraries: three

Complexity: complex (270 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: neon-rush-3d-phantomcobra95" to link back to the original. Then publish at arcadelab.ai/publish.