Neon Target Rush
by BlazeGalaxy19356 lines12.6 KB
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Neon Target Rush</title>
<style>
*{box-sizing:border-box}
body{margin:0;background:#080d1c;color:white;font-family:Arial,sans-serif;display:flex;justify-content:center;align-items:center;min-height:100vh}
#game{width:min(960px,100vw);position:relative}
canvas{width:100%;display:block;background:#101b35;border:2px solid #36d9ff;border-radius:12px;cursor:crosshair;touch-action:none}
#help{text-align:center;color:#b8c9e8;font-size:13px;padding:8px}
</style>
</head>
<body>
<div id="game">
<canvas id="c" width="960" height="560"></canvas>
<div id="help">Move your mouse to aim • Click to fire • Press SPACE to fire • Press P to pause</div>
</div>
<script>
const canvas=document.getElementById("c");
const ctx=canvas.getContext("2d");
const W=960,H=560;
let mode="menu",score=0,level=1,coins=0,best=0;
let shots=0,hits=0,selected=0,ammo=Infinity;
let mouse={x:480,y:280};
let target=null,obstacles=[],particles=[],bullets=[];
let last=0,spawnTimer=0,audio=null,musicTimer=null;
let musicOn=true,soundOn=true;
let leaderboard=JSON.parse(localStorage.getItem("neonBoard")||"[]");
let owned=JSON.parse(localStorage.getItem("neonOwned")||"[0]");
let weapons=[
{name:"SIDEARM",price:0,damage:1,cooldown:350,color:"#5ce1ff",desc:"Fast starter blaster"},
{name:"BURST BLASTER",price:80,damage:2,cooldown:500,color:"#ffcf5c",desc:"Powerful energy burst"},
{name:"PRECISION BLASTER",price:160,damage:3,cooldown:700,color:"#ff75d5",desc:"High power energy shot"}
];
let lastShot=0;
function save(){
localStorage.setItem("neonBoard",JSON.stringify(leaderboard));
localStorage.setItem("neonOwned",JSON.stringify(owned));
}
function audioStart(){
if(!audio) audio=new (window.AudioContext||window.webkitAudioContext)();
if(audio.state==="suspended")audio.resume();
}
function tone(freq,duration,type="sine",volume=.04){
if(!soundOn)return;
audioStart();
let o=audio.createOscillator(),g=audio.createGain();
o.type=type;o.frequency.value=freq;
g.gain.setValueAtTime(volume,audio.currentTime);
g.gain.exponentialRampToValueAtTime(.001,audio.currentTime+duration);
o.connect(g);g.connect(audio.destination);
o.start();o.stop(audio.currentTime+duration);
}
function shootSound(){tone(240,.12,"sawtooth",.035);tone(520,.08,"sine",.025)}
function hitSound(){tone(700,.1,"triangle",.05);tone(950,.16,"sine",.03)}
function music(){
if(!musicOn)return;
audioStart();
let notes=[110,146,174,220,174,146,130,196];
let i=0;
if(musicTimer)clearInterval(musicTimer);
musicTimer=setInterval(()=>{
if(mode!=="play"||!musicOn)return;
tone(notes[i%notes.length],.22,"triangle",.012);
if(i%2===0)tone(55,.18,"sine",.018);
i++;
},260);
}
function stopMusic(){if(musicTimer){clearInterval(musicTimer);musicTimer=null}}
function newTarget(){
let r=22+Math.random()*13;
target={
x:Math.random()*(W-180)+90,
y:100+Math.random()*330,
r:r,hp:1+Math.floor(level/3),
vx:(Math.random()<.5?-1:1)*(1.4+level*.22),
vy:(Math.random()<.5?-1:1)*(0.7+level*.12),
phase:Math.random()*6
};
}
function makeObstacles(){
obstacles=[];
for(let i=0;i<5+Math.min(level,5);i++){
obstacles.push({
x:100+Math.random()*760,
y:100+Math.random()*350,
w:45+Math.random()*75,
h:25+Math.random()*55,
glow:Math.random()*5
});
}
}
function startGame(){
audioStart();score=0;level=1;shots=0;hits=0;
particles=[];bullets=[];makeObstacles();newTarget();
mode="play";last=performance.now();music();
}
function endGame(){
mode="over";stopMusic();
leaderboard.push(score);
leaderboard.sort((a,b)=>b-a);
leaderboard=leaderboard.slice(0,5);
best=Math.max(best,score);
localStorage.setItem("neonBest",best);
save();
tone(180,.4,"sawtooth",.05);
}
best=Number(localStorage.getItem("neonBest")||0);
function fire(){
if(mode!=="play")return;
let w=weapons[selected],now=performance.now();
if(now-lastShot<w.cooldown)return;
lastShot=now;shots++;shootSound();
let dx=mouse.x-480,dy=mouse.y-510;
let a=Math.atan2(dy,dx);
bullets.push({x:480,y:510,vx:Math.cos(a)*13,vy:Math.sin(a)*13,life:55,color:w.color});
}
function burst(x,y,color){
for(let i=0;i<13;i++){
let a=Math.random()*Math.PI*2,s=1+Math.random()*5;
particles.push({x,y,vx:Math.cos(a)*s,vy:Math.sin(a)*s,life:25+Math.random()*20,color});
}
}
function blocked(x,y){
return obstacles.some(o=>x>o.x&&x<o.x+o.w&&y>o.y&&y<o.y+o.h);
}
function update(dt){
if(mode!=="play")return;
let t=dt/16.67;
if(!target)newTarget();
target.x+=target.vx*t;target.y+=target.vy*t;
target.phase+=.08*t;
if(target.x<target.r||target.x>W-target.r)target.vx*=-1;
if(target.y<80||target.y>H-80)target.vy*=-1;
// Obstacles slowly shift to make each level harder.
obstacles.forEach(o=>{
o.x+=Math.sin(performance.now()/900+o.glow)*.35*t;
if(o.x<20)o.x=20;
if(o.x+o.w>W-20)o.x=W-o.w-20;
});
bullets.forEach(b=>{b.x+=b.vx*t;b.y+=b.vy*t;b.life-=t});
bullets=bullets.filter(b=>b.life>0&&b.x>0&&b.x<W&&b.y>0&&b.y<H);
for(let b of bullets){
if(target&&Math.hypot(b.x-target.x,b.y-target.y)<target.r+5){
b.life=0;target.hp-=weapons[selected].damage;
burst(b.x,b.y,weapons[selected].color);hitSound();
if(target.hp<=0){
score+=10;coins+=10;hits++;
if(score>=level*50){level++;makeObstacles();tone(850,.3,"sine",.05)}
newTarget();
}
}else if(blocked(b.x,b.y)){
b.life=0;burst(b.x,b.y,"#9ba9c9");
}
}
particles.forEach(p=>{p.x+=p.vx*t;p.y+=p.vy*t;p.vx*=.97;p.vy*=.97;p.life-=t});
particles=particles.filter(p=>p.life>0);
}
function background(){
let g=ctx.createLinearGradient(0,0,W,H);
g.addColorStop(0,"#101a3a");g.addColorStop(.5,"#172b48");g.addColorStop(1,"#090f24");
ctx.fillStyle=g;ctx.fillRect(0,0,W,H);
ctx.strokeStyle="rgba(60,190,255,.12)";ctx.lineWidth=1;
for(let x=0;x<W;x+=48){ctx.beginPath();ctx.moveTo(x,0);ctx.lineTo(x,H);ctx.stroke()}
for(let y=0;y<H;y+=48){ctx.beginPath();ctx.moveTo(0,y);ctx.lineTo(W,y);ctx.stroke()}
for(let i=0;i<45;i++){
let x=(i*173)%W,y=(i*97)%H;
ctx.fillStyle="rgba(160,220,255,.35)";
ctx.fillRect(x,y,2,2);
}
}
function drawTarget(){
if(!target)return;
let pulse=Math.sin(target.phase)*4;
ctx.save();ctx.translate(target.x,target.y);
ctx.shadowBlur=22;ctx.shadowColor="#ff5b78";
ctx.fillStyle="#d94d68";
ctx.beginPath();ctx.arc(0,0,target.r+pulse,0,Math.PI*2);ctx.fill();
ctx.shadowBlur=0;
ctx.fillStyle="#29172c";
ctx.beginPath();ctx.arc(0,0,target.r*.63,0,Math.PI*2);ctx.fill();
ctx.fillStyle="#ffdf75";
ctx.beginPath();ctx.arc(-7,-4,4,0,Math.PI*2);ctx.arc(7,-4,4,0,Math.PI*2);ctx.fill();
ctx.fillStyle="#fff";ctx.fillRect(-8,9,16,4);
ctx.strokeStyle="#ff9eaa";ctx.lineWidth=3;
ctx.beginPath();ctx.arc(0,0,target.r+8,0,Math.PI*2);ctx.stroke();
ctx.restore();
// Health bar
ctx.fillStyle="#333";ctx.fillRect(target.x-25,target.y-target.r-19,50,6);
ctx.fillStyle="#ff647f";ctx.fillRect(target.x-25,target.y-target.r-19,50*Math.max(0,target.hp/(1+Math.floor(level/3))),6);
}
function draw(){
background();
// distant city shapes
for(let i=0;i<13;i++){
let x=i*78,h=70+(i*47)%100;
ctx.fillStyle="#101b35";ctx.fillRect(x,H-h,60,h);
ctx.fillStyle="rgba(66,205,255,.3)";
for(let yy=H-h+12;yy<H-10;yy+=19)ctx.fillRect(x+12,yy,5,5);
}
// Obstacles
obstacles.forEach(o=>{
ctx.fillStyle="#263d5d";ctx.strokeStyle="#6c9ab9";ctx.lineWidth=2;
ctx.fillRect(o.x,o.y,o.w,o.h);ctx.strokeRect(o.x,o.y,o.w,o.h);
ctx.fillStyle="rgba(67,213,255,.2)";ctx.fillRect(o.x+5,o.y+5,o.w-10,5);
});
drawTarget();
// Energy bolts
bullets.forEach(b=>{
ctx.shadowBlur=15;ctx.shadowColor=b.color;ctx.fillStyle=b.color;
ctx.beginPath();ctx.arc(b.x,b.y,6,0,Math.PI*2);ctx.fill();ctx.shadowBlur=0;
});
particles.forEach(p=>{
ctx.globalAlpha=Math.max(0,p.life/40);ctx.fillStyle=p.color;
ctx.fillRect(p.x,p.y,5,5);ctx.globalAlpha=1;
});
// Player blaster at bottom
ctx.save();ctx.translate(480,510);
let a=Math.atan2(mouse.y-510,mouse.x-480);
ctx.rotate(a);
ctx.fillStyle=weapons[selected].color;
ctx.shadowBlur=15;ctx.shadowColor=weapons[selected].color;
ctx.fillRect(-4,-9,42,18);ctx.fillRect(25,-5,15,10);
ctx.shadowBlur=0;ctx.restore();
ctx.fillStyle="white";ctx.font="bold 20px Arial";
ctx.fillText("SCORE: "+score,22,34);
ctx.fillText("LEVEL: "+level,22,62);
ctx.fillText("COINS: "+coins,22,90);
ctx.textAlign="right";ctx.fillText("BEST: "+best,W-22,34);ctx.textAlign="left";
if(mode==="play"){
ctx.fillStyle="#b8d9f5";ctx.font="14px Arial";
ctx.fillText("BLASTER: "+weapons[selected].name,22,H-20);
}
if(mode!=="play")drawMenu();
}
function panel(){
ctx.fillStyle="rgba(5,10,25,.88)";
ctx.fillRect(190,80,580,400);
ctx.strokeStyle="#39dfff";ctx.lineWidth=2;
ctx.strokeRect(190,80,580,400);
}
function drawMenu(){
panel();ctx.textAlign="center";
if(mode==="menu"){
ctx.fillStyle="#5ce1ff";ctx.font="bold 42px Arial";ctx.fillText("NEON TARGET RUSH",480,155);
ctx.fillStyle="white";ctx.font="20px Arial";ctx.fillText("Aim • Dodge obstacles • Hit the moving bot!",480,205);
button("START GAME",350,245,260,55);
button("WEAPON SHOP",350,315,260,48);
button("LEADERBOARD",350,378,260,48);
}else if(mode==="shop"){
ctx.fillStyle="#5ce1ff";ctx.font="bold 32px Arial";ctx.fillText("BLASTER SHOP",480,125);
ctx.fillStyle="white";ctx.font="17px Arial";ctx.fillText("Coins: "+coins,480,158);
weapons.forEach((w,i)=>{
let y=190+i*65;
ctx.fillStyle=i===selected?"#284c6b":"#182943";
ctx.fillRect(245,y,470,54);
ctx.fillStyle=w.color;ctx.font="bold 18px Arial";ctx.fillText(w.name,365,y+23);
ctx.fillStyle="white";ctx.font="14px Arial";
ctx.fillText(owned.includes(i)?"OWNED":w.price+" COINS",590,y+23);
});
button("BACK",390,405,180,45);
}else if(mode==="board"){
ctx.fillStyle="#5ce1ff";ctx.font="bold 32px Arial";ctx.fillText("LOCAL LEADERBOARD",480,135);
ctx.fillStyle="white";ctx.font="20px Arial";
for(let i=0;i<5;i++)ctx.fillText((i+1)+". "+(leaderboard[i]??"---"),480,195+i*42);
button("BACK",390,405,180,45);
}else if(mode==="over"){
ctx.fillStyle="#ff7894";ctx.font="bold 42px Arial";ctx.fillText("ROUND OVER!",480,160);
ctx.fillStyle="white";ctx.font="22px Arial";ctx.fillText("Your score: "+score,480,215);
ctx.fillText("Targets hit: "+hits,480,250);
button("PLAY AGAIN",350,290,260,52);
button("MAIN MENU",350,355,260,48);
}else if(mode==="pause"){
ctx.fillStyle="#5ce1ff";ctx.font="bold 42px Arial";ctx.fillText("PAUSED",480,205);
button("RESUME",350,265,260,55);
button("MAIN MENU",350,340,260,48);
}
ctx.textAlign="left";
}
function button(label,x,y,w,h){
ctx.fillStyle="#174c6c";ctx.fillRect(x,y,w,h);
ctx.strokeStyle="#5ce1ff";ctx.lineWidth=2;ctx.strokeRect(x,y,w,h);
ctx.fillStyle="white";ctx.font="bold 19px Arial";ctx.textAlign="center";
ctx.fillText(label,x+w/2,y+h/2+7);ctx.textAlign="left";
}
function clickMenu(x,y){
if(mode==="menu"){
if(x>350&&x<610&&y>245&&y<300)startGame();
else if(x>350&&x<610&&y>315&&y<363)mode="shop";
else if(x>350&&x<610&&y>378&&y<426)mode="board";
}else if(mode==="shop"){
if(y>190&&y<244){selectWeapon(0)}
if(y>255&&y<309){selectWeapon(1)}
if(y>320&&y<374){selectWeapon(2)}
if(y>405&&y<450)mode="menu";
}else if(mode==="board"){
if(y>405&&y<450)mode="menu";
}else if(mode==="over"){
if(y>290&&y<342)startGame();
if(y>355&&y<403)mode="menu";
}else if(mode==="pause"){
if(y>265&&y<320){mode="play";music()}
if(y>340&&y<390){mode="menu";stopMusic()}
}
}
function selectWeapon(i){
if(owned.includes(i)){selected=i;return}
if(coins>=weapons[i].price){
coins-=weapons[i].price;owned.push(i);selected=i;save();
tone(800,.2,"sine",.05);
}else tone(120,.2,"square",.04);
}
function position(e){
let r=canvas.getBoundingClientRect();
return {x:(e.clientX-r.left)*W/r.width,y:(e.clientY-r.top)*H/r.height};
}
canvas.addEventListener("pointermove",e=>{mouse=position(e)});
canvas.addEventListener("pointerdown",e=>{
e.preventDefault();audioStart();
let p=position(e);mouse=p;
if(mode!=="play"){clickMenu(p.x,p.y);return}
fire();
});
window.addEventListener("keydown",e=>{
if(e.code==="Space"){
e.preventDefault();
if(mode==="play")fire();
else if(mode==="menu")startGame();
else if(mode==="over")startGame();
else if(mode==="pause"){mode="play";music()}
}
if(e.key.toLowerCase()==="p"&&mode==="play"){mode="pause";stopMusic()}
if(e.key==="Escape"&&mode==="play"){mode="pause";stopMusic()}
});
function loop(now){
let dt=Math.min(40,now-last||16);last=now;
update(dt);draw();
requestAnimationFrame(loop);
}
requestAnimationFrame(loop);
</script>
</body>
</html>
Game Source: Neon Target Rush
Creator: BlazeGalaxy19
Libraries: none
Complexity: complex (356 lines, 12.6 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-target-rush-blazegalaxy19-mw54vhc6" to link back to the original. Then publish at arcadelab.ai/publish.