“Cinemagraphs” are animated photographs that feature repeated and subtle movement. Early experiments by Kevin Burg and Jamie Beck in fashion photography led to the format’s modern popularity.

But GIFs - the usual source for cinegraphs - aren’t a video format, and can’t be controlled very well. In addition, creating a true cinemagraph can require a lot of work. Over the weekend I realized that for some images the same effect could be created using . As an alternative to the expense of shooting full video, this could be a valuable, performant and efficient technique for product demos and visualizations.

Setting The Stage

We start with the still portion of the image: in this case I’ve used a photograph from the previously mentioned startupstockphotos, used as a background in a div:

<div id="backgroundcontainer">
</div>

The CSS sets the image to cover the width of the element, and sets the height of the element to the background image:

div#backgroundcontainer {
	background-image: url(macbook-background.jpg);
	background-image: image-set("macbook-background.jpg" 1x,
	"macbook-background-2x.jpg" 2x);
	background-size: contain;
	background-repeat: no-repeat;
	max-width: 750px;
	margin: 0 auto;
	padding-top: 53.4%;
	position: relative;
}

Placing The Player

Even if they’re familiar with CSS 3D, most web developers don’t realise that 3D transforms can be applied to any element… even <iframe>`, meaning that YouTube videos and other sources are open to manipulation in three dimensions.

Videos from YouTube aren’t automatically responsive, but I’ve previously addressed how to solve that problem, and will use the same solution here:

<div id="backgroundcontainer">
	<div id="ivideo">
		<iframe frameborder="0" 
			src="https://youtube.com/embed/V7be0UEJDdo…">
		</iframe>
	</div>
</div>

The embedded video will autoplay; the next step is to set it on the page.

The Light Angle

To set the position of the video correctly, several things need to happen. The first is that the outer <div> element needs the correct perspective values applied:

div#backgroundcontainer { 
	perspective: 472px;
	perspective-origin: 103% 8%;
}

We’ll use the fact that this outer container has relative positioning to set the absolute position of the inner <div> containing the video:

#ivideo {
	position: absolute;
	top: 0;
	right: 2.1%;
	width: 41.6%;
	padding-bottom: 21%;
}

The combination of absolute positioning together with width and padding-bottom makes the video responsive, if it is combined with the following declaration:

#ivideo iframe {
	position: absolute;
	width: 100%;
	height: 100%;
}

The values I used to set the correct angle for this video were:

#ivideo {
	transform: rotateX(10deg) rotateY(-7deg) translateY(74%);
}

Finding these values is a combination of experience, experimentation and testing. I knew that the vanishing point set by the perspective-origin value corresponded to the top right of the background image: you can see the vertical edges of the Macbook screen narrowing towards that location.

Chrome Element inspect property value adjusted with up-down cursor keys
The iframe starts off in the same approximate location due to the positioning of its surrounding <div>, so moving it down (using translateY was the first order of operations. Then, I had to rotate it in X and Y. This was helped by using a small trick in Chrome’s Element inspector: placing your cursor in a CSS value and using the up and down cursor keys will raise and lower the value by 1.

Cut & Print

Using this live experimentation, I gained the correct values to move the video to the right place. However, you may find that the perspective isn’t perfect at every viewport size. To fix this, I introduced a @media query:

@media screen and (max-width: 600px) {
	div#container { 
		perspective: 250px;
	}
}

I have plans for at least one other variation of this technique: if you enjoyed it, I hope you’ll come back for more. To be alerted for new updates and more web development news, I’d suggest following me on Twitter… or, if you’re old school, subscribing to this site’s RSS feed.