🎮ArcadeLab

CAR RACER THUNDER

by BoldRider35
435 lines14.1 KB
▶ Play
ArcadeLab
<!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>CAR RACER THUNDER</title>
<style>
*{box-sizing:border-box}
body{margin:0;background:#050b18;color:white;font-family:Arial,sans-serif;overflow:hidden;touch-action:none}
#game{display:block;width:100vw;height:100dvh}
#hud{position:fixed;inset:0;pointer-events:none}
.top{display:flex;justify-content:space-between;padding:12px;gap:6px}
.panel{background:#07162bd9;border:1px solid #42d9ff;border-radius:12px;padding:8px 12px;text-align:center;min-width:72px;box-shadow:0 0 12px #00aaff55}
.small{font-size:10px;color:#8feaff;letter-spacing:1px}
.value{font-size:19px;font-weight:900;margin-top:4px}
#bar{height:7px;background:#17243a;border-radius:8px;overflow:hidden;margin-top:5px}
#nitroFill{height:100%;width:100%;background:linear-gradient(90deg,#00eaff,#b6ffff)}
#controls{position:fixed;bottom:20px;left:0;right:0;display:flex;justify-content:space-between;align-items:end;padding:0 18px;pointer-events:none}
.group{display:flex;gap:12px;align-items:end}
.ctrl{pointer-events:auto;width:66px;height:66px;border-radius:20px;border:2px solid #6deaff;background:#07182dcc;color:white;font-size:27px;font-weight:bold;box-shadow:0 0 15px #00aaff44;touch-action:none}
.ctrl:active{background:#087b9d}
#nitro{width:76px;height:58px;font-size:13px;color:#9fffff}
#overlay{position:fixed;inset:0;display:flex;align-items:center;justify-content:center;background:#020817d9;padding:20px}
.card{width:min(390px,100%);background:linear-gradient(145deg,#11294a,#07101f);border:2px solid #36dfff;border-radius:24px;padding:25px;text-align:center;box-shadow:0 0 40px #00aaff44}
h1{font-size:32px;margin:5px 0;color:#8cf4ff;text-shadow:0 0 18px #00cfff}
button{cursor:pointer}
.mainbtn{width:100%;padding:15px;margin-top:12px;border:0;border-radius:12px;background:linear-gradient(90deg,#00b8ff,#00f0bb);font-size:17px;font-weight:900;color:#041225}
#message{font-size:14px;color:#c8eaff;line-height:1.6}
#pause{position:fixed;right:12px;top:95px;pointer-events:auto;border:1px solid #64eaff;background:#07182dcc;color:white;border-radius:10px;padding:10px 14px}
@media(max-height:500px){.ctrl{width:53px;height:53px}.panel{padding:5px 8px}.value{font-size:15px}#controls{bottom:8px}.card{padding:14px}}
</style>
</head>
<body>
<canvas id="game"></canvas>

<div id="hud">
 <div class="top">
  <div class="panel"><div class="small">SCORE</div><div class="value" id="score">0</div></div>
  <div class="panel"><div class="small">DISTANCE</div><div class="value" id="distance">0 m</div></div>
  <div class="panel"><div class="small">COINS</div><div class="value" id="coins">0</div></div>
  <div class="panel"><div class="small">LEVEL</div><div class="value" id="level">1</div></div>
 </div>
 <button id="pause">Ⅱ PAUSE</button>
 <div style="position:absolute;top:91px;left:12px;width:120px" class="panel">
  <div class="small">NITRO BOOST</div>
  <div id="bar"><div id="nitroFill"></div></div>
 </div>
</div>

<div id="controls">
 <div class="group">
  <button class="ctrl" id="left">◀</button>
  <button class="ctrl" id="right">▶</button>
 </div>
 <div class="group">
  <button class="ctrl" id="brake">▼</button>
  <button class="ctrl" id="nitro">⚡ NITRO</button>
 </div>
</div>

<div id="overlay">
 <div class="card">
  <div style="font-size:45px">🏎️</div>
  <h1>CAR RACER<br>THUNDER</h1>
  <p id="message">Race through traffic!<br>Collect coins, use nitro and beat your record.</p>
  <div style="font-size:13px;color:#8feaff">MOBILE RACING • TRAFFIC • LEVELS</div>
  <button class="mainbtn" id="start">START RACING</button>
  <button class="mainbtn" id="garage" style="background:linear-gradient(90deg,#764dff,#e04fff)">CHANGE CAR</button>
 </div>
</div>

<script>
(() => {
"use strict";

const canvas=document.getElementById("game");
const ctx=canvas.getContext("2d");
const $=id=>document.getElementById(id);

let W=innerWidth,H=innerHeight,DPR=1;
function resize(){
 W=innerWidth;H=innerHeight;
 DPR=Math.min(devicePixelRatio||1,2);
 canvas.width=Math.floor(W*DPR);
 canvas.height=Math.floor(H*DPR);
 canvas.style.width=W+"px";
 canvas.style.height=H+"px";
 ctx.setTransform(DPR,0,0,DPR,0,0);
}
addEventListener("resize",resize);resize();

let state="menu";
let score=0,coins=0,distance=0,level=1;
let speed=0.48,nitro=100;
let roadOffset=0,worldTime=0;
let traffic=[],pickups=[],particles=[];
let carChoice=0;
const carColors=["#00eaff","#ff315c","#ffd447","#9d72ff","#48ff82"];
let car={x:0,lean:0};
let input={left:false,right:false,brake:false,nitro:false};
let best=0;
try{best=Number(localStorage.getItem("crtBest")||0)}catch(e){}

function clamp(v,a,b){return Math.max(a,Math.min(b,v))}
function rand(a,b){return a+Math.random()*(b-a)}

function roadWidth(y){
 const t=clamp((y-H*.17)/(H*.86),0,1);
 return W*(.12+t*.43);
}
function roadCenter(y){
 return W/2+Math.sin(worldTime*.00025)*W*.025*
 clamp((y-H*.17)/(H*.86),0,1);
}
function roadX(lane,y){
 return roadCenter(y)+lane*roadWidth(y)*.31;
}
function projectY(z){
 return H*.18+Math.pow(clamp(z,0,1),1.7)*H*.84;
}

function polygon(points,color){
 ctx.fillStyle=color;ctx.beginPath();
 ctx.moveTo(points[0][0],points[0][1]);
 for(let i=1;i<points.length;i++)ctx.lineTo(points[i][0],points[i][1]);
 ctx.closePath();ctx.fill();
}

function drawBackground(){
 let sky=ctx.createLinearGradient(0,0,0,H*.65);
 sky.addColorStop(0,"#06132e");
 sky.addColorStop(.55,"#17668b");
 sky.addColorStop(1,"#f4a26a");
 ctx.fillStyle=sky;ctx.fillRect(0,0,W,H);

 let sun=ctx.createRadialGradient(W*.72,H*.22,2,W*.72,H*.22,H*.16);
 sun.addColorStop(0,"#fff4bd");sun.addColorStop(1,"#ffb64b00");
 ctx.fillStyle=sun;ctx.fillRect(0,0,W,H*.5);

 // distant mountains
 polygon([[0,H*.43],[W*.12,H*.24],[W*.24,H*.42],
 [W*.39,H*.27],[W*.53,H*.43],[W*.73,H*.25],
 [W*.88,H*.4],[W,H*.29],[W,H*.57],[0,H*.57]],"#344a68");

 // roadside trees and buildings
 for(let i=0;i<18;i++){
  let z=((i/18+roadOffset*.23)%1);
  let y=projectY(z);
  let spread=roadWidth(y)*.72+W*.04;
  for(const side of [-1,1]){
   let x=roadCenter(y)+side*spread;
   let size=8+z*35;
   ctx.fillStyle="#173d3d";
   ctx.fillRect(x-size*.09,y-size*.2,size*.18,size*.7);
   ctx.fillStyle=i%2?"#1c7959":"#286b4c";
   ctx.beginPath();ctx.arc(x,y-size*.3,size*.48,0,Math.PI*2);ctx.fill();
   ctx.beginPath();ctx.arc(x-size*.25,y-size*.1,size*.3,0,Math.PI*2);ctx.fill();
  }
 }

 // ground
 ctx.fillStyle="#267448";
 ctx.fillRect(0,H*.45,W,H*.55);
}

function drawRoad(){
 const top=H*.18,bottom=H*1.08;
 const ct=roadCenter(top),cb=roadCenter(bottom);
 const wt=roadWidth(top)/2,wb=roadWidth(bottom)/2;

 polygon([[ct-wt-W*.02,top],[ct+wt+W*.02,top],
 [cb+wb+W*.02,bottom],[cb-wb-W*.02,bottom]],"#e7e7dc");

 polygon([[ct-wt,top],[ct+wt,top],[cb+wb,bottom],[cb-wb,bottom]],"#343d4a");

 // road edge strips
 polygon([[ct-wt,top],[ct-wt+wt*.035,top],
 [cb-wb+wb*.035,bottom],[cb-wb,bottom]],"#ffcc42");
 polygon([[ct+wt-wt*.035,top],[ct+wt,top],
 [cb+wb,bottom],[cb+wb-wb*.035,bottom]],"#ffcc42");

 // moving lane markings
 for(let i=0;i<18;i++){
  let z=((i/18+roadOffset)%1);
  let y=projectY(z),y2=projectY(Math.min(1,z+.025));
  let rw=roadWidth(y),rw2=roadWidth(y2);
  for(let lane of [-1/3,1/3]){
   polygon([[roadCenter(y)+rw*lane-rw*.008,y],
   [roadCenter(y)+rw*lane+rw*.008,y],
   [roadCenter(y2)+rw2*lane+rw2*.008,y2],
   [roadCenter(y2)+rw2*lane-rw2*.008,y2]],"#f8f5df");
  }
 }
}

function drawCar(x,y,w,h,color,isPlayer=false){
 ctx.save();ctx.translate(x,y);
 // shadow
 ctx.fillStyle="#0008";
 ctx.beginPath();ctx.ellipse(0,h*.39,w*.6,h*.13,0,0,Math.PI*2);ctx.fill();

 // tires
 ctx.fillStyle="#10141b";
 for(const side of [-1,1]){
  ctx.fillRect(side*w*.39-w*.08,-h*.2,w*.16,h*.34);
  ctx.fillRect(side*w*.39-w*.08,h*.14,w*.16,h*.3);
 }
 // body
 polygon([[-w*.33,h*.4],[-w*.44,h*.1],[-w*.36,-h*.31],
 [-w*.2,-h*.45],[w*.2,-h*.45],[w*.36,-h*.31],
 [w*.44,h*.1],[w*.33,h*.4]],color);

 // windshield
 polygon([[-w*.27,-h*.19],[w*.27,-h*.19],
 [w*.19,-h*.38],[-w*.19,-h*.38]],"#a8efff");

 polygon([[-w*.29,-h*.13],[w*.29,-h*.13],
 [w*.34,h*.07],[-w*.34,h*.07]],"#172a42");

 // headlights and rear lights
 ctx.fillStyle=isPlayer?"#aaffff":"#ff344a";
 ctx.fillRect(-w*.32,h*.27,w*.17,h*.07);
 ctx.fillRect(w*.15,h*.27,w*.17,h*.07);
 ctx.fillStyle="#ffffff";
 ctx.fillRect(-w*.3,-h*.37,w*.12,h*.04);
 ctx.fillRect(w*.18,-h*.37,w*.12,h*.04);

 if(isPlayer){
  ctx.fillStyle="#ffffff";
  ctx.font=`bold ${Math.max(7,w*.17)}px Arial`;
  ctx.textAlign="center";ctx.fillText("THUNDER",0,h*.18);
 }
 ctx.restore();
}

function spawnTraffic(){
 let lane=Math.floor(rand(-1.5,2))*1;
 lane=clamp(lane,-1,1);
 if(Math.random()<.25)lane=rand(-1,1);
 traffic.push({
  lane,z:rand(.02,.12),
  color:["#e63b52","#f4b72e","#8b65f7","#f0f3f8","#30c8a1"][Math.floor(rand(0,5))],
  factor:rand(.35,.65),passed:false
 });
}

function spawnCoin(){
 pickups.push({lane:Math.round(rand(-1,1)),z:.03,spin:rand(0,6)});
}

function burst(x,y,color){
 for(let i=0;i<14;i++){
  particles.push({x,y,vx:rand(-3,3),vy:rand(-4,1),
  life:rand(15,32),color,size:rand(2,5)});
 }
}

function drawWorld(){
 drawBackground();
 drawRoad();

 for(const p of pickups){
  const y=projectY(p.z),x=roadX(p.lane,y);
  const r=5+p.z*15;
  ctx.save();ctx.translate(x,y);ctx.rotate(worldTime*.004+p.spin);
  ctx.fillStyle="#ffc928";ctx.strokeStyle="#fff2a0";ctx.lineWidth=3;
  ctx.beginPath();ctx.ellipse(0,0,r*.72,r,0,0,Math.PI*2);ctx.fill();ctx.stroke();
  ctx.fillStyle="#8b5700";ctx.font=`bold ${r}px Arial`;ctx.textAlign="center";
  ctx.fillText("$",0,r*.35);ctx.restore();
 }

 traffic.slice().sort((a,b)=>a.z-b.z).forEach(t=>{
  let y=projectY(t.z),x=roadX(t.lane,y);
  let w=roadWidth(y)*.18,h=w*1.55;
  drawCar(x,y,w,h,t.color,false);
 });

 let py=H*.81;
 let px=roadCenter(py)+car.x*roadWidth(py)*.31;
 let pw=roadWidth(py)*.19;
 if(input.nitro&&nitro>0&&state==="playing"){
  for(let i=0;i<3;i++){
   ctx.fillStyle=i===0?"#aaffff":"#00aaff";
   ctx.globalAlpha=rand(.3,.8);
   ctx.beginPath();ctx.moveTo(px-pw*.18,py+pw*.5);
   ctx.lineTo(px,py+pw*rand(1,2.3));
   ctx.lineTo(px+pw*.18,py+pw*.5);ctx.fill();
  }
  ctx.globalAlpha=1;
 }
 drawCar(px,py,pw,pw*1.55,carColors[carChoice],true);

 particles.forEach(p=>{
  ctx.globalAlpha=clamp(p.life/30,0,1);
  ctx.fillStyle=p.color;ctx.fillRect(p.x,p.y,p.size,p.size);
 });
 ctx.globalAlpha=1;
}

function update(dt){
 worldTime+=dt;
 if(state!=="playing")return;

 const boost=input.nitro&&nitro>0&&!input.brake;
 if(input.left)car.x-=dt*.0018*(1+speed);
 if(input.right)car.x+=dt*.0018*(1+speed);
 car.x=clamp(car.x,-1.05,1.05);

 if(boost){nitro=Math.max(0,nitro-dt*.025)}
 else nitro=Math.min(100,nitro+dt*.009);

 speed=boost?.95:input.brake?.25:.48+Math.min(level*.012,.22);
 distance+=speed*dt*.035;
 score+=speed*dt*.055;
 roadOffset=(roadOffset+speed*dt*.0006)%1;
 level=1+Math.floor(distance/450);
 $("nitroFill").style.width=nitro+"%";

 if(Math.random()<dt*.0009*(1+level*.12))spawnTraffic();
 if(Math.random()<dt*.0007)spawnCoin();

 for(const t of traffic){
  t.z+=dt*.00016*(.5+speed+t.factor*.5);
  if(!t.passed&&t.z>.85){
   t.passed=true;
   score+=10;
  }
  if(t.z>.76&&t.z<.93&&Math.abs(car.x-t.lane)<.32){
   burst(roadX(t.lane,H*.81),H*.81,"#ff5b52");
   endGame();return;
  }
 }
 traffic=traffic.filter(t=>t.z<1.08);

 for(const p of pickups){
  p.z+=dt*.00016*(.5+speed);
  if(p.z>.77&&p.z<.91&&Math.abs(car.x-p.lane)<.3){
   coins++;score+=50;p.z=2;
   burst(roadX(p.lane,H*.8),H*.8,"#ffd34d");
  }
 }
 pickups=pickups.filter(p=>p.z<1.05);

 particles.forEach(p=>{
  p.x+=p.vx;p.y+=p.vy;p.vy+=.08;p.life-=1;
 });
 particles=particles.filter(p=>p.life>0);

 $("score").textContent=Math.floor(score);
 $("distance").textContent=Math.floor(distance)+" m";
 $("coins").textContent=coins;
 $("level").textContent=level;
}

let last=0;
function loop(now){
 const dt=Math.min(now-last||16,35);last=now;
 update(dt);drawWorld();
 requestAnimationFrame(loop);
}
requestAnimationFrame(loop);

function startGame(){
 score=0;coins=0;distance=0;level=1;speed=.48;nitro=100;
 roadOffset=0;worldTime=0;car.x=0;
 traffic=[];pickups=[];particles=[];
 input={left:false,right:false,brake:false,nitro:false};
 state="playing";
 $("overlay").style.display="none";
 $("pause").textContent="Ⅱ PAUSE";
}

function endGame(){
 if(state!=="playing")return;
 state="over";
 best=Math.max(best,Math.floor(score));
 try{localStorage.setItem("crtBest",best)}catch(e){}
 $("message").innerHTML=
 "RACE FINISHED!<br>Score: <b>"+Math.floor(score)+
 "</b><br>Coins: <b>"+coins+"</b><br>Best: <b>"+best+
 "</b><br>Tap below to race again.";
 $("start").textContent="RACE AGAIN";
 $("overlay").style.display="flex";
}

$("start").addEventListener("click",startGame);
$("garage").addEventListener("click",()=>{
 carChoice=(carChoice+1)%carColors.length;
 $("message").textContent="Car color changed! Choose your favorite car and start racing.";
});

$("pause").addEventListener("click",()=>{
 if(state==="playing"){
  state="paused";$("pause").textContent="▶ RESUME";
  $("message").textContent="Race paused. Ready to continue?";
  $("start").textContent="RESUME RACE";
  $("overlay").style.display="flex";
 }else if(state==="paused"){
  state="playing";$("overlay").style.display="none";
  $("pause").textContent="Ⅱ PAUSE";
 }
});

function bindHold(id,key){
 const el=$(id);
 const on=e=>{e.preventDefault();input[key]=true};
 const off=e=>{e.preventDefault();input[key]=false};
 el.addEventListener("pointerdown",on);
 el.addEventListener("pointerup",off);
 el.addEventListener("pointercancel",off);
 el.addEventListener("pointerleave",off);
}
bindHold("left","left");
bindHold("right","right");
bindHold("brake","brake");
bindHold("nitro","nitro");

addEventListener("keydown",e=>{
 if(["ArrowLeft","ArrowRight","ArrowDown","ArrowUp"," "].includes(e.key))e.preventDefault();
 if(e.key==="ArrowLeft"||e.key==="a")input.left=true;
 if(e.key==="ArrowRight"||e.key==="d")input.right=true;
 if(e.key==="ArrowDown"||e.key==="s")input.brake=true;
 if(e.key==="ArrowUp"||e.key===" ")input.nitro=true;
});
addEventListener("keyup",e=>{
 if(e.key==="ArrowLeft"||e.key==="a")input.left=false;
 if(e.key==="ArrowRight"||e.key==="d")input.right=false;
 if(e.key==="ArrowDown"||e.key==="s")input.brake=false;
 if(e.key==="ArrowUp"||e.key===" ")input.nitro=false;
});
})();
</script>
</body>
</html>

Game Source: CAR RACER THUNDER

Creator: BoldRider35

Libraries: none

Complexity: complex (435 lines, 14.1 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: car-racer-thunder-boldrider35" to link back to the original. Then publish at arcadelab.ai/publish.