3D Carousels have always been dominated by Flash but with the recent arrival of javascript animation libraries like Raphael and Processing Js, it is now possible to create effects that in the past were limited to flash only. In this tutorial with the magic of maths and javascript we are going to create an awesome 3D carousel, so lets get started !!!
Difficulty – Intermediate

1) Index.html is our main html file.
2) We will put Raphael.js in the js folder.
<html>
<head>
<style>
#pane
{
background:#000000;
}
#logger
{
height:200px;
width:100%;
overflow:scroll;
}
</style>
<link rel="stylesheet" type="text/css" href="css/style.css" />
<script type="text/javascript" src="js/jquery.js"></script>
<script type="text/javascript" src="js/raphael-min.js"></script>
<script type="text/javascript" src="js/carousel.js"></script>
<title>3D Carousel</title>
</head>
<body>
<div id="pane">
<input type ="button" value="Previous" onclick="previous()" />
<input type ="button" value="Next" onclick="next()" />
</div>
<div id="logger">Logger</div>
</body>
</html>
This is a simple HTML code in which we are going to create a div that will act as the stage for the carousel and a logger that will output our calculations for debugging purpose.
4. Now after that point the cycle reverses and as the furthest image starts to come at the front its opacity and scale starts to increase upto to a point where the image retains its original attributes.

// these variable decide the position at the beginning
var y=0;
var radius=100;
var xcenter=70;
var ycenter=50;
var zcenter=100;
var radius_y = 60;
// Used for scaling factor.
// Basically it will work like a camera at Z axis.
var fl=100;
var shift_v = Math.PI / 2;
// placement of each image at PI/3 distance,because of 6 images.
var image_gap = Math.PI / 3;
var angle;
var angle_rad=angle * Math.PI / 180;

Step 5. Now after that we have understood the working of carousel, its time to start coding the actual code. We will create a carousel.js file in the js folder.
function MyClass()
{
}
This is same as function declaration, to create a constructor we will pass the parameters in the MyClass()
and the code looks like
function MyClass(var1,var2,..)
{
}
Now class variables and functions are bit different to create, to create a variable we will use this keyword.
For example to create a variable myVar1 we will type
this.myVar1;
and to create a function we will type
this.myFunc = function(){
}
Now our overall class looks like
function MyClass(var1)
{
this.myvar = var1;
this.myFunc = function()
{
alert(this.myvar);
}
}
That’s it we have just created a class in javascript language. To instantiate all we have to do is
var obj = MyClass(“Hello”);
and to call the method
obj.myFunc();
If you want to go in more detail regarding this, NETTUTS has an awesome article regarding Objects Oriented Javascript.
var y=0;
var radius=100;
var xcenter=180;
var ycenter=50;
var zcenter=100;
var radius_y = 60;
var fl=100;
var shift_v = Math.PI / 2;
var image_gap = Math.PI / 3;
var angle=0;
var angle_rad=angle * Math.PI / 180;
var image_ar = new Array("img/im1.PNG","img/im2.PNG","img/im3.PNG","img/im4.PNG","img/im5.PNG","img/im6.PNG");
var obj; // array in which we will store our carousel objects.
Here most of the variables have been mentioned earlier except for the angle which is used for calculation for the coordinates , image_ar which is the array for holding image file path and obj which we will use later for storing our carousel objects.
function Carousel(x,y,z,scale,imge)
{
}
Here x,y,z are the coordinates, scale is for the opacity and dimension purpose and imge refers to the path of the image.
this.X = x; this.Y = y; this.Z = z; this.img = imge; this.scale = scale; this.r_image = r.image(imge,this.X,this.Y,100,100);
X,Y,Z refers to each objects coordinates and r_image is the Raphael image object which is created using image function with parameters such as image path, coordinates and size. r is the global Raphael object which we will declare later.
this.create = function()
{
with (this)
this.r_image.scale(scale,scale);
this.r_image.animate({opacity:scale},1000);
}
Here we need to specify only its scale and opacity since the coordinates and size has already been specified when the Raphael image object was created. You might be wandering what the with(this) code is for, it is used to refer to the current object in the function.
this.update = function(angle,i)
{
with (this)
this.X= Math.cos(angle + i * image_gap + shift_v) * radius + xcenter ;
this.Z=Math.sin(angle + i * image_gap + shift_v) * radius + zcenter ;
this.scale = fl / (fl + this.Z);
this.Y= -Math .sin(angle + i * image_gap + shift_v) * radius_y + ycenter ;
with(this)
$("#logger").append("<br> Image "+img+" Scale "+scale);
this.r_image.animate({x:this.X,y:this.Y,opacity:this.scale,scale:this.scale+' '+this.scale},15);
}
This function takes angle and i which stands for the image index which we will use later. Now to create an elliptical path we will use the above formula for each coordinates. We are using trigonometric equations because of the nature of the path we want. Here we also append the scale value in the logger div. Finally we use the animate function to move the object to its new attributes. Here we took time for the animation to be 15 ms, this seems to be very short but upon looking the big picture this animates only over a short distance and increasing this time would make the whole animation much more longer to complete.
function Carousel(x,y,z,scale,imge)
{
this.X = x;
this.Y = y;
this.Z = z;
this.img = imge;
this.scale = scale;
this.r_image = r.image(imge,this.X,this.Y,100,100);
this.create = function()
{
with (this)
this.r_image.scale(scale,scale);
this.r_image.animate({opacity:scale},1000);
}
this.update = function(angle,i)
{
with (this)
var tempx = this.X;
var tempy = this.Y;
this.X= Math.cos(angle + i * image_gap + shift_v) * radius + xcenter ;
this.Z=Math.sin(angle + i * image_gap + shift_v) * radius + zcenter ;
this.scale = fl / (fl + this.Z);
this.Y= -Math .sin(angle + i * image_gap + shift_v) * radius_y + ycenter ;
with(this)
$("#logger").append("<br> Image "+img+" Scale "+scale);
this.r_image.animate({x:this.X,y:this.Y,opacity:this.scale,scale:this.scale+' '+this.scale},15);
}
}
$(function(){
W = 800; H = 400;
r = Raphael("pane", W, H);
var i =0;
obj = new Array();
for(i=0;i<image_ar.length;i++)
{
var x1 = Math.cos(angle + i * image_gap + shift_v ) * radius + xcenter ;
var y1 = -Math.sin(angle + i * image_gap + shift_v ) * radius_y + ycenter;
var z = Math.sin(angle + i * image_gap + shift_v ) * radius + zcenter;
var scale = fl/(fl + Math.sin(angle +i * image_gap + shift_v) * radius + zcenter);
obj[i] = new Carousel(x1,y1,z,scale,image_ar[i]);
obj[i].create();
}
});
So when the DOM gets loaded we will create a global Raphael object with 800 width and 400 height which makes our pane div a canvas. Now We will create 6 carousels objects on the plane and to keep track of them we will store them in an array obj which we have declared in the global namespace. In the for loop we create the Carousel objects with their proper attributes and call the create() function to set the scale and opacity attributes. Now it looks like a 3D carousel.

