Recently one of my students asked if it was possible to free-rotate an element on screen using mouse or touch movement. As my first semester classes concentrate on just HTML5 and CSS, the answer had to be “No, not without JavaScript”… but this solution needs only a few lines of JS, and this variation has the interesting addition of CSS variables.

Objects in Space

The demo features just a single element on the page:

<img src="dasha.jpg" srcset="dasha-2x.jpg 2x" alt>

(Of course, the page could have as many elements you wish; I’ve eliminated everything else for the sake of simplicity and clarity).

The body is set to 0 margin and a min-height of 100vh, and uses flexbox to center the element on the page:

body {
  min-height: 100vh;
  display: flex;
  justify-content: center;
  align-items: center;
  perspective: 60vw;
  margin: 0;
  --mouseX: 0deg;
  --mouseY: 0deg;

In addition, a CSS 3D perspective is set, together with two CSS variables. These variables are used to style the image:

img {
  width: 80vmin;
  height: 80vmin;
  transform: rotateX(calc(var(--mouseY)))

Position Tracking

The JavaScript starts by identifying the image:

const img = document.getElementsByTagName("img")[0];

While it’s common for CSS variables can be defined and used solely inside of a stylesheet, it’s also possible to set them from JavaScript, using setProperty. I’ve done so inside a function that takes the current position of the pointer, determines the height and width of browser window (and divides it in half), then uses this information to determine the correct amount of rotation for the image:

function sway(xPos, yPos) {
  let wh = window.innerHeight / 2,
  ww = window.innerWidth / 2;"--mouseX", (xPos - ww) / 25+"deg");"--mouseY", (yPos - wh) / 25+"deg");

This function is initiated by both mouse and touch movement:

document.addEventListener("mousemove", function(e) {

document.addEventListener("touchmove", function(e) {
    var touch = e.targetTouches[0];
    if (touch) {
        sway(touch.pageX, touch.pageY);

The Power of Variables

As Lea Verou pointed out in her recent CSS Conf presentation, browser’s near-universal support for CSS variables today allows styles to move back into the position they are meant to hold - that of presentation - while JavaScript can take its traditional role of behaviour. CSS variables allow a seamless communication between the two.

If you did need support in MS Edge and IE, (and get around the current limitations of Firefox) you could write the function solely in JavaScript:

function sway(xPos, yPos) { = "rotateX("+(yPos - wh) / 25+"deg) rotateY("+(xPos - ww) / 25 +"deg)";

I’ve also created a CodePen of this version, in addition to the original code.

Photograph by Sean Archer, used under a Creative Commons license

Enjoy this piece? I invite you to follow me at to learn more.
Check out the CodePen demo for this article at