<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();

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.