<iframe
style="width:100%;max-width:920px;aspect-ratio:16/9;border:0;border-radius:14px;box-shadow:0 16px 55px rgba(0,0,0,.45);display:block;margin:14px auto;"
sandbox="allow-scripts allow-same-origin"
srcdoc="
html,body{margin:0;height:100%;background:#0b0f1a;color:#e8eefc;font-family:system-ui,-apple-system,Segoe UI,Roboto,Arial}
.w{display:grid;place-items:center;height:100%}
canvas{width:min(920px,96vw);height:auto;background:radial-gradient(1200px 700px at 50% 30%,#141b2f,#070a12 70%);
border-radius:14px;outline:1px solid rgba(255,255,255,.06);touch-action:none}
.hud{position:fixed;top:10px;left:50%;transform:translateX(-50%);display:flex;gap:10px;flex-wrap:wrap;align-items:baseline;justify-content:center;opacity:.92}
.pill{padding:4px 10px;border-radius:999px;background:rgba(255,255,255,.07);outline:1px solid rgba(255,255,255,.1);font-size:12px}

(function(){
const c=document.getElementById(‘c’),x=c.getContext(‘2d’),W=c.width,H=c.height,P=10,WIN=7;
const sEl=document.getElementById(‘s’),mEl=document.getElementById(‘m’);
const clamp=(v,a,b)=>Math.max(a,Math.min(b,v)),r=(a,b)=>a+Math.random()*(b-a),t=()=>performance.now();
const keys=new Set(); let ptr=false, paused=false, serve=true, to=’player’;
const L={x:40,y:H/2,w:14,h:110,vy:0,ly:H/2}, R={x:W-40,y:H/2,w:14,h:110,vy:0,ly:H/2};
const fx={p:{big:1,until:0,shield:false},a:{big:1,until:0,shield:false},fast:0};
const bump={x:W/2,y:H/2,r:22,ph:0};
let balls=[mkBall()], pu=[], nextPU=t()+r(1800,3200), ps=0, as=0;
function mkBall(){return {x:W/2,y:H/2,r:9,vx:0,vy:0,spin:0,hit:null,tr:[]};}
function ph(side){return (side===’p’?L.h*fx.p.big:R.h*fx.a.big);}
function hud(){
sEl.textContent=ps+’ — ‘+as;
if(ps>=WIN||as>=WIN) mEl.textContent=(ps>as?’You win! Press R’:’AI wins! Press R’);
else if(paused) mEl.textContent=”Paused”;
else if(serve) mEl.textContent=”Click/Enter to serve”;
else mEl.textContent=””;
}
function reset(loser){
balls=[mkBall()]; pu=[]; nextPU=t()+r(1500,3500); serve=true; to=(loser===’player’?’ai’:’player’);
L.y=R.y=H/2; L.ly=L.y; R.ly=R.y; hud();
}
function start(){
if(!serve||ps>=WIN||as>=WIN) return;
const b=balls[0], dir=(to===’player’?-1:1), sp=8.6, ang=r(-0.45,0.45);
b.vx=Math.cos(ang)*sp*dir; b.vy=Math.sin(ang)*sp; b.hit=null; serve=false; hud();
}
function spawnPU(){
const types=[‘BIG’,’MULTI’,’SHIELD’,’FAST’];
pu.push({x:r(W*0.28,W*0.72),y:r(P+60,H-P-60),r:14,type:types[(Math.random()*types.length)|0],exp:t()+r(9000,14000)});
}
function apply(type,who){
const S=(who===’player’?fx.p:fx.a);
if(type===’BIG’){S.big=1.35; S.until=t()+8000;}
if(type===’SHIELD’){S.shield=true;}
if(type===’FAST’){fx.fast=t()+6000;}
if(type===’MULTI’){
const base=balls[0], sp=Math.max(6,Math.hypot(base.vx,base.vy)||8.6), dir=Math.sign(base.vx||1)||1;
for(let i=0;i0&&dfx.p.until){fx.p.big=1;fx.p.until=0}
if(fx.a.until && tt>fx.a.until){fx.a.big=1;fx.a.until=0}
if(fx.fast && tt>fx.fast){fx.fast=0}
const LH=ph(‘p’), RH=ph(‘a’);
if(!ptr){
let dy=0; if(keys.has(‘ArrowUp’)||keys.has(‘w’)||keys.has(‘W’)) dy-=10.5;
if(keys.has(‘ArrowDown’)||keys.has(‘s’)||keys.has(‘S’)) dy+=10.5;
L.y+=dy;
}
L.y=clamp(L.y, LH/2+P, H-LH/2-P); L.vy=L.y-L.ly; L.ly=L.y;
// AI
const track=balls.find(b=>b.vx>0) || balls[0];
const desired=(track && track.x>W*0.42 ? track.y : H/2) + (Math.random()*2-1)*15;
R.y += clamp(desired-R.y, -7.7, 7.7);
R.y=clamp(R.y, RH/2+P, H-RH/2-P); R.vy=R.y-R.ly; R.ly=R.y;
// powerups
if(!serve && tt>=nextPU && pu.lengthp.exp>tt);
if(!serve && ps<WIN && astt ? 1.18 : 1.0;
for(const b of balls){
b.x+=b.vx*mul; b.y+=b.vy*mul;
if(b.y-b.r=H-P){b.y=H-P-b.r; b.vy*=-1}
const hitL=(b.x-b.rL.x-12)&&(Math.abs(b.y-L.y)<=LH/2);
if(hitL && b.vx=R.x-R.w/2)&&(b.x<R.x+12)&&(Math.abs(b.y-R.y)0){b.spin=clamp(R.vy,-14,14); hitPaddle(b,R,’ai’)}
bounceBump(b);
for(let i=pu.length-1;i>=0;i–){
const p=pu[i],dx=b.x-p.x,dy=b.y-p.y;
if(dx*dx+dy*dy 0?’player’:’ai’);
apply(p.type, who);
}
}
if(b.x W+40){
if(fx.a.shield){fx.a.shield=false; b.x=W-P-40; b.vx=-Math.abs(b.vx)-1}
else {ps++; reset(‘ai’); break;}
}
}
}
}
draw(); hud(); requestAnimationFrame(step);
}
// pointer
c.addEventListener(‘pointerdown’,e=>{ptr=true; c.setPointerCapture(e.pointerId); setY(e.clientY); start();});
c.addEventListener(‘pointermove’,e=>{if(ptr) setY(e.clientY);});
c.addEventListener(‘pointerup’,e=>{ptr=false; try{c.releasePointerCapture(e.pointerId)}catch{}});
function setY(cy){const rct=c.getBoundingClientRect(); const y=(cy-rct.top)*(H/rct.height); L.y=clamp(y, ph(‘p’)/2+P, H-ph(‘p’)/2-P);}
// keys
window.addEventListener(‘keydown’,e=>{
keys.add(e.key);
if(e.key===’ ‘){e.preventDefault(); paused=!paused; hud();}
if(e.key===’Enter’) start();
if(e.key===’r’||e.key===’R’){ps=0;as=0;fx.p.big=fx.a.big=1;fx.p.shield=fx.a.shield=false;fx.fast=0; reset(‘ai’);}
});
window.addEventListener(‘keyup’,e=>keys.delete(e.key));
reset(‘ai’); requestAnimationFrame(step);
})();
“>
The post Play Arcade Tennis Online (Free, No Signup) appeared first on Be on the Right Side of Change.

