← back to Model Arena
data/artifacts/172f180e3161/gemma3-12b.html
245 lines
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Two-Player Pong</title>
<style>
html, body {
margin: 0;
padding: 0;
overflow: hidden;
background: #111; /* Darker background */
color: #fff;
font-family: 'Press Start 2P', monospace; /* Retro font */
display: flex;
align-items: center;
justify-content: center;
height: 100vh;
}
canvas {
background: #000;
display: block;
image-rendering: pixelated; /* Sharpens pixels */
}
#gameOver, #restart {
position: absolute;
top: 50%;
transform: translateY(-50%);
text-align: center;
font-size: 2em;
width: 100%;
color: #f0f0f0;
cursor: pointer;
padding: 10px;
border: 1px solid #444; /* Subtle border */
background: rgba(0, 0, 0, 0.7); /* Semi-transparent background */
}
#restart {
display: none;
}
</style>
<link href="https://fonts.googleapis.com/css2?family=Press+Start+2P&display=swap" rel="stylesheet">
</head>
<body>
<canvas id="pongCanvas" width="800" height="400"></canvas>
<div id="gameOver" style="display:none;">Game Over</div>
<div id="restart" style="display:none;" onclick="init()">Press to Restart</div>
<script>
const canvas = document.getElementById('pongCanvas');
const ctx = canvas.getContext('2d');
const width = canvas.width;
const height = canvas.height;
let leftPaddle = { x: 10, y: 150, width: 8, height: 80, dy: 0 };
let rightPaddle = { x: width - 20, y: 150, width: 8, height: 80, dy: 0 };
let ball = {
x: width / 2,
y: height / 2,
radius: 8,
dx: 3,
dy: 3,
speed: 3
};
let leftScore = 0;
let rightScore = 0;
let gameOver = false;
const winScore = 11;
function drawPaddle(paddle) {
ctx.fillStyle = '#fff'; /* White paddles */
ctx.fillRect(paddle.x, paddle.y, paddle.width, paddle.height);
}
function drawBall() {
ctx.beginPath();
ctx.arc(ball.x, ball.y, ball.radius, 0, Math.PI * 2);
ctx.fillStyle = '#fff'; /* White ball */
ctx.fill();
ctx.closePath();
}
function drawNet() {
ctx.strokeStyle = '#fff';
ctx.lineWidth = 1;
for (let i = 0; i < height; i += 25) {
ctx.beginPath();
ctx.moveTo(width / 2, i);
ctx.lineTo(width / 2, i + 12);
ctx.stroke();
}
}
function drawScore() {
ctx.font = '32px "Press Start 2P", monospace'; /* Retro font */
ctx.fillStyle = '#fff';
ctx.fillText(leftScore, width / 4 - 20, 50);
ctx.fillText(rightScore, 3 * width / 4 + 20, 50);
}
function movePaddles() {
leftPaddle.y += leftPaddle.dy;
rightPaddle.y += rightPaddle.dy;
// Prevent paddles from moving out of bounds
if (leftPaddle.y < 0) leftPaddle.y = 0;
if (leftPaddle.y + leftPaddle.height > height) leftPaddle.y = height - leftPaddle.height;
if (rightPaddle.y < 0) rightPaddle.y = 0;
if (rightPaddle.y + rightPaddle.height > height) rightPaddle.y = height - rightPaddle.height;
}
function moveBall() {
ball.x += ball.dx;
ball.y += ball.dy;
// Bounce off top and bottom
if (ball.y - ball.radius < 0 || ball.y + ball.radius > height) {
ball.dy *= -1;
playSound('hit');
}
// Paddle collision
let leftPaddleCollision = ball.x - ball.radius < leftPaddle.x + leftPaddle.width &&
ball.y > leftPaddle.y && ball.y < leftPaddle.y + leftPaddle.height;
if (leftPaddleCollision) {
ball.dx *= -1;
let relativeY = (ball.y - (leftPaddle.y + leftPaddle.height / 2)) / (leftPaddle.height / 2);
ball.dy = relativeY * 4; // Adjusted angle
ball.speed += 0.15;
playSound('hit');
}
let rightPaddleCollision = ball.x + ball.radius > rightPaddle.x &&
ball.y > rightPaddle.y && ball.y < rightPaddle.y + rightPaddle.height;
if (rightPaddleCollision) {
ball.dx *= -1;
let relativeY = (ball.y - (rightPaddle.y + rightPaddle.height / 2)) / (rightPaddle.height / 2);
ball.dy = relativeY * 4; // Adjusted angle
ball.speed += 0.15;
playSound('hit');
}
// Score
if (ball.x - ball.radius < 0) {
rightScore++;
resetBall();
playSound('score');
}
if (ball.x + ball.radius > width) {
leftScore++;
resetBall();
playSound('score');
}
}
function resetBall() {
ball.x = width / 2;
ball.y = height / 2;
ball.dx = (Math.random() > 0.5 ? -1 : 1) * ball.speed;
ball.dy = (Math.random() - 0.5) * 6;
}
function draw() {
ctx.clearRect(0, 0, width, height);
drawNet();
drawPaddle(leftPaddle);
drawPaddle(rightPaddle);
drawBall();
drawScore();
}
function update() {
movePaddles();
moveBall();
draw();
}
function gameLoop() {
if (gameOver) return;
update();
requestAnimationFrame(gameLoop);
}
function checkWin() {
if (leftScore >= winScore || rightScore >= winScore) {
gameOver = true;
document.getElementById('gameOver').style.display = 'block';
document.getElementById('restart').style.display = 'block';
}
}
function init() {
leftScore = 0;
rightScore = 0;
gameOver = false;
resetBall();
ball.speed = 3; // Reset ball speed on restart
document.getElementById('gameOver').style.display = 'none';
document.getElementById('restart').style.display = 'none';
gameLoop();
}
document.addEventListener('keydown', function(e) {
if (e.key === 'w') leftPaddle.dy = -6;
if (e.key === 's') leftPaddle.dy = 6;
if (e.key === 'ArrowUp') rightPaddle.dy = -6;
if (e.key === 'ArrowDown') rightPaddle.dy = 6;
});
document.addEventListener('keyup', function(e) {
if (e.key === 'w' || e.key === 's') leftPaddle.dy = 0;
if (e.key === 'ArrowUp' || e.key === 'ArrowDown') rightPaddle.dy = 0;
});
const audioCtx = new (window.AudioContext || window.webkitAudioContext)();
function playSound(type) {
if (!audioCtx) return;
if (audioCtx.state === 'suspended') {
audioCtx.resume();
}
const osc = audioCtx.createOscillator();
const gain = audioCtx.createGain();
osc.type = type === 'hit' ? 'square' : 'sine';
osc.frequency.value = type === 'hit' ? 1200 : 440;
gain.gain.setValueAtTime(0.3, audioCtx.currentTime);
gain.gain.exponentialRampToValueAtTime(0.01, audioCtx.currentTime + 0.2);
osc.connect(gain);
gain.connect(audioCtx.destination);
osc.start();
osc.stop(audioCtx.currentTime + 0.2);
}
init();
</script>
</body>
</html>