function next(){
angle_rad = angle_rad + Math.PI / 60;
var i;
for(i=0;i<image_ar.length;i++)
{
obj[i].update(angle_rad,i);
}
}
We will update all the objects of the carousel to their new values. Since we need motion in clockwise direction we will add PI/60 to the angle variable. Similarly for previous function we will subtract PI/60 from angle and update all objects.
function previous()
{
angle_rad = angle_rad - Math.PI / 60;
var i;
for(i=0;i<image_ar.length;i++)
{
obj[i].update(angle_rad,i);
}
}
But there is a problem with these functions that the displacement is so small we will barely notice it, we need keyframes similarly like flash for our functions for consistent animation. So we are going to wrap it in setIntveral() function and repeat it specified times. So now our functions looks like.
function next()
{
var count =0;
var t = setInterval(function(){
angle_rad = angle_rad + Math.PI / 60;
var i;
for(i=0;i<image_ar.length;i++)
{
obj[i].update(angle_rad,i);
}
count++;
if(count==20)
clearInterval(t);
},50);
}
function previous()
{
var count =0;
var t = setInterval(function(){
angle_rad = angle_rad - Math.PI / 60;
var i;
for(i=0;i<image_ar.length;i++)
{
obj[i].update(angle_rad,i);
}
count++;
if(count==20)
clearInterval(t);
},50);
}
var y=0;
var radius=100;
var xcenter=180;
var ycenter=50;
var zcenter=100;
var radius_y = 60;
var fl=100;
var shift_v = Math.PI / 2;
var image_gap = Math.PI / 3;
var angle=0;
var angle_rad=angle * Math.PI / 180;
var image_ar = new Array("img/im1.PNG","img/im2.PNG","img/im3.PNG","img/im4.PNG","img/im5.PNG","img/im6.PNG");
var obj;
function Carousel(x,y,z,scale,imge)
{
this.X = x;
this.Y = y;
this.Z = z;
this.img = imge;
this.scale = scale;
this.r_image = r.image(imge,this.X,this.Y,100,100);
this.create = function()
{
with (this)
this.r_image.scale(scale,scale);
this.r_image.animate({opacity:scale},1000);
}
this.update = function(angle,i)
{
with (this)
var tempx = this.X;
var tempy = this.Y;
this.X= Math.cos(angle + i * image_gap + shift_v) * radius + xcenter ;
this.Z=Math.sin(angle + i * image_gap + shift_v) * radius + zcenter ;
this.scale = fl / (fl + this.Z);
this.Y= -Math .sin(angle + i * image_gap + shift_v) * radius_y + ycenter ;
with(this)
$("#logger").append("<br> Image "+img+" Scale "+scale);
this.r_image.animate({x:this.X,y:this.Y,opacity:this.scale,scale:this.scale+' '+this.scale},15);
}
}
function next()
{
var count =0;
var t = setInterval(function(){
angle_rad = angle_rad + Math.PI / 60;
var i;
for(i=0;i<image_ar.length;i++)
{
obj[i].update(angle_rad,i);
}
count++;
if(count==20)
clearInterval(t);
},50);
}
function previous()
{
var count =0;
var t = setInterval(function(){
angle_rad = angle_rad - Math.PI / 60;
var i;
for(i=0;i<image_ar.length;i++)
{
obj[i].update(angle_rad,i);
}
count++;
if(count==20)
clearInterval(t);
},50);
}
$(function(){
W = 800; H = 400;
r = Raphael("pane", W, H);
var i =0;
obj = new Array();
for(i=0;i<image_ar.length;i++)
{
var x1 = Math.cos(angle + i * image_gap + shift_v ) * radius + xcenter ;
var y1 = -Math.sin(angle + i * image_gap + shift_v ) * radius_y + ycenter;
var z = Math.sin(angle + i * image_gap + shift_v ) * radius + zcenter;
var scale = fl/(fl + Math.sin(angle +i * image_gap + shift_v) * radius + zcenter);
obj[i] = new Carousel(x1,y1,z,scale,image_ar[i]);
obj[i].create();
}
});
})
Step 18. We are finally done:), for IE users animate() function does not work it can be overcome by using translate() method.

