Press Space to emit a shockwave that expands and physically pushes nearby dots outward as it passes
Code for Above
<div style="max-width:1500px;margin:0 auto;">
<iframe
title="POC — Shockwave Pushback"
scrolling="no"
style="width:1500px;height:1500px;border:0;display:block;background:#000;border-radius:14px;box-shadow:0 20px 60px rgba(0,0,0,.5);"
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>POC — Shockwave Pushback</title>
<style>
html, body { margin:0; width:100%; height:100%; overflow:hidden; background:#000; }
canvas { display:block; outline:none; }
.hint { position:absolute; left:12px; bottom:12px; color:#aaa; font:14px system-ui; user-select:none; }
</style>
</head>
<body>
<canvas id="c" width="1500" height="1500" tabindex="0"></canvas>
<div class="hint">CLICK canvas once • Arrows move • Space = shockwave push</div>
<script>
const c = document.getElementById("c");
const x = c.getContext("2d");
const W = 1500, H = 1500;
function focusCanvas(){ try { c.focus(); } catch(_){} }
focusCanvas();
c.addEventListener("pointerdown", focusCanvas);
// Key state inside iframe
const k = Object.create(null);
function isBlocked(code, key){
return code === "ArrowUp" || code === "ArrowDown" || code === "ArrowLeft" || code === "ArrowRight" ||
code === "Space" || key === " ";
}
document.addEventListener("keydown", (e) => {
if (isBlocked(e.code, e.key)) e.preventDefault();
if (e.code.startsWith("Arrow")) k[e.code] = true;
if (e.code === "Space" || e.key === " "){
if (!k.__spaceDown){
k.__spaceDown = true;
emitShock();
}
}
}, {passive:false});
document.addEventListener("keyup", (e) => {
if (isBlocked(e.code, e.key)) e.preventDefault();
if (e.code.startsWith("Arrow")) k[e.code] = false;
if (e.code === "Space" || e.key === " ") k.__spaceDown = false;
}, {passive:false});
// Player
const p = { x: W/2, y: H/2, vx:0, vy:0 };
const ACCEL = 0.55, DRAG = 0.92, MAX = 14;
// Particles/dots that get pushed
const dots = Array.from({length: 110}, () => ({
x: Math.random()*W,
y: Math.random()*H,
vx: (Math.random()*2-1)*0.4,
vy: (Math.random()*2-1)*0.4,
r: 3 + Math.random()*8
}));
// Shockwaves
const shocks = [];
const SHOCK_SPEED = 20; // radius growth/frame
const SHOCK_BAND = 30; // thickness where force applies
const SHOCK_FORCE = 2.6; // impulse strength
const DOT_DAMP = 0.985; // motion damping
function clamp(v,a,b){ return Math.max(a, Math.min(b,v)); }
function emitShock(){
shocks.push({ x:p.x, y:p.y, r:0, life:1 });
}
function update(){
// player movement
if (k.ArrowUp) p.vy -= ACCEL;
if (k.ArrowDown) p.vy += ACCEL;
if (k.ArrowLeft) p.vx -= ACCEL;
if (k.ArrowRight) p.vx += ACCEL;
p.vx *= DRAG; p.vy *= DRAG;
const sp = Math.hypot(p.vx, p.vy);
if (sp > MAX){ p.vx = (p.vx/sp)*MAX; p.vy = (p.vy/sp)*MAX; }
p.x = clamp(p.x + p.vx, 0, W);
p.y = clamp(p.y + p.vy, 0, H);
// advance shocks
for (const s of shocks){
s.r += SHOCK_SPEED;
s.life *= 0.986;
}
for (let i=shocks.length-1;i>=0;i--){
if (shocks[i].r > 2600 || shocks[i].life < 0.06) shocks.splice(i,1);
}
// update dots
for (const d of dots){
// apply shock impulse if within band
for (const s of shocks){
const dx = d.x - s.x;
const dy = d.y - s.y;
const dist = Math.hypot(dx,dy) || 0.0001;
const band = Math.abs(dist - s.r);
if (band <= SHOCK_BAND){
const nx = dx / dist;
const ny = dy / dist;
const t = 1 - (band / SHOCK_BAND); // 1 at center of band, 0 at edge
const impulse = SHOCK_FORCE * t * (0.35 + s.life);
d.vx += nx * impulse;
d.vy += ny * impulse;
}
}
// integrate + damp
d.vx *= DOT_DAMP;
d.vy *= DOT_DAMP;
d.x += d.vx;
d.y += d.vy;
// soft bounds bounce
if (d.x < d.r){ d.x = d.r; d.vx *= -0.35; }
if (d.x > W-d.r){ d.x = W-d.r; d.vx *= -0.35; }
if (d.y < d.r){ d.y = d.r; d.vy *= -0.35; }
if (d.y > H-d.r){ d.y = H-d.r; d.vy *= -0.35; }
}
}
function draw(){
x.clearRect(0,0,W,H);
// grid
x.strokeStyle = "rgba(255,255,255,0.05)";
for (let i=0;i<=W;i+=150){ x.beginPath(); x.moveTo(i,0); x.lineTo(i,H); x.stroke(); }
for (let j=0;j<=H;j+=150){ x.beginPath(); x.moveTo(0,j); x.lineTo(W,j); x.stroke(); }
// dots
x.fillStyle = "rgba(255,255,255,0.65)";
for (const d of dots){
x.beginPath();
x.arc(d.x, d.y, d.r, 0, Math.PI*2);
x.fill();
}
// shock rings
for (const s of shocks){
x.strokeStyle = `rgba(255,255,255,${0.18 + s.life*0.35})`;
x.lineWidth = 3;
x.beginPath();
x.arc(s.x, s.y, s.r, 0, Math.PI*2);
x.stroke();
}
// player
x.fillStyle = "#fff";
x.beginPath();
x.arc(p.x, p.y, 10, 0, Math.PI*2);
x.fill();
// HUD
x.fillStyle = "rgba(255,255,255,0.6)";
x.font = "14px system-ui";
x.fillText(`shocks=${shocks.length} dots=${dots.length}`, 12, 24);
}
function loop(){
update();
draw();
requestAnimationFrame(loop);
}
loop();
</script>
</body>
</html>
'></iframe>
</div>