left right

The construction of this CSS 3D gallery is fairly straightforward: eight images, absolutely stacked inside a relatively positioned figure element, which in turn is placed inside a containing div.

<div id="gallery">
	<figure id="spinner">
		<img src="wanaka-tree.jpg" alt>
		<img src="still-lake.jpg" alt>
		<img src="pink-milford-sound.jpg" alt>
		<img src="paradise.jpg" alt>
		<img src="morekai.jpg" alt>
		<img src="milky-blue-lagoon.jpg" alt>
		<img src="lake-tekapo.jpg" alt>
		<img src="milford-sound.jpg" alt>

(I’ve left the alt attribute values of the images blank for the sake of brevity).


The initial CSS is also fairly simple: the z of the origin for the images and <figure> (the axis around which the elements will be transformed) is pushed “back” into the screen by 500 pixels, with the <figure> provided a transition speed of one second. The images are 40% wide, with a left of 30% positioning them in the center of the figure, making the gallery perfectly .

body {
	background: #100000;
	font-size: 1.5rem;
div#gallery {
	perspective: 1200px;
figure#spinner {
	transform-style: preserve-3d;
	min-height: 122px;
	-webkit-transform-origin: 50% 50%;
	transform-origin: 50% 50% -500px;
	transition: 1s;
figure#spinner img {
	width: 40%;
	position: absolute;
	left: 30%;
	transform-origin-z: 50% 50% -500px;
	outline: 1px solid transparent;

(The min-height on the <figure> and outline on the images are provided to avoid the issues I discussed in my recent article on CSS 3D tips and tricks, while the min-width on the images ensures that the photographs do not grow so large as to overlap).

The images are then evenly distributed around the central axis, using nth-child selectors:

figure#spinner img:nth-child(1) {
	transform: rotateY(0deg);
figure#spinner img:nth-child(2) {
	transform: rotateY(-45deg);
figure#spinner img:nth-child(3) {
	transform: rotateY(-90deg);
figure#spinner img:nth-child(4) {
	transform: rotateY(-135deg);
figure#spinner img:nth-child(5) {
	transform: rotateY(-180deg);
figure#spinner img:nth-child(6) {
	transform: rotateY(-225deg);
figure#spinner img:nth-child(7) {
	transform: rotateY(-270deg);
figure#spinner img:nth-child(8) {
	transform: rotateY(-315deg);

At this stage, you’ll see that the 3D carousel is naturally responsive: as you narrow the browser window, the images get smaller, while the separation between them increases.

Safari continues to have a bug in locating the origin of 3D elements; since Chrome is now prefix-free in regards to CSS 3D, only Safari should follow the hacked line of code needed for it to set the origin correctly. (You’ll still need to provide prefixed versions of the other transforms; they’re not included here to save space).

JavaScript-driven controls

Finally, we want to add two controls for the gallery: a left and right arrow to control the spin. Adding the linked elements below the page, we’ll use them to call on a JavaScript function that carries a variable.

<a href="#" style="float: left" onclick="galleryspin('-')">◀</a>
<a href="#" style="float: right" onclick="galleryspin('')">▶</a>

As you can see, the left-hand arrow carries a negative sign as a variable to a galleryspin function:

var angle = 0;
function galleryspin(sign) {
	spinner = document.querySelector("#spinner");
	if (!sign) {
		angle = angle + 45;
	} else {
		angle = angle - 45;
	spinner.setAttribute("style","-webkit-transform: rotateY("+ angle +"deg);
	transform: rotateY("+ angle +"deg);");

The function looks at the sign variable, and, based on its absence or presence, adds or removes 45 degrees from the orientation of the gallery, producing the result you see at the top of this article.


The code presented here works well in all modern browsers, with issues in just IE 10, as the browser can have problems supporting CSS 3D when it is applied to child elements.


Outside of wider browser support, there many possible improvements that we could make to the 3D carousel gallery, including:

  • fading any images that are not front and center.
  • abstracting the JavaScript to allow for any number of images to be added to the gallery.
  • captions on the images.
  • gesture support.

Many of these advances are in the Advanced CSS 3D Carousel article, which comes next.

This article is a variant of a tutorial in my book, promoted by a reader bringing my attention that there was a tiny but vital piece of code missing from the printed text. I’ve wanted to upgrade the code ever since I wrote it, so this entry is a chance to address both issues.

Enjoy this piece? I invite you to follow me at twitter.com/dudleystorey to learn more.
Check out the CodePen demo for this article at https://codepen.io/dudleystorey/pen/kiajB