<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Interactive Face Game</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<canvas id="gameCanvas" width="800" height="600"></canvas>
<p>Click and drag the eyeballs!</p>
<script src="script.js"></script>
</body>
</html>
/* Basic styling for the canvas */
body {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
flex-direction: column;
background-color: #f4f4f4;
font-family: Arial, sans-serif;
}
canvas {
border: 2px solid black;
background-color: #e0e0e0;
}
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
// Define the two eyeballs and their properties
let eyeballs = [
{ // Left eye
x: 300, y: 250, radius: 40,
dragging: false, dx: 0, dy: 0,
restX: 300, restY: 250, elasticity: 0.9, stiffness: 0.1,
draggedX: 300, draggedY: 250
},
{ // Right eye
x: 500, y: 250, radius: 40,
dragging: false, dx: 0, dy: 0,
restX: 500, restY: 250, elasticity: 0.9, stiffness: 0.1,
draggedX: 500, draggedY: 250
}
];
// Variables to keep track of mouse position
let mouse = {
x: 0,
y: 0
};
// Dynamic properties for the mouth and eyebrows
let distressLevel = 0;
// Handle mouse down event to start dragging one of the eyeballs
canvas.addEventListener('mousedown', function(e) {
mouse.x = e.offsetX;
mouse.y = e.offsetY;
// Check if the mouse is within the radius of any of the eyeballs
eyeballs.forEach(eye => {
let distance = Math.sqrt((mouse.x - eye.x) ** 2 + (mouse.y - eye.y) ** 2);
if (distance <= eye.radius) {
eye.dragging = true;
}
});
});
// Handle mouse move event to drag the eyeballs
canvas.addEventListener('mousemove', function(e) {
mouse.x = e.offsetX;
mouse.y = e.offsetY;
// Update the dragged position of the eyeball being dragged
eyeballs.forEach(eye => {
if (eye.dragging) {
eye.draggedX = mouse.x;
eye.draggedY = mouse.y;
// Calculate the distress level based on how far the eyeballs are being stretched
let deltaX = Math.abs(eye.restX - eye.draggedX);
let deltaY = Math.abs(eye.restY - eye.draggedY);
distressLevel = Math.max(distressLevel, deltaX, deltaY) / 150; // Scale distress level
}
});
});
// Handle mouse up event to stop dragging
canvas.addEventListener('mouseup', function() {
eyeballs.forEach(eye => eye.dragging = false);
distressLevel = 0; // Reset distress when eyeballs are released
});
// Update the position of the eyeballs (simulate bounce back)
function updateEyeballs() {
eyeballs.forEach(eye => {
if (!eye.dragging) {
// Calculate the difference between the eyeball's current position and its rest position
let deltaX = eye.restX - eye.draggedX;
let deltaY = eye.restY - eye.draggedY;
// Apply a spring-like force to bring the eyeball back to its original position
eye.dx += deltaX * eye.stiffness;
eye.dy += deltaY * eye.stiffness;
// Apply elasticity (friction) to slow down the movement over time
eye.dx *= eye.elasticity;
eye.dy *= eye.elasticity;
// Update the eyeball's dragged position based on velocity
eye.draggedX += eye.dx;
eye.draggedY += eye.dy;
}
});
}
// Draw the entire face (eyeballs, mouth, and eyebrows)
function drawFace() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Draw the eyeballs (with veins)
eyeballs.forEach(eye => {
// Draw the socket
ctx.beginPath();
ctx.arc(eye.restX, eye.restY, eye.radius, 0, Math.PI * 2);
ctx.fillStyle = 'darkgray';
ctx.fill();
ctx.strokeStyle = 'black';
ctx.stroke();
ctx.closePath();
// Draw the vein (line between socket and dragged eyeball)
ctx.beginPath();
ctx.moveTo(eye.restX, eye.restY);
ctx.lineTo(eye.draggedX, eye.draggedY);
ctx.strokeStyle = 'red';
ctx.lineWidth = 5;
ctx.stroke();
ctx.closePath();
// Draw the eyeball being dragged
ctx.beginPath();
ctx.arc(eye.draggedX, eye.draggedY, eye.radius * 0.8, 0, Math.PI * 2);
ctx.fillStyle = 'white';
ctx.fill();
ctx.strokeStyle = 'black';
ctx.stroke();
ctx.closePath();
// Draw the iris and pupil on the eyeball
ctx.beginPath();
ctx.arc(eye.draggedX, eye.draggedY, eye.radius * 0.4, 0, Math.PI * 2);
ctx.fillStyle = 'blue'; // Iris color
ctx.fill();
ctx.strokeStyle = 'black';
ctx.stroke();
ctx.closePath();
// Draw the pupil
ctx.beginPath();
ctx.arc(eye.draggedX, eye.draggedY, eye.radius * 0.2, 0, Math.PI * 2);
ctx.fillStyle = 'black'; // Pupil color
ctx.fill();
ctx.closePath();
});
// Draw the mouth (opening wider both vertically and horizontally based on distress level)
let mouthHeight = Math.max(10, distressLevel * 50); // Mouth height increases with distress
let mouthWidth = Math.max(50, 50 + distressLevel * 40); // Mouth width increases with distress
ctx.beginPath();
ctx.ellipse(400, 400, mouthWidth, mouthHeight, 0, 0, 2 * Math.PI); // Horizontally and vertically stretching oval
ctx.fillStyle = 'black';
ctx.fill();
ctx.closePath();
// Draw the eyebrows (curved upwards to indicate worry or shock)
ctx.beginPath();
ctx.moveTo(250, 210); // Left eyebrow start point
ctx.quadraticCurveTo(300, 180 - distressLevel * 20, 350, 210); // Left eyebrow arch
ctx.strokeStyle = 'black';
ctx.lineWidth = 5;
ctx.stroke();
ctx.beginPath();
ctx.moveTo(450, 210); // Right eyebrow start point
ctx.quadraticCurveTo(500, 180 - distressLevel * 20, 550, 210); // Right eyebrow arch
ctx.strokeStyle = 'black';
ctx.lineWidth = 5;
ctx.stroke();
}
// Main game loop
function gameLoop() {
updateEyeballs();
drawFace();
requestAnimationFrame(gameLoop);
}
// Start the game loop
gameLoop();
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.