<div style="text-align:center;color:#000066;font-family:monospace">
<h3>SPACE INVADERS</h3>
<div id="highscore">High Score: 0 | Kernal Coded 2026 for TinyPortal</div>
<canvas id="game" width="360" height="440"
style="background:black;border:6px solid #00ff66;box-shadow:0 0 30px #00ff66 inset;"></canvas>
<div style="margin-top:5px;">
<button id="leftBtn" style="width:60px;height:40px;margin:5px;">⬅</button>
<button id="fireBtn" style="width:60px;height:40px;margin:5px;">🔥</button>
<button id="rightBtn" style="width:60px;height:40px;margin:5px;">➡</button>
</div>
<p>⬅ ➡ Move | SPACE Fire | R Restart</p>
</div>
<script>
const canvas = document.getElementById("game");
const ctx = canvas.getContext("2d");
// Player
let player={x:170,y:400,w:24,h:12,speed:4,flash:0}
// Bullets
let bullets=[], enemyBullets=[]
// Invaders
let invaders=[]
// Shields
let shields=[]
// UFO
let ufo=null
let ufoScoreFlash=[]
// Game state
let level=1, score=0, direction=1, speed=0.6
let keys={}, lastShot=0, fireDelay=300
let highScore=localStorage.getItem("tpInvaderHighScore")||0
let gameOver=false
document.getElementById("highscore").innerHTML=
"High Score: "+highScore+" | Kernal Coded 2026 for TinyPortal"
// Starfield
const stars=[]
for(let i=0;i<80;i++) stars.push({x:Math.random()*360,y:Math.random()*440,r:Math.random()*2+1,speed:Math.random()*0.5+0.2})
function drawStars(){ ctx.fillStyle="white"; stars.forEach(s=>{ ctx.fillRect(s.x,s.y,s.r,s.r); s.y+=s.speed; if(s.y>440){s.y=0;s.x=Math.random()*360} }) }
// Sprites
const alienA1=["00111100","01111110","11011011","11111111","01111110","01000010"]
const alienA2=["00111100","01111110","11111111","11011011","11111111","00100100"]
const playerShip=["00100","01110","11111"]
const ufoSprite=["00111100","01111110","11111111","01111110"]
let marchStep=0, marchTimer=0, marchInterval=30
function drawSprite(sprite,x,y,s,c){
ctx.fillStyle=c
for(let r=0;r<sprite.length;r++)
for(let d=0;d<sprite[r].length;d++)
if(sprite[r][d]=="1") ctx.fillRect(x+d*s,y+r*s,s,s)
}
// Sounds
const audioCtx = new (window.AudioContext || window.webkitAudioContext)()
function playTone(freq,duration=0.1,volume=0.2){
let osc=audioCtx.createOscillator()
let gain=audioCtx.createGain()
osc.connect(gain); gain.connect(audioCtx.destination)
osc.type="square"; osc.frequency.value=freq
gain.gain.value=volume
osc.start()
osc.stop(audioCtx.currentTime+duration)
}
// Explosions
let explosions=[]
function explosion(x,y){
for(let i=0;i<8;i++) explosions.push({x:x+Math.random()*16,y:y+Math.random()*16,r:Math.random()*2+1,l:12,color:Math.random()>0.5?"yellow":"white"})
playTone(600,0.1)
}
// Create invaders
function createInvaders(){
invaders=[];
for(let r=0;r<5;r++)
for(let c=0;c<10;c++) invaders.push({x:40+c*28,y:50+r*26,alive:true,flash:0})
}
function drawInvaders(){
invaders.forEach(i=>{
if(!i.alive) return
let sprite=(marchStep%2)?alienA1:alienA2
if(i.flash>0){ ctx.fillStyle="white"; i.flash--; ctx.fillRect(i.x,i.y,16,16); return }
drawSprite(sprite,i.x,i.y,2,"white")
})
}
// Invader marching rhythm
function marchInvaders(){
marchTimer++
if(marchTimer>marchInterval){ marchTimer=0; marchStep=(marchStep+1)%4; playTone(100+marchStep*50,0.05) }
}
// Invader movement
function moveInvaders(){
let edge=false;
invaders.forEach(i=>{ if(!i.alive) return; i.x+=direction*speed; if(i.x<10||i.x>320) edge=true });
if(edge){ direction*=-1; invaders.forEach(i=>i.y+=10); }
}
// Alien shooting
function alienShoot(){
let alive=invaders.filter(i=>i.alive);
if(alive.length && Math.random()<0.01*level){
let s=alive[Math.floor(Math.random()*alive.length)];
enemyBullets.push({x:s.x+8,y:s.y+12});
playTone(400,0.05)
}
}
// Shields
function createShields(){
shields=[];
for(let i=0;i<4;i++) shields.push({x:50+i*70,y:340,hp:30})
}
function drawShields(){
ctx.fillStyle="#00ff66";
shields.forEach(s=>{
for(let i=0;i<s.hp;i++) ctx.fillRect(s.x+(i%6)*5,s.y+Math.floor(i/6)*5,4,4)
})
}
// Player
function movePlayer(){
if(keys["ArrowLeft"]||keys["TouchLeft"]) player.x-=player.speed;
if(keys["ArrowRight"]||keys["TouchRight"]) player.x+=player.speed;
if(player.x<0) player.x=0; if(player.x>canvas.width-player.w) player.x=canvas.width-player.w
}
function drawPlayer(){
if(player.flash>0){ ctx.fillStyle="white"; ctx.fillRect(player.x-2,player.y-6,player.w+4,player.h); player.flash--; return }
drawSprite(playerShip,player.x,player.y-6,4,"#00ff66");
}
// Bullets
function drawBullets(){ ctx.fillStyle="yellow"; bullets.forEach(b=>ctx.fillRect(b.x,b.y,3,10)); ctx.fillStyle="red"; enemyBullets.forEach(b=>ctx.fillRect(b.x,b.y,3,10)) }
function updateBullets(){
bullets.forEach(b=>b.y-=6)
enemyBullets.forEach(b=>b.y+=4)
bullets.forEach(b=>{
invaders.forEach(i=>{
if(i.alive && b.x>i.x && b.x<i.x+16 && b.y>i.y && b.y<i.y+16){
i.alive=false; i.flash=4; b.y=-100; score+=10; saveHigh(); explosion(i.x,i.y)
}
})
shields.forEach(s=>{ if(s.hp>0 && b.x>s.x && b.x<s.x+30 && b.y>s.y && b.y<s.y+30){ s.hp-=1; b.y=-100 } })
})
enemyBullets.forEach(b=>{
if(b.x>player.x && b.x<player.x+player.w && b.y>player.y){
player.flash=6; explosion(player.x,player.y); gameOver=true; playTone(80,0.3)
}
shields.forEach(s=>{ if(s.hp>0 && b.x>s.x && b.x<s.x+30 && b.y>s.y && b.y<s.y+30){ s.hp-=1; b.y=-100 } })
})
bullets=bullets.filter(b=>b.y>0)
enemyBullets=enemyBullets.filter(b=>b.y<440)
}
// Invaders hitting shields or base
function checkInvaderCollision(){
let baseHit=false
invaders.forEach(i=>{
if(!i.alive) return;
// Hit shields
shields.forEach(s=>{
if(s.hp>0 && i.y+16 >= s.y && i.x+16 > s.x && i.x < s.x+30){
s.hp-=2; // invader destroys shield
i.flash=4; // invader flashes
}
})
// Hit player area
if(i.y+16 >= player.y){
// Check if shields already gone
let shieldRemaining=shields.some(s=>s.hp>0)
if(!shieldRemaining){
baseHit=true;
i.flash=4;
player.flash=6;
explosion(player.x,player.y);
explosion(i.x,i.y);
}
}
})
if(baseHit) gameOver=true
}
// UFO
function spawnUFO(){ if(!ufo && Math.random()<0.003 + level*0.0005) ufo={x:-40,y:25,s:2} }
function updateUFO(){
if(!ufo) return;
ufo.x+=ufo.s;
drawSprite(ufoSprite,ufo.x,ufo.y,2,"red");
bullets.forEach(b=>{
if(b.x>ufo.x && b.x<ufo.x+32 && b.y>ufo.y){
score+=150;
ufoScoreFlash.push({x:ufo.x,y:ufo.y-10,value:150,timer:20});
ufo=null;
playTone(700,0.15)
}
});
if(ufo && ufo.x>380) ufo=null
}
// Draw UFO score flashes
function drawUfoScoreFlash(){
ufoScoreFlash.forEach(f=>{
ctx.fillStyle="yellow";
ctx.font="14px monospace";
ctx.fillText(f.value,f.x,f.y);
f.timer--;
});
ufoScoreFlash=ufoScoreFlash.filter(f=>f.timer>0)
}
// Explosions
function drawExplosions(){
explosions.forEach(e=>{ ctx.fillStyle=e.color; ctx.fillRect(e.x,e.y,e.r,e.r); e.l--; });
explosions=explosions.filter(e=>e.l>0)
}
// Level / HUD
function checkWave(){ if(!invaders.some(i=>i.alive)){ level++; speed+=0.15 + level*0.05; createInvaders() } }
function hud(){ ctx.fillStyle="#00ff66"; ctx.fillText("Score "+score,10,20); ctx.fillText("Level "+level,290,20) }
function saveHigh(){ if(score>highScore){ highScore=score; localStorage.setItem("tpInvaderHighScore",highScore) }; document.getElementById("highscore").innerHTML="High Score: "+highScore+" | Kernal Coded 2026 for TinyPortal" }
// Game reset
function resetGame(){
score=0; level=1; speed=0.6; bullets=[]; enemyBullets=[];
createInvaders(); createShields(); ufo=null; gameOver=false;
marchStep=0; marchTimer=0; ufoScoreFlash=[]; player.flash=0
}
// Game loop
function gameLoop(){
ctx.clearRect(0,0,canvas.width,canvas.height)
drawStars()
if(gameOver) resetGame()
marchInvaders()
movePlayer(); drawPlayer(); drawInvaders(); drawBullets(); drawShields(); drawExplosions(); updateBullets()
moveInvaders(); spawnUFO(); updateUFO(); drawUfoScoreFlash(); checkWave(); checkInvaderCollision()
if(Math.random()<0.02) alienShoot()
hud()
requestAnimationFrame(gameLoop)
}
// Controls
document.addEventListener("keydown",e=>{
keys[e.key]=true
if(e.key===" "){ let now=Date.now(); if(now-lastShot>fireDelay && bullets.length<1){ bullets.push({x:player.x+11,y:player.y}); lastShot=now; playTone(800,0.05) } }
if(e.key==="r"||e.key==="R") resetGame()
})
document.addEventListener("keyup",e=>keys[e.key]=false)
// Mobile buttons
document.getElementById("leftBtn").addEventListener("touchstart",()=>keys["TouchLeft"]=true)
document.getElementById("leftBtn").addEventListener("touchend",()=>keys["TouchLeft"]=false)
document.getElementById("rightBtn").addEventListener("touchstart",()=>keys["TouchRight"]=true)
document.getElementById("rightBtn").addEventListener("touchend",()=>keys["TouchRight"]=false)
document.getElementById("fireBtn").addEventListener("touchstart",()=>{
let now=Date.now()
if(now-lastShot>fireDelay && bullets.length<1){ bullets.push({x:player.x+11,y:player.y}); lastShot=now; playTone(800,0.05) }
})
// Init
createInvaders()
createShields()
gameLoop()
</script>
think it need`s a little graphics adding to it too...
<div style="text-align:center;color:#00ff66;font-family:monospace">
<h3>SPACE INVADERS</h3>
<div id="highscore">High Score: 0 | Kernal Coded 2026 for TinyPortal</div>
<canvas id="game" width="360" height="440"
style="background:black;border:6px solid #00ff66;box-shadow:0 0 30px #00ff66 inset;"></canvas>
<p>⬅ ➡ Move | SPACE Fire | R Restart</p>
</div>
<script>
const canvas = document.getElementById("game")
const ctx = canvas.getContext("2d")
// Player
let player={x:170,y:400,w:24,h:12,speed:4}
// Bullets
let bullets=[], enemyBullets=[]
// Invaders
let invaders=[]
// Shields
let shields=[]
// UFO
let ufo=null
// Game state
let level=1, score=0, direction=1, speed=0.6
let keys={}, lastShot=0, fireDelay=300
let highScore=localStorage.getItem("tpInvaderHighScore")||0
let gameOver=false
document.getElementById("highscore").innerHTML=
"High Score: "+highScore+" | Kernal Coded 2026 for TinyPortal"
// Starfield background
const stars=[]
for(let i=0;i<80;i++) stars.push({x:Math.random()*360,y:Math.random()*440,r:Math.random()*2+1,speed:Math.random()*0.5+0.2})
function drawStars(){ ctx.fillStyle="white"; stars.forEach(s=>{ ctx.fillRect(s.x,s.y,s.r,s.r); s.y+=s.speed; if(s.y>440){s.y=0;s.x=Math.random()*360} }) }
// Sprites
const alienA1=["00111100","01111110","11011011","11111111","01111110","01000010"]
const alienA2=["00111100","01111110","11111111","11011011","11111111","00100100"]
let animFrame=0
function drawSprite(sprite,x,y,s,c){
ctx.fillStyle=c
for(let r=0;r<sprite.length;r++)
for(let d=0;d<sprite[r].length;d++)
if(sprite[r][d]=="1") ctx.fillRect(x+d*s,y+r*s,s,s)
}
// Sound helpers
const audioCtx = new (window.AudioContext || window.webkitAudioContext)()
function playTone(freq,duration=0.1,volume=0.2){
let osc=audioCtx.createOscillator()
let gain=audioCtx.createGain()
osc.connect(gain); gain.connect(audioCtx.destination)
osc.type="square"; osc.frequency.value=freq
gain.gain.value=volume
osc.start()
osc.stop(audioCtx.currentTime+duration)
}
// Explosions
let explosions=[]
function explosion(x,y){
for(let i=0;i<5;i++) explosions.push({x:x+Math.random()*16,y:y+Math.random()*16,r:Math.random()*2+1,l:10})
playTone(100+Math.random()*300,0.1)
}
// Invaders
function createInvaders(){ invaders=[]; for(let r=0;r<5;r++) for(let c=0;c<10;c++) invaders.push({x:40+c*28,y:50+r*26,alive:true}) }
function drawInvaders(){ invaders.forEach(i=>{ if(!i.alive) return; let sprite=animFrame?alienA1:alienA2; drawSprite(sprite,i.x,i.y,2,"white") }) }
function moveInvaders(){ let edge=false; invaders.forEach(i=>{ if(!i.alive) return; i.x+=direction*speed; if(i.x<10||i.x>320) edge=true }); if(edge){ direction*=-1; invaders.forEach(i=>i.y+=10) } }
function alienShoot(){ let alive=invaders.filter(i=>i.alive); if(alive.length){ let s=alive[Math.floor(Math.random()*alive.length)]; enemyBullets.push({x:s.x+8,y:s.y+12}); playTone(400,0.05) } }
// Shields
function createShields(){ shields=[]; for(let i=0;i<4;i++) shields.push({x:50+i*70,y:340,hp:30}) }
function drawShields(){ ctx.fillStyle="#00ff66"; shields.forEach(s=>{for(let i=0;i<s.hp;i++) ctx.fillRect(s.x+(i%6)*5,s.y+Math.floor(i/6)*5,4,4)}) }
// Player
function movePlayer(){ if(keys["ArrowLeft"]) player.x-=player.speed; if(keys["ArrowRight"]) player.x+=player.speed; if(player.x<0) player.x=0; if(player.x>canvas.width-player.w) player.x=canvas.width-player.w }
function drawPlayer(){ ctx.fillStyle="#00ff66"; ctx.fillRect(player.x,player.y,player.w,player.h); ctx.fillRect(player.x+9,player.y-6,6,6) }
// Bullets
function drawBullets(){ ctx.fillStyle="yellow"; bullets.forEach(b=>ctx.fillRect(b.x,b.y,3,10)); ctx.fillStyle="red"; enemyBullets.forEach(b=>ctx.fillRect(b.x,b.y,3,10)) }
function updateBullets(){
bullets.forEach(b=>b.y-=6)
enemyBullets.forEach(b=>b.y+=4)
// Player bullets hit invaders or shields
bullets.forEach(b=>{
invaders.forEach(i=>{ if(i.alive && b.x>i.x && b.x<i.x+16 && b.y>i.y && b.y<i.y+16){ i.alive=false; b.y=-100; score+=10; saveHigh(); explosion(i.x,i.y) } })
shields.forEach(s=>{ if(s.hp>0 && b.x>s.x && b.x<s.x+30 && b.y>s.y && b.y<s.y+30){ s.hp-=1; b.y=-100 } })
})
// Enemy bullets hit player or shields
enemyBullets.forEach(b=>{
if(b.x>player.x && b.x<player.x+player.w && b.y>player.y){ explosion(player.x,player.y); gameOver=true; playTone(50,0.3) }
shields.forEach(s=>{ if(s.hp>0 && b.x>s.x && b.x<s.x+30 && b.y>s.y && b.y<s.y+30){ s.hp-=1; b.y=-100 } })
})
bullets=bullets.filter(b=>b.y>0)
enemyBullets=enemyBullets.filter(b=>b.y<440)
}
// UFO
function spawnUFO(){ if(!ufo && Math.random()<0.003) ufo={x:-40,y:25,s:2} }
function updateUFO(){ if(!ufo) return; ufo.x+=ufo.s; ctx.fillStyle="red"; ctx.fillRect(ufo.x,ufo.y,30,12); bullets.forEach(b=>{ if(b.x>ufo.x && b.x<ufo.x+30 && b.y>ufo.y){ score+=150; ufo=null; playTone(600,0.1) } }); if(ufo && ufo.x>380) ufo=null }
// Explosions
function drawExplosions(){ ctx.fillStyle="orange"; explosions.forEach(e=>ctx.fillRect(e.x,e.y,e.r,e.r)); explosions.forEach(e=>e.l--); explosions=explosions.filter(e=>e.l>0) }
// Level / HUD
function checkWave(){ if(!invaders.some(i=>i.alive)){ level++; speed+=0.25; createInvaders() } }
function hud(){ ctx.fillStyle="#00ff66"; ctx.fillText("Score "+score,10,20); ctx.fillText("Level "+level,290,20) }
function saveHigh(){ if(score>highScore){ highScore=score; localStorage.setItem("tpInvaderHighScore",highScore) }; document.getElementById("highscore").innerHTML="High Score: "+highScore+" | Kernal Coded 2026 for TinyPortal" }
// Game reset
function resetGame(){ score=0; level=1; speed=0.6; bullets=[]; enemyBullets=[]; createInvaders(); createShields(); ufo=null; gameOver=false }
// Game loop
function gameLoop(){
ctx.clearRect(0,0,canvas.width,canvas.height)
drawStars()
if(gameOver) resetGame()
animFrame^=1
movePlayer()
drawPlayer()
drawInvaders()
drawBullets()
drawShields()
drawExplosions()
updateBullets()
moveInvaders()
spawnUFO()
updateUFO()
checkWave()
if(Math.random()<0.02) alienShoot()
hud()
requestAnimationFrame(gameLoop)
}
// Controls
document.addEventListener("keydown",e=>{
keys[e.key]=true
if(e.key===" "){ let now=Date.now(); if(now-lastShot>fireDelay && bullets.length<1){ bullets.push({x:player.x+11,y:player.y}); lastShot=now; playTone(800,0.05) } }
if(e.key==="r"||e.key==="R") resetGame()
})
document.addEventListener("keyup",e=>keys[e.key]=false)
// Init
createInvaders()
createShields()
gameLoop()
</script>
Page created in 0.142 seconds with 19 queries.