With a little tweaking you can use it in website header, ads and by attaching events they can be used with lightbox too.
November 17th, 2009 at 8:48 am
Cool ![]()
A demo would have been better
November 17th, 2009 at 8:56 am
Here’s a working demo –
http://webaura.1free.ws/carousel/
November 17th, 2009 at 10:08 am
nice ..
November 17th, 2009 at 7:48 pm
Great work Abhin! I love to see things that would usually require a plugin or flash done by hand instead. Keep it up!
November 18th, 2009 at 1:40 am
I love carousels but always wondered how they worked. Thank you for the info and also thank you to the person who posted a link for an example.
November 18th, 2009 at 1:42 am
thanks
November 18th, 2009 at 8:17 am
Nice tutorial, I like trying to do things myself before using a plugin. It’s a good learning curve. I also usually find that it’s not actually that hard.
[...] Creating a 3D carousel from scratch [...]
December 10th, 2009 at 1:36 am
[...] Creating a 3D carousel from scratch By Abhin Sharma, November 17th, 2009 Site: Brenelz Web Solutions [...]
December 31st, 2009 at 12:09 am
excellent work you did in whole tutorial, helping for developers.
One thing that I’ve noticed is that upon rotating the carousel will shift the elements and will have an offset from the initialized location. I’ve been diving through the code but have not been able to find what causes this. any answers?
One thing that I’ve noticed is that upon rotating the carousel will shift the elements and will have an offset from the initialized location. I’ve been diving through the code but have not been able to find what causes this. any answers?
Great tutorial. After some improvements I would like to obtain the same effect like 3d gallery
The example web site seems to be broken. When I copied the code and tried it. The images appear and rotate like a carousel but the sizes do not change. The first image which is big and in front moves to the next location, but the size of all images remain the same.
December 27th, 2011 at 1:29 pm
my 2ct
line 43:
this.r_image.animate({x:this.X,y:this.Y,opacity:this.scale,transform:’s'+this.scale},15);
line 103:
remove })
Great example thanks !!
Is there any process to make this rotation automatic? Imean to say, I dont want to put previous and next buttons. Thanks in advance…
Twitter
Follow me on Twitter to keep up to date!
RSS Feed
Keep up with all of our updates by subscribing to our RSS feed!
FaceBook
Join our group on Facebook and become a fan of us!