This demonstrates:

Behaviors implemented

Cursor Morph POC


Code for Above

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Cursor Morph POC</title>
<style>
  html, body {
    margin: 0;
    background: #000;
    overflow: hidden;
  }
  canvas {
    display: block;
    margin: 0 auto;
    background: radial-gradient(#0a0a0a, #000);
    cursor: none;
  }
</style>
</head>
<body>

<canvas id="c" width="900" height="900"></canvas>

<script>
const canvas = document.getElementById("c");
const ctx = canvas.getContext("2d");

const mouse = { x: 450, y: 450 };
let targetMode = "idle";
let mode = "idle";
let t = 0;

const zones = [
  { x: 250, y: 250, r: 60, mode: "dock" },
  { x: 650, y: 250, r: 60, mode: "attack" },
  { x: 450, y: 600, r: 70, mode: "hover" }
];

canvas.addEventListener("mousemove", e => {
  const rect = canvas.getBoundingClientRect();
  mouse.x = e.clientX - rect.left;
  mouse.y = e.clientY - rect.top;
});

function dist(a, b) {
  return Math.hypot(a.x - b.x, a.y - b.y);
}

function updateMode() {
  targetMode = "idle";
  for (const z of zones) {
    if (dist(mouse, z) < z.r) {
      targetMode = z.mode;
      break;
    }
  }
}

function lerp(a, b, t) {
  return a + (b - a) * t;
}

function drawCursor(alpha) {
  ctx.save();
  ctx.translate(mouse.x, mouse.y);
  ctx.strokeStyle = `rgba(255,255,255,${alpha})`;
  ctx.lineWidth = 2;

  if (mode === "idle") {
    ctx.beginPath();
    ctx.arc(0, 0, lerp(3, 8, alpha), 0, Math.PI * 2);
    ctx.fillStyle = "#fff";
    ctx.fill();
  }

  if (mode === "hover") {
    ctx.strokeRect(-8, -8, 16, 16);
  }

  if (mode === "attack") {
    ctx.beginPath();
    ctx.moveTo(-10, 0);
    ctx.lineTo(10, 0);
    ctx.moveTo(0, -10);
    ctx.lineTo(0, 10);
    ctx.stroke();
  }

  if (mode === "dock") {
    ctx.beginPath();
    ctx.arc(0, 0, 12, 0, Math.PI * 2);
    ctx.stroke();
  }

  ctx.restore();
}

function drawZones() {
  for (const z of zones) {
    ctx.beginPath();
    ctx.arc(z.x, z.y, z.r, 0, Math.PI * 2);
    ctx.strokeStyle = "rgba(255,255,255,0.15)";
    ctx.stroke();
  }
}

function loop() {
  ctx.clearRect(0, 0, canvas.width, canvas.height);

  updateMode();

  if (mode !== targetMode) {
    t += 0.08;
    if (t >= 1) {
      t = 0;
      mode = targetMode;
    }
  } else {
    t = 0;
  }

  drawZones();
  drawCursor(1 - t);
  if (t > 0) {
    const prev = mode;
    mode = targetMode;
    drawCursor(t);
    mode = prev;
  }

  requestAnimationFrame(loop);
}

loop();
</script>

</body>
</html>