<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Pinball</title>
<style>
body, html {
margin: 0;
padding: 0;
overflow: hidden;
height: 100vh;
width: 100vw;
background: #000;
display: flex;
justify-content: center;
align-items: center;
font-family: sans-serif;
}
#game-container {
position: relative;
width: 100vw;
height: 100vh;
max-width: 1512px;
max-height: 982px;
aspect-ratio: 1512 / 982;
background: linear-gradient(to bottom, #222, #111);
overflow: hidden;
box-sizing: border-box;
border: 2px solid #444;
transform-origin: center;
}
#table {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: transparent;
}
.wall {
position: absolute;
background: #333;
box-shadow: 0 0 10px rgba(0,0,0,0.5);
}
#left-wall { left: 0; top: 0; width: 2%; height: 100%; }
#right-wall { right: 0; top: 0; width: 2%; height: 100%; }
#top-wall { left: 0; top: 0; width: 100%; height: 2%; }
#bottom-drain { left: 40%; bottom: 0; width: 20%; height: 2%; background: #000; }
.bumper {
position: absolute;
width: 5vw;
height: 5vw;
border-radius: 50%;
background: radial-gradient(circle, #f00, #a00);
box-shadow: 0 0 15px rgba(255,0,0,0.5);
}
.bumper.flash { animation: flash 0.5s; }
@keyframes flash {
0% { background: radial-gradient(circle, #ff0, #aa0); box-shadow: 0 0 30px #ff0; }
100% { background: radial-gradient(circle, #f00, #a00); box-shadow: 0 0 15px rgba(255,0,0,0.5); }
}
#bumper1 { top: 20%; left: 30%; }
#bumper2 { top: 20%; right: 30%; }
#bumper3 { top: 40%; left: 45%; }
.flipper {
position: absolute;
bottom: 5%;
width: 15vw;
height: 2vw;
background: #ccc;
transform-origin: left center;
transition: transform 0.1s;
box-shadow: 0 5px 10px rgba(0,0,0,0.5);
}
.flipper.active { transform: rotate(-45deg); }
#left-flipper { left: 20%; transform-origin: left center; }
#right-flipper { right: 20%; transform-origin: right center; }
#right-flipper.active { transform: rotate(45deg); }
#ball {
position: absolute;
width: 3vw;
height: 3vw;
border-radius: 50%;
background: radial-gradient(circle at 30% 30%, #fff, #aaa, #555);
box-shadow: 0 0 10px rgba(255,255,255,0.3), 5px 5px 10px rgba(0,0,0,0.5);
}
#score {
position: absolute;
top: 5%;
left: 50%;
transform: translateX(-50%);
color: #fff;
font-size: 4vw;
text-shadow: 0 0 10px #000;
}
#balls-left {
position: absolute;
top: 10%;
left: 50%;
transform: translateX(-50%);
color: #fff;
font-size: 2vw;
}
#game-over {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: #f00;
font-size: 10vw;
opacity: 0;
transition: opacity 1s;
text-shadow: 0 0 20px #000;
}
#game-over.visible { opacity: 1; }
@keyframes shake {
0% { transform: translate(0, 0) rotate(0); }
10% { transform: translate(-1%, 0) rotate(-1deg); }
20% { transform: translate(1%, 0) rotate(1deg); }
30% { transform: translate(0, 1%) rotate(0); }
40% { transform: translate(1%, -1%) rotate(1deg); }
50% { transform: translate(-1%, 1%) rotate(-1deg); }
60% { transform: translate(-1%, 0) rotate(0); }
70% { transform: translate(1%, 0) rotate(0); }
80% { transform: translate(-1%, -1%) rotate(-1deg); }
90% { transform: translate(1%, 1%) rotate(1deg); }
100% { transform: translate(0, 0) rotate(0); }
}
.shake { animation: shake 0.5s; }
</style>
</head>
<body>
<div id="game-container">
<div id="table">
<div id="left-wall" class="wall"></div>
<div id="right-wall" class="wall"></div>
<div id="top-wall" class="wall"></div>
<div id="bottom-drain"></div>
<div id="bumper1" class="bumper"></div>
<div id="bumper2" class="bumper"></div>
<div id="bumper3" class="bumper"></div>
<div id="left-flipper" class="flipper"></div>
<div id="right-flipper" class="flipper"></div>
<div id="ball"></div>
</div>
<div id="score">Score: 0</div>
<div id="balls-left">Balls: 3</div>
<div id="game-over">GAME OVER</div>
</div>
<script>
const container = document.getElementById('game-container');
const ball = document.getElementById('ball');
const leftFlipper = document.getElementById('left-flipper');
const rightFlipper = document.getElementById('right-flipper');
const scoreEl = document.getElementById('score');
const ballsLeftEl = document.getElementById('balls-left');
const gameOverEl = document.getElementById('game-over');
const bumpers = document.querySelectorAll('.bumper');
let ballX = container.clientWidth * 0.9;
let ballY = container.clientHeight * 0.1;
let velX = -2;
let velY = 0;
let gravity = 0.5;
let friction = 0.98;
let score = 0;
let balls = 3;
let gameOver = false;
ball.style.left = `${ballX}px`;
ball.style.top = `${ballY}px`;
function launchBall() {
ballX = container.clientWidth * 0.9;
ballY = container.clientHeight * 0.1;
velX = -Math.random() * 2 - 1;
velY = Math.random() * 2 + 1;
ball.style.left = `${ballX}px`;
ball.style.top = `${ballY}px`;
}
function checkCollision(el1, el2) {
const rect1 = el1.getBoundingClientRect();
const rect2 = el2.getBoundingClientRect();
return !(rect1.right < rect2.left || rect1.left > rect2.right || rect1.bottom < rect2.top || rect1.top > rect2.bottom);
}
function bounceOff(el, isFlipper = false) {
velY = -Math.abs(velY) * (isFlipper ? 1.5 : 1.2);
velX += (Math.random() - 0.5) * 2;
if (isFlipper) score += 10;
else score += 50;
container.classList.add('shake');
setTimeout(() => container.classList.remove('shake'), 500);
}
function update() {
if (gameOver) return;
velY += gravity;
velX *= friction;
velY *= friction;
ballX += velX;
ballY += velY;
// Walls
if (ballX <= container.clientWidth * 0.02 || ballX >= container.clientWidth * 0.98 - ball.clientWidth) {
velX = -velX * 0.8;
ballX = Math.max(container.clientWidth * 0.02, Math.min(ballX, container.clientWidth * 0.98 - ball.clientWidth));
}
if (ballY <= container.clientHeight * 0.02) {
velY = -velY * 0.8;
ballY = container.clientHeight * 0.02;
}
// Drain
if (ballY >= container.clientHeight - ball.clientHeight && ballX > container.clientWidth * 0.4 && ballX < container.clientWidth * 0.6) {
balls--;
ballsLeftEl.textContent = `Balls: ${balls}`;
if (balls > 0) {
launchBall();
} else {
gameOver = true;
gameOverEl.classList.add('visible');
}
return;
}
// Bumpers
bumpers.forEach(bumper => {
if (checkCollision(ball, bumper)) {
bounceOff(bumper);
bumper.classList.add('flash');
setTimeout(() => bumper.classList.remove('flash'), 500);
}
});
// Auto-flippers
const flipZone = container.clientHeight * 0.8;
if (ballY > flipZone) {
if (ballX < container.clientWidth / 2) {
leftFlipper.classList.add('active');
setTimeout(() => leftFlipper.classList.remove('active'), 200);
if (checkCollision(ball, leftFlipper)) bounceOff(leftFlipper, true);
} else {
rightFlipper.classList.add('active');
setTimeout(() => rightFlipper.classList.remove('active'), 200);
if (checkCollision(ball, rightFlipper)) bounceOff(rightFlipper, true);
}
}
ball.style.left = `${ballX}px`;
ball.style.top = `${ballY}px`;
scoreEl.textContent = `Score: ${score}`;
requestAnimationFrame(update);
}
launchBall();
update();
</script>
let ballX = container.clientWidth * 0.9;
let ballY = container.clientHeight * 0.1;
let velX = -2;
let velY = 0;
let gravity = 0.5;
let friction = 0.98;
let score = 0;
let balls = 3;
let gameOver = false;
ball.style.left = `${ballX}px`;
ball.style.top = `${ballY}px`;
function launchBall() {
ballX = container.clientWidth * 0.9;
ballY = container.clientHeight * 0.1;
velX = -Math.random() * 2 - 1;
velY = Math.random() * 2 + 1;
ball.style.left = `${ballX}px`;
ball.style.top = `${ballY}px`;
}
function checkCollision(el1, el2) {
const rect1 = el1.getBoundingClientRect();
const rect2 = el2.getBoundingClientRect();
return !(rect1.right < rect2.left || rect1.left > rect2.right || rect1.bottom < rect2.top || rect1.top > rect2.bottom);
}
function bounceOff(el, isFlipper = false) {
velY = -Math.abs(velY) * (isFlipper ? 1.5 : 1.2);
velX += (Math.random() - 0.5) * 2;
if (isFlipper) score += 10;
else score += 50;
container.classList.add('shake');
setTimeout(() => container.classList.remove('shake'), 500);
}
function update() {
if (gameOver) return;
velY += gravity;
velX *= friction;
velY *= friction;
ballX += velX;
ballY += velY;
// Walls
if (ballX <= container.clientWidth * 0.02 || ballX >= container.clientWidth * 0.98 - ball.clientWidth) {
velX = -velX * 0.8;
ballX = Math.max(container.clientWidth * 0.02, Math.min(ballX, container.clientWidth * 0.98 - ball.clientWidth));
}
if (ballY <= container.clientHeight * 0.02) {
velY = -velY * 0.8;
ballY = container.clientHeight * 0.02;
}
// Drain
if (ballY >= container.clientHeight - ball.clientHeight && ballX > container.clientWidth * 0.4 && ballX < container.clientWidth * 0.6) {
balls--;
ballsLeftEl.textContent = `Balls: ${balls}`;
if (balls > 0) {
launchBall();
} else {
gameOver = true;
gameOverEl.classList.add('visible');
}
return;
}
// Bumpers
bumpers.forEach(bumper => {
if (checkCollision(ball, bumper)) {
bounceOff(bumper);
bumper.classList.add('flash');
setTimeout(() => bumper.classList.remove('flash'), 500);
}
});
// Auto-flippers
const flipZone = container.clientHeight * 0.8;
if (ballY > flipZone) {
if (ballX < container.clientWidth / 2) {
leftFlipper.classList.add('active');
setTimeout(() => leftFlipper.classList.remove('active'), 200);
if (checkCollision(ball, leftFlipper)) bounceOff(leftFlipper, true);
} else {
rightFlipper.classList.add('active');
setTimeout(() => rightFlipper.classList.remove('active'), 200);
if (checkCollision(ball, rightFlipper)) bounceOff(rightFlipper, true);
}
}
ball.style.left = `${ballX}px`;
ball.style.top = `${ballY}px`;
scoreEl.textContent = `Score: ${score}`;
requestAnimationFrame(update);
}
launchBall();
update();
</script>
</body>
</html>*