🎮ArcadeLab

Supercar X3D

by WildOwl74
257 lines9.0 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">
<title>Supercar X3D</title>
<style>
*{box-sizing:border-box}
body{margin:0;overflow:hidden;background:#07111e;color:white;font-family:Arial,sans-serif}
#ui{position:fixed;inset:0;pointer-events:none}
.panel{pointer-events:auto;background:#071522df;border:1px solid #48d9ff66;
border-radius:14px;padding:12px;backdrop-filter:blur(8px)}
#top{position:absolute;top:10px;left:10px;right:10px;display:flex;
justify-content:space-between;align-items:flex-start;gap:8px}
h2{margin:0 0 6px;color:#52e1ff;font-size:19px}
small{color:#b4d8e8}
select,button,input{accent-color:#36dfff}
select,button{padding:9px;border-radius:8px;border:1px solid #3b8fa9;
background:#10293a;color:white}
button{cursor:pointer}
#tune{position:absolute;right:10px;top:120px;width:190px}
#tune label{display:block;font-size:12px;margin-top:12px}
#tune input{width:100%}
#bottom{position:absolute;bottom:15px;left:0;right:0;display:flex;
justify-content:space-between;align-items:end;padding:0 15px}
.controls{display:flex;gap:9px;align-items:end}
.drive{width:65px;height:65px;font-size:23px;background:#102b40dd}
#gas{height:76px;width:76px;background:#087b91;font-weight:bold}
#stats{font-size:14px;line-height:1.7}
@media(max-width:500px){
 #tune{top:126px;width:150px;padding:9px}
 #top .panel{padding:9px}
 h2{font-size:16px}
 .drive{width:54px;height:58px}
 #gas{width:66px;height:68px}
 #stats{font-size:12px}
}
</style>
</head>
<body>
<div id="ui">
 <div id="top">
  <div class="panel">
   <h2>🏎️ SUPERCAR X3D</h2>
   <select id="car">
    <option value="0">V12 Hypercar</option>
    <option value="1">Red Supercar</option>
    <option value="2">Electric GT</option>
    <option value="3">Track Racer</option>
   </select>
   <button id="start">START ENGINE</button>
  </div>
  <div class="panel" id="stats">
   SPEED: <b id="speed">0</b> km/h<br>
   DISTANCE: <b id="dist">0</b> m<br>
   GEAR: <b id="gear">N</b>
  </div>
 </div>
 <div id="tune" class="panel">
  <b>🔧 ENGINE TUNING</b>
  <label>Engine RPM <span id="rpmv">45%</span></label>
  <input id="rpm" type="range" min="10" max="100" value="45">
  <label>Sound Volume <span id="volv">40%</span></label>
  <input id="vol" type="range" min="0" max="100" value="40">
  <label>Engine Power <span id="powv">60%</span></label>
  <input id="power" type="range" min="20" max="100" value="60">
  <button id="sound">🔊 Enable Sound</button>
  <button id="reset">↻ Reset</button>
  <small>RPM, volume aur power badlo ya ghatao.</small>
 </div>
 <div id="bottom">
  <div class="controls">
   <button class="drive" id="left">◀</button>
   <button class="drive" id="right">▶</button>
  </div>
  <div class="controls">
   <button class="drive" id="brake">▼</button>
   <button class="drive" id="gas">GAS ▲</button>
  </div>
 </div>
</div>
<script>
let scene,camera,renderer,carBody,wheels=[];
let speed=0,distance=0,steer=0,started=false;
let audio,osc,filter,gain;
let held={};
const $=id=>document.getElementById(id);
const colors=[0x00bfff,0xff2525,0x27efb1,0xffa322];

function mat(color,roughness=.35,metalness=.25){
 return new THREE.MeshStandardMaterial({color,roughness,metalness});
}
function cube(parent,x,y,z,sx,sy,sz,material){
 let m=new THREE.Mesh(new THREE.BoxGeometry(sx,sy,sz),material);
 m.position.set(x,y,z);parent.add(m);return m;
}
function makeCar(){
 if(carBody)scene.remove(carBody);
 carBody=new THREE.Group();wheels=[];
 let color=mat(colors[+$('car').value],.24,.65);
 let black=mat(0x11151b,.35,.35),glass=mat(0x183c50,.15,.55);
 cube(carBody,0,.72,0,1.8,.48,3.65,color);
 cube(carBody,0,1.05,-.35,1.5,.55,1.8,color);
 cube(carBody,0,1.36,-.42,1.15,.43,1.0,glass);
 cube(carBody,0,.98,1.02,1.52,.24,.68,color);
 cube(carBody,-.56,.74,-1.52,.35,.13,.08,mat(0xff2020));
 cube(carBody,.56,.74,-1.52,.35,.13,.08,mat(0xff2020));
 cube(carBody,0,.9,-1.9,1.75,.13,.22,black);
 for(let x of [-.96,.96])for(let z of [-1.15,1.15]){
  let w=new THREE.Mesh(new THREE.CylinderGeometry(.34,.34,.22,20),
    black);
  w.rotation.z=Math.PI/2;w.position.set(x,.42,z);
  carBody.add(w);wheels.push(w);
  let hub=new THREE.Mesh(new THREE.CylinderGeometry(.15,.15,.23,12),
    mat(0x9daab5,.25,.8));
  hub.rotation.z=Math.PI/2;hub.position.set(x,.42,z);
  carBody.add(hub);
 }
 scene.add(carBody);carBody.position.set(0,0,2);
}
function init(){
 scene=new THREE.Scene();
 scene.background=new THREE.Color(0x80c8ed);
 scene.fog=new THREE.Fog(0x80c8ed,35,180);
 camera=new THREE.PerspectiveCamera(62,innerWidth/innerHeight,.1,400);
 camera.position.set(0,5,10);
 renderer=new THREE.WebGLRenderer({antialias:true});
 renderer.setSize(innerWidth,innerHeight);
 renderer.setPixelRatio(Math.min(devicePixelRatio,1.7));
 renderer.shadowMap.enabled=true;
 renderer.shadowMap.type=THREE.PCFSoftShadowMap;
 document.body.appendChild(renderer.domElement);
 scene.add(new THREE.HemisphereLight(0xc5eaff,0x38452b,2));
 let sun=new THREE.DirectionalLight(0xffffff,3);
 sun.position.set(-8,15,7);sun.castShadow=true;scene.add(sun);
 let ground=new THREE.Mesh(new THREE.PlaneGeometry(300,400),
 mat(0x43804b,1));
 ground.rotation.x=-Math.PI/2;ground.position.z=-120;
 ground.receiveShadow=true;scene.add(ground);
 cube(scene,-6,.02,-120,.25,.04,400,mat(0xffffff));
 cube(scene,6,.02,-120,.25,.04,400,mat(0xffffff));
 for(let z=15;z>-240;z-=8){
  cube(scene,0,.025,z, .12,.04,3.5,mat(0xffe88a));
 }
 // Trees and roadside scenery
 for(let z=10;z>-230;z-=13){
  for(let side of [-1,1]){
   let x=side*(9+Math.random()*7);
   let trunk=new THREE.Mesh(new THREE.CylinderGeometry(.15,.22,1.8,7),
    mat(0x67452d));
   trunk.position.set(x,.9,z);scene.add(trunk);
   let leaves=new THREE.Mesh(new THREE.ConeGeometry(1.25,3.6,7),
    mat(0x12613c));
   leaves.position.set(x,3,z);scene.add(leaves);
  }
 }
 makeCar();
}
function initSound(){
 if(audio)return;
 audio=new (window.AudioContext||window.webkitAudioContext)();
 osc=audio.createOscillator();filter=audio.createBiquadFilter();
 gain=audio.createGain();
 osc.type='sawtooth';filter.type='lowpass';
 osc.connect(filter);filter.connect(gain);gain.connect(audio.destination);
 osc.start();updateSound();
}
function updateSound(){
 if(!audio)return;
 if(audio.state==='suspended')audio.resume();
 let rpm=+$('rpm').value/100;
 let throttle=held.gas?1:0;
 osc.frequency.setTargetAtTime(
  45+rpm*150+speed*1.8+throttle*35,audio.currentTime,.06);
 filter.frequency.setTargetAtTime(180+rpm*1800,audio.currentTime,.08);
 gain.gain.setTargetAtTime(+$('vol').value/100*.11,
  audio.currentTime,.05);
 $('rpmv').textContent=$('rpm').value+'%';
 $('volv').textContent=$('vol').value+'%';
 $('powv').textContent=$('power').value+'%';
}
function bindHold(id,key){
 let el=$(id);
 el.addEventListener('pointerdown',e=>{
  e.preventDefault();held[key]=true;
  if(el.setPointerCapture)el.setPointerCapture(e.pointerId);
 });
 for(let ev of ['pointerup','pointercancel','lostpointercapture'])
  el.addEventListener(ev,()=>held[key]=false);
}
bindHold('gas','gas');bindHold('brake','brake');
bindHold('left','left');bindHold('right','right');
addEventListener('keydown',e=>{
 held[({ArrowUp:'gas',w:'gas',ArrowDown:'brake',s:'brake',
 ArrowLeft:'left',a:'left',ArrowRight:'right',d:'right'})[e.key]||e.key]=true;
 if(['ArrowUp','ArrowDown','ArrowLeft','ArrowRight',' '].includes(e.key))
 e.preventDefault();
});
addEventListener('keyup',e=>{
 let k=({ArrowUp:'gas',w:'gas',ArrowDown:'brake',s:'brake',
 ArrowLeft:'left',a:'left',ArrowRight:'right',d:'right'})[e.key]||e.key;
 held[k]=false;
});
$('start').onclick=()=>{
 started=true;initSound();$('start').textContent='ENGINE ON';
};
$('sound').onclick=()=>{
 initSound();$('sound').textContent='🔊 Sound Enabled';
};
$('car').onchange=makeCar;
['rpm','vol','power'].forEach(id=>$(id).oninput=updateSound);
$('reset').onclick=()=>{
 speed=0;distance=0;steer=0;
 carBody.position.x=0;
};
addEventListener('resize',()=>{
 camera.aspect=innerWidth/innerHeight;
 camera.updateProjectionMatrix();renderer.setSize(innerWidth,innerHeight);
});
init();
let last=performance.now();
function loop(now){
 requestAnimationFrame(loop);
 let dt=Math.min((now-last)/1000,.04);last=now;
 if(started){
  let power=+$('power').value/100;
  if(held.gas)speed+=42*power*dt;
  else speed-=8*dt;
  if(held.brake)speed-=65*dt;
  speed=Math.max(0,Math.min(260,speed));
  if(held.left)steer+=2.0*dt;
  if(held.right)steer-=2.0*dt;
  if(!held.left&&!held.right)steer*=Math.max(0,1-3*dt);
  steer=Math.max(-1,Math.min(1,steer));
  carBody.position.x=Math.max(-4,Math.min(4,
    carBody.position.x+steer*speed*.012*dt));
  carBody.rotation.y=-steer*.12;
  wheels.forEach(w=>w.rotation.x+=speed*dt*.08);
  distance+=speed*dt/3.6;
  $('speed').textContent=Math.round(speed);
  $('dist').textContent=Math.floor(distance);
  $('gear').textContent=speed<1?'N':Math.min(7,Math.ceil(speed/35));
  updateSound();
 }
 // Follow camera
 let target=new THREE.Vector3(carBody.position.x*.55,3.6,8.7);
 camera.position.lerp(target,.07);
 camera.lookAt(carBody.position.x*.35,0,-3);
 // Move road scenery illusion using the ground markings
 renderer.render(scene,camera);
}
requestAnimationFrame(loop);
</script>
</body>
</html>

Game Source: Supercar X3D

Creator: WildOwl74

Libraries: three

Complexity: complex (257 lines, 9.0 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: supercar-x3d-wildowl74" to link back to the original. Then publish at arcadelab.ai/publish.