Code for Above
<div style="max-width:1500px;margin:0 auto;">
<iframe
title="FLAK POC — Shell Travel → Burst"
scrolling="no"
style="display:block;margin:0 auto;border:0;width:1500px;height:1500px;overflow:hidden;border-radius:14px;background:#000;"
sandbox="allow-scripts allow-same-origin"
srcdoc='<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<title>FLAK POC — Shell Travel → Burst</title>
<style>
:root{color-scheme:dark;}
html,body{margin:0;height:100%;background:#05070b;overflow:hidden;font-family:system-ui,-apple-system,Segoe UI,Roboto,Arial,sans-serif;}
canvas{display:block;}
</style>
</head>
<body>
<canvas id="c"></canvas>
<script>
(() => {
const c = document.getElementById("c");
const ctx = c.getContext("2d");
const W = c.width = 1500;
const H = c.height = 1500;
const TAU = Math.PI*2;
const clamp=(v,a,b)=>Math.max(a,Math.min(b,v));
const rand=(a,b)=>a+Math.random()*(b-a);
// ===== Player =====
const ship = { x: W/2, y: H*0.72, a: -Math.PI/2 };
// ===== FLAK params =====
const rofs = [{name:"SLOW",sec:0.22},{name:"FAST",sec:0.12}];
let rofIdx = 0;
let fuseDist = 520; // where shell should detonate (Z/C)
let shellSpeed = 820; // px/sec (G/H)
let pelletN = 22; // fragments per burst (X/V)
let blastR = 120; // blast radius (B/N)
let fragSpeed = 760; // fragment velocity
let fragLife = 0.55; // seconds
let proxOn = false; // proximity fuse (P)
let proxR = 90; // prox radius
// ===== State =====
const keys = Object.create(null);
let triggerHeld = false;
let cd = 0;
const targets = [];
const particles = []; // frags + visuals
const shells = []; // traveling shells
let score=0, hits=0, kills=0;
function spawnTarget() {
const dist = rand(220, 820);
const ang = ship.a + rand(-0.9, 0.9);
const x = clamp(ship.x + Math.cos(ang)*dist, 80, W-80);
const y = clamp(ship.y + Math.sin(ang)*dist, 120, H-80);
targets.push({
x,y, r: rand(12, 20),
vx: rand(-42,42), vy: rand(-42,42),
hp: 3
});
}
function ensureTargets(){
while(targets.length < 12) spawnTarget();
}
ensureTargets();
// ===== Input =====
addEventListener("keydown",(e)=>{
const k=e.key.toLowerCase();
keys[k]=true;
if(e.key===" "){ e.preventDefault(); triggerHeld=true; }
if(k==="r") reset();
// fuse distance
if(k==="z") fuseDist = Math.max(220, fuseDist-40);
if(k==="c") fuseDist = Math.min(900, fuseDist+40);
// pellet count
if(k==="x") pelletN = Math.max(8, pelletN-2);
if(k==="v") pelletN = Math.min(60, pelletN+2);
// blast radius
if(k==="b") blastR = Math.max(60, blastR-10);
if(k==="n") blastR = Math.min(240, blastR+10);
// fire rate
if(k==="1") rofIdx = 0;
if(k==="2") rofIdx = 1;
// shell speed
if(k==="g") shellSpeed = Math.max(420, shellSpeed-60);
if(k==="h") shellSpeed = Math.min(1400, shellSpeed+60);
// proximity fuse
if(k==="p") proxOn = !proxOn;
});
addEventListener("keyup",(e)=>{
const k=e.key.toLowerCase();
keys[k]=false;
if(e.key===" ") triggerHeld=false;
});
function reset(){
targets.length=0;
particles.length=0;
shells.length=0;
score=0; hits=0; kills=0;
ensureTargets();
}
// ===== Effects =====
function addExplosion(x,y){
particles.push({type:"boom", x,y, t:0, dur:0.45});
particles.push({type:"ring", x,y, t:0, dur:0.28, r0:10, r1:blastR});
for(let j=0;j<18;j++){
const a=rand(0,TAU), sp=rand(220,760);
particles.push({type:"spark", x,y, vx:Math.cos(a)*sp, vy:Math.sin(a)*sp, t:0, dur:rand(0.18,0.35), s:rand(1.2,2.2)});
}
}
function detonateShell(sx,sy, forwardAngle){
// Burst visuals
particles.push({type:"bloom", x:sx, y:sy, t:0, dur:0.18, r0:8, r1:60});
particles.push({type:"ring", x:sx, y:sy, t:0, dur:0.25, r0:10, r1:blastR});
// Immediate area damage
let blastHits = 0;
for(const t of targets){
const d = Math.hypot(t.x-sx, t.y-sy);
if(d <= blastR){
const dmg = (d < blastR*0.5) ? 2 : 1;
t.hp -= dmg;
blastHits++;
}
}
if(blastHits>0){ hits += blastHits; score += blastHits*2; }
// Fragments (slightly forward-biased cone so it reads better)
for(let i=0;i<pelletN;i++){
const a = forwardAngle + rand(-1.05, 1.05);
const sp = rand(fragSpeed*0.55, fragSpeed);
particles.push({
type:"frag",
x:sx, y:sy,
vx: Math.cos(a)*sp,
vy: Math.sin(a)*sp,
t:0, dur: rand(fragLife*0.7, fragLife),
s: rand(1.6, 3.2)
});
}
}
// ===== Fire =====
function fireShell(){
cd = rofs[rofIdx].sec;
const ax = Math.cos(ship.a);
const ay = Math.sin(ship.a);
const tx = ship.x + ax*fuseDist;
const ty = ship.y + ay*fuseDist;
const dist = Math.hypot(tx-ship.x, ty-ship.y);
const ttl = dist / shellSpeed;
shells.push({
x: ship.x + ax*26,
y: ship.y + ay*26,
vx: ax*shellSpeed,
vy: ay*shellSpeed,
t: 0,
ttl,
tx, ty,
a: ship.a
});
}
// ===== Update =====
function updateTargets(dt){
for(const t of targets){
t.x += t.vx*dt;
t.y += t.vy*dt;
if(t.x < 80 || t.x > W-80) t.vx *= -1;
if(t.y < 120 || t.y > H-80) t.vy *= -1;
t.x = clamp(t.x, 80, W-80);
t.y = clamp(t.y, 120, H-80);
}
// remove dead
for(let i=targets.length-1;i>=0;i--){
if(targets[i].hp<=0){
const tx=targets[i].x, ty=targets[i].y;
addExplosion(tx,ty);
targets.splice(i,1);
kills++; score+=10;
}
}
}
function updateShells(dt){
for(let i=shells.length-1;i>=0;i--){
const s = shells[i];
s.t += dt;
s.x += s.vx*dt;
s.y += s.vy*dt;
// proximity fuse
if(proxOn){
for(const t of targets){
const d = Math.hypot(t.x-s.x, t.y-s.y);
if(d <= proxR){
detonateShell(s.x,s.y,s.a);
shells.splice(i,1);
return;
}
}
}
// reached time-to-live or close to target point
const dTo = Math.hypot(s.tx - s.x, s.ty - s.y);
if(s.t >= s.ttl || dTo < 18){
detonateShell(s.tx, s.ty, s.a);
shells.splice(i,1);
}
}
}
function updateParticles(dt){
for(let i=particles.length-1;i>=0;i--){
const p=particles[i];
p.t += dt;
if(p.t >= p.dur){ particles.splice(i,1); continue; }
if(p.type==="frag" || p.type==="spark"){
p.x += p.vx*dt;
p.y += p.vy*dt;
p.vx *= Math.pow(0.08, dt);
p.vy *= Math.pow(0.08, dt);
if(p.type==="frag"){
for(const t of targets){
const d = Math.hypot(t.x-p.x, t.y-p.y);
if(d <= t.r){
t.hp -= 1;
p.type="spark";
p.dur = 0.14;
p.t = Math.min(p.t, p.dur*0.4);
hits++; score+=1;
break;
}
}
}
}
}
}
// ===== Render =====
function roundRect(x,y,w,h,r){
const rr=Math.min(r,w/2,h/2);
ctx.beginPath();
ctx.moveTo(x+rr,y);
ctx.arcTo(x+w,y,x+w,y+h,rr);
ctx.arcTo(x+w,y+h,x,y+h,rr);
ctx.arcTo(x,y+h,x,y,rr);
ctx.arcTo(x,y,x+w,y,rr);
ctx.closePath();
}
function drawBackground(){
ctx.fillStyle="#05070b";
ctx.fillRect(0,0,W,H);
ctx.globalAlpha=0.18;
ctx.fillStyle="rgba(220,235,255,.9)";
for(let i=0;i<240;i++){
ctx.fillRect((i*173)%W,(i*97)%H,1,1);
}
ctx.globalAlpha=1;
}
function drawTargets(){
for(const t of targets){
ctx.globalAlpha=0.22;
ctx.strokeStyle="rgba(240,245,255,.90)";
ctx.lineWidth=2;
ctx.beginPath(); ctx.arc(t.x,t.y,t.r+10,0,TAU); ctx.stroke();
ctx.globalAlpha=0.95;
ctx.fillStyle="rgba(255,200,120,.95)";
ctx.beginPath(); ctx.arc(t.x,t.y,t.r,0,TAU); ctx.fill();
ctx.globalAlpha=0.85;
ctx.fillStyle="rgba(255,240,240,.9)";
for(let i=0;i<t.hp;i++){
ctx.beginPath();
ctx.arc(t.x - (t.hp-1)*5 + i*10, t.y + t.r + 18, 2.2, 0, TAU);
ctx.fill();
}
ctx.globalAlpha=1;
}
}
function drawShells(){
for(const s of shells){
// trajectory line (faint) to detonation point
ctx.globalAlpha=0.18;
ctx.strokeStyle="rgba(120,255,210,.85)";
ctx.lineWidth=2;
ctx.beginPath();
ctx.moveTo(s.x,s.y);
ctx.lineTo(s.tx,s.ty);
ctx.stroke();
// shell glow
ctx.globalAlpha=0.70;
ctx.fillStyle="rgba(255,220,160,1)";
ctx.beginPath(); ctx.arc(s.x,s.y,3.2,0,TAU); ctx.fill();
ctx.globalAlpha=0.25;
ctx.fillStyle="rgba(255,220,160,1)";
ctx.beginPath(); ctx.arc(s.x,s.y,10,0,TAU); ctx.fill();
ctx.globalAlpha=1;
}
}
function drawParticles(){
for(const p of particles){
const u = p.t / p.dur;
const a = 1-u;
if(p.type==="frag"){
ctx.globalAlpha=0.65*a;
ctx.fillStyle="rgba(255,200,140,1)";
ctx.beginPath(); ctx.arc(p.x,p.y,p.s*(0.7+a),0,TAU); ctx.fill();
} else if(p.type==="spark"){
ctx.globalAlpha=0.85*a;
ctx.fillStyle="rgba(255,120,140,1)";
ctx.beginPath(); ctx.arc(p.x,p.y,p.s*(0.6+a),0,TAU); ctx.fill();
} else if(p.type==="ring"){
const rr = p.r0 + (p.r1-p.r0)*(u*u);
ctx.globalAlpha=0.40*a;
ctx.strokeStyle="rgba(240,245,255,1)";
ctx.lineWidth=2.0*a;
ctx.beginPath(); ctx.arc(p.x,p.y,rr,0,TAU); ctx.stroke();
} else if(p.type==="bloom"){
const rr = p.r0 + (p.r1-p.r0)*(u*u);
ctx.globalAlpha=0.22*a;
ctx.fillStyle="rgba(255,220,160,1)";
ctx.beginPath(); ctx.arc(p.x,p.y,rr,0,TAU); ctx.fill();
} else if(p.type==="boom"){
const rr = 18 + u*u*120;
ctx.globalAlpha=0.24*a;
ctx.fillStyle="rgba(255,160,120,1)";
ctx.beginPath(); ctx.arc(p.x,p.y,rr,0,TAU); ctx.fill();
ctx.globalAlpha=0.14*a;
ctx.fillStyle="rgba(255,220,160,1)";
ctx.beginPath(); ctx.arc(p.x,p.y,rr*0.55,0,TAU); ctx.fill();
}
}
ctx.globalAlpha=1;
}
function drawShip(){
ctx.globalAlpha=0.20;
ctx.fillStyle="rgba(0,255,200,.50)";
ctx.beginPath(); ctx.arc(ship.x,ship.y,42,0,TAU); ctx.fill();
ctx.save();
ctx.translate(ship.x,ship.y);
ctx.rotate(ship.a);
ctx.globalAlpha=1;
ctx.strokeStyle="rgba(240,245,255,.95)";
ctx.lineWidth=2;
ctx.beginPath();
ctx.moveTo(22,0);
ctx.lineTo(-14,12);
ctx.lineTo(-10,0);
ctx.lineTo(-14,-12);
ctx.closePath();
ctx.stroke();
// aim line to fuse point
ctx.globalAlpha=0.22;
ctx.beginPath();
ctx.moveTo(22,0);
ctx.lineTo(fuseDist,0);
ctx.stroke();
ctx.restore();
ctx.globalAlpha=1;
// fuse point marker
const ax=Math.cos(ship.a), ay=Math.sin(ship.a);
const bx=ship.x + ax*fuseDist;
const by=ship.y + ay*fuseDist;
ctx.globalAlpha=0.55;
ctx.strokeStyle="rgba(120,255,210,.85)";
ctx.lineWidth=2;
ctx.beginPath(); ctx.arc(bx,by,10,0,TAU); ctx.stroke();
ctx.globalAlpha=1;
}
function drawHUD(){
ctx.globalAlpha=0.85;
ctx.fillStyle="rgba(8,10,16,.65)";
ctx.strokeStyle="rgba(255,255,255,.10)";
ctx.lineWidth=1;
roundRect(18,18,720,120,14);
ctx.fill(); ctx.stroke();
ctx.globalAlpha=0.95;
ctx.fillStyle="rgba(240,245,255,.92)";
ctx.font="800 14px system-ui";
ctx.fillText("FLAK POC — Shell travels to fuse point then bursts. Space fires (hold). A/D rotates. R reset.", 32, 42);
ctx.font="700 12px system-ui";
ctx.globalAlpha=0.85;
ctx.fillText(`Score ${score} • Hits ${hits} • Kills ${kills} • Targets ${targets.length} • Shells ${shells.length}`, 32, 64);
ctx.fillText(`FuseDist ${fuseDist} (Z/C) • ShellSpeed ${shellSpeed} (G/H) • Pellets ${pelletN} (X/V) • Radius ${blastR} (B/N) • ROF ${rofs[rofIdx].name} (1/2)`, 32, 86);
ctx.fillText(`Proximity Fuse ${proxOn?"ON":"OFF"} (P) • ProxRadius ${proxR}`, 32, 108);
ctx.globalAlpha=1;
}
// ===== Loop =====
let last = performance.now();
function tick(now){
const dt = Math.min(0.033,(now-last)/1000);
last = now;
// rotate
const turn=2.4;
if(keys["a"]||keys["arrowleft"]) ship.a -= turn*dt;
if(keys["d"]||keys["arrowright"]) ship.a += turn*dt;
if(cd>0) cd -= dt;
if(triggerHeld && cd<=0){
fireShell();
}
updateTargets(dt);
updateShells(dt);
updateParticles(dt);
ensureTargets();
drawBackground();
drawTargets();
drawShells();
drawParticles();
drawShip();
drawHUD();
requestAnimationFrame(tick);
}
requestAnimationFrame(tick);
})();
</script>
</body>
</html>'
></iframe>
</div>