David Cabezas
Copywriter
Lo-fi site. Hi-fi creative.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>AD ATTACK!: David Cabezas</title>
<style>
@import url('https://fonts.googleapis.com/css2?family=Press+Start+2P&display=swap');
body {
background-color: #000000;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
color: #FF4D4D; /* Light Red */
font-family: 'Press Start 2P', cursive;
height: 100vh;
margin: 0;
overflow: hidden;
}
canvas {
border: 4px solid #FF4D4D;
background-color: #000000;
box-shadow: 0 0 40px rgba(255, 77, 77, 0.3);
cursor: none;
image-rendering: pixelated;
}
#ui { margin-bottom: 15px; text-align: center; }
/* Interactive Link Button */
#portfolio-btn {
display: none;
position: absolute;
top: 82%;
background: #FF4D4D;
color: #000;
padding: 15px 25px;
text-decoration: none;
font-size: 12px;
border: 4px solid #fff;
cursor: pointer;
z-index: 10;
}
#portfolio-btn:hover { background: #fff; }
</style>
</head>
<body>
<div id="ui">
<h1>AD ATTACK!</h1>
<p>DODGE IDEA KILLERS FOR <span style="color:#fff">6 SECONDS</span></p>
</div>
<canvas id="gameCanvas" width="700" height="450"></canvas>
<a id="portfolio-btn" href="https://www.davidcabezaswrites.com" target="_blank">VIEW PORTFOLIO</a>
<script>
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
const btn = document.getElementById('portfolio-btn');
let gameState = 'START';
let startTime = 0;
let timeLeft = 6.00;
let mouseX = canvas.width / 2;
let mouseY = canvas.height / 2;
// --- GAME SETTINGS ---
const ERASER_SPEED = 4.5;
const ERASER_COUNT = 10;
canvas.addEventListener('mousemove', (e) => {
const rect = canvas.getBoundingClientRect();
mouseX = e.clientX - rect.left;
mouseY = e.clientY - rect.top;
});
canvas.addEventListener('mousedown', () => {
if (gameState === 'START' || gameState === 'GAMEOVER') startGame();
});
function drawPencil(x, y) {
ctx.save();
ctx.translate(x, y);
ctx.rotate(Math.PI / 4);
ctx.fillStyle = '#FF4D4D';
ctx.fillRect(-15, -5, 30, 10);
ctx.fillStyle = '#FFFFFF';
ctx.beginPath();
ctx.moveTo(15, -5); ctx.lineTo(25, 0); ctx.lineTo(15, 5);
ctx.fill();
ctx.fillStyle = '#000000';
ctx.fillRect(22, -1, 3, 2);
ctx.restore();
}
let erasers = [];
class Eraser {
constructor() {
this.size = 35;
this.reset();
}
reset() {
const side = Math.floor(Math.random() * 4);
if(side === 0) { this.x = -this.size; this.y = Math.random() * canvas.height; }
else if(side === 1) { this.x = canvas.width; this.y = Math.random() * canvas.height; }
else if(side === 2) { this.x = Math.random() * canvas.width; this.y = -this.size; }
else { this.x = Math.random() * canvas.width; this.y = canvas.height; }
const angle = Math.atan2(mouseY - this.y, mouseX - this.x);
this.dx = Math.cos(angle) * ERASER_SPEED;
this.dy = Math.sin(angle) * ERASER_SPEED;
}
update() {
this.x += this.dx;
this.y += this.dy;
if (this.x < -100 || this.x > canvas.width + 100 || this.y < -100 || this.y > canvas.height + 100) {
this.reset();
}
}
draw() {
ctx.fillStyle = 'rgba(255, 77, 77, 0.2)';
ctx.fillRect(this.x, this.y, this.size, this.size);
ctx.strokeStyle = '#FF4D4D';
ctx.lineWidth = 2;
ctx.strokeRect(this.x, this.y, this.size, this.size);
ctx.fillStyle = '#FF4D4D';
ctx.fillRect(this.x + 8, this.y + 10, 5, 5);
ctx.fillRect(this.x + 22, this.y + 10, 5, 5);
}
}
function startGame() {
gameState = 'PLAYING';
btn.style.display = 'none';
startTime = Date.now();
timeLeft = 6.00;
erasers = [];
for (let i = 0; i < ERASER_COUNT; i++) erasers.push(new Eraser());
canvas.style.cursor = 'none';
requestAnimationFrame(gameLoop);
}
function drawWinScreen() {
ctx.fillStyle = '#000000';
ctx.fillRect(0, 0, canvas.width, canvas.height);
const pulse = Math.sin(Date.now() / 200) * 3;
ctx.textAlign = 'center';
// Updated Ethos Text
ctx.fillStyle = '#FF4D4D';
ctx.font = '14px "Press Start 2P"';
ctx.fillText("AD LIFE IS A WILD GAME.", canvas.width / 2, 65 + pulse);
ctx.fillStyle = '#FFFFFF';
ctx.font = '18px "Press Start 2P"';
ctx.fillText("LET'S WIN IT.", canvas.width / 2, 105);
// David Character (Hat & Glasses)
let cx = canvas.width / 2;
let cy = 200;
let p = 6;
ctx.fillStyle = '#FF4D4D'; // Face
ctx.fillRect(cx - 3*p, cy - 3*p, 6*p, 7*p);
ctx.fillStyle = '#ffffff'; // Emote Sparks
ctx.fillRect(cx - 8*p, cy - 6*p + pulse, p, p);
ctx.fillRect(cx + 7*p, cy - 4*p - pulse, p, p);
ctx.fillStyle = '#000000'; // Hat
ctx.fillRect(cx - 4*p, cy - 5*p, 8*p, 2*p);
ctx.fillRect(cx - 3*p, cy - 7*p, 6*p, 2*p);
ctx.fillStyle = '#FFFFFF'; // Glasses
ctx.fillRect(cx - 3*p, cy - 1*p, 2*p, 2*p);
ctx.fillRect(cx + 1*p, cy - 1*p, 2*p, 2*p);
ctx.fillRect(cx - 1*p, cy, 2*p, 1*p);
ctx.fillStyle = '#000000'; // Smile
ctx.fillRect(cx - 2*p, cy + 3*p, 4*p, 1*p);
// Name & Contact
ctx.fillStyle = '#FFFFFF';
ctx.font = '16px "Press Start 2P"';
ctx.fillText("DAVID CABEZAS", canvas.width / 2, 310);
ctx.font = '10px "Press Start 2P"';
ctx.fillStyle = '#FF4D4D';
ctx.fillText("davidcabezas23@gmail.com", canvas.width / 2, 340);
btn.style.display = 'block';
canvas.style.cursor = 'default';
}
function gameLoop() {
if (gameState !== 'PLAYING') {
if (gameState === 'WIN') drawWinScreen();
return;
}
ctx.clearRect(0, 0, canvas.width, canvas.height);
let elapsed = (Date.now() - startTime) / 1000;
timeLeft = 6.00 - elapsed;
if (timeLeft <= 0) {
gameState = 'WIN';
drawWinScreen();
return;
}
erasers.forEach(e => {
e.update();
e.draw();
if (mouseX > e.x && mouseX < e.x + e.size && mouseY > e.y && mouseY < e.y + e.size) {
gameState = 'GAMEOVER';
}
});
drawPencil(mouseX, mouseY);
ctx.fillStyle = '#FFFFFF';
ctx.font = '20px "Press Start 2P"';
ctx.textAlign = 'left';
ctx.fillText(timeLeft.toFixed(2), 20, 40);
if (gameState === 'PLAYING') {
requestAnimationFrame(gameLoop);
} else if (gameState === 'GAMEOVER') {
canvas.style.cursor = 'default';
ctx.fillStyle = 'rgba(255, 0, 0, 0.4)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = '#fff';
ctx.textAlign = 'center';
ctx.font = '16px "Press Start 2P"';
ctx.fillText("ERASED!", canvas.width/2, canvas.height/2);
ctx.font = '10px "Press Start 2P"';
ctx.fillText("CLICK TO TRY AGAIN", canvas.width/2, canvas.height/2 + 40);
}
}
// Initial Screen
ctx.fillStyle = '#FF4D4D';
ctx.textAlign = 'center';
ctx.font = '12px "Press Start 2P"';
ctx.fillText("CLICK TO START", canvas.width/2, canvas.height/2);
// Keep the character animation running on win screen
setInterval(() => {
if (gameState === 'WIN') drawWinScreen();
}, 100);
</script>
</body>
</html>