<div class="hover-container">
  <img src="https://images.unsplash.com/photo-1469474968028-56623f02e42e?auto=format&fit=crop&w=1953&q=80" class="img" />
  <div class="overlay"></div>
</div>
<div class="hover-container">
  <img src="https://images.unsplash.com/photo-1469474968028-56623f02e42e?auto=format&fit=crop&w=1953&q=80" class="img" />
  <div class="overlay"></div>
</div>
.hover-container {
	height: max-content;
	width: max-content;
	position: relative;
	margin: 12px 24px;
}

.overlay,
.hover-container::after {
	position: absolute;
	top: 0;
	bottom: 0;
	left: 0;
	right: 0;
}

/* Without the ::after on hovering would trigger .overlay:hover and not .hover-container:hover */
.hover-container::after {
	content: "";
}

/* Additional Styling */
* {
	transition: 0.1s;
}

html,
body {
	display: flex;
	flex-direction: column;
	justify-content: center;
	align-items: center;
	min-height: 100vh;
	padding: 0;
	margin: 0;
}

.img {
	display: inline-block;
	height: 200px;
}
document.querySelectorAll(".hover-container").forEach((container) => {
  container.onmouseleave = (e) => {
    const overlayChild = e.target.querySelector(".overlay");

    e.target.style.transform = "rotateY(0) rotateX(0)";
    overlayChild.style.background = "transparent";
    overlayChild.style.borderImage = "none";
  };

  container.addEventListener("mousemove", (e) => {
    const rect = e.target.getBoundingClientRect();
    const x = e.clientX - rect.left; //x position within the element.
    const y = e.clientY - rect.top; //y position within the element.
    const width = e.target.offsetWidth;
    const height = e.target.offsetHeight;

    const overlayChild = e.target.querySelector(".overlay");

    e.target.style.transform = `rotateY(${
      -(0.5 - x / width) * 30
    }deg) rotateX(${(y / height - 0.5) * 30}deg)`;

    overlayChild.style.background = `radial-gradient(
		    circle at ${x}px ${y}px,
		    rgba(255, 255, 255, 0.2),
		    rgba(0, 0, 0, 0.2)
	    )`;
    overlayChild.style.borderImage = `radial-gradient(
            20% 75% at ${x}px ${y}px,
            rgba(255, 255, 255, 0.7),
            rgba(0, 0, 0, 0.2)
        )
        1 / 1px / 0px stretch`;
  });
});

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.