• Skip to main content
  • Skip to primary sidebar
  • Skip to footer

Matt Doyle | Elated Communications

Web and WordPress Development

  • About Me
  • Blog
    • Design & Multimedia
      • Photoshop
      • Paint Shop Pro
      • Video & Audio
    • Online Marketing
      • E-Commerce
      • Social Media
    • Running a Website
      • WordPress
      • Apache
      • UNIX and Linux
      • Using FTP
    • Web Development
      • HTML
      • CSS
      • JavaScript
      • PHP
      • Perl and CGI Scripting
  • Portfolio
  • Contact Me
  • Hire Me
Home / Blog / Web Development / JavaScript / Make a Rotatable 3D Product Boxshot with Three.js

Make a Rotatable 3D Product Boxshot with Three.js

14 November 2019 / 13 Comments

Rotatable 3D Product Boxshot with Three.js

View Demo »

Download Code

[This article was originally published on 12 August 2011. It was updated on 14 November 2019 to cover the latest version of Three.js and add mobile support.]

To say that JavaScript has come a long way in recent years is a bit of an understatement. At the turn of the century we were using it mainly for validating forms and opening annoying pop-up windows. These days, JavaScript can play HTML5 video and audio; generate richly-detailed graphics on the fly; query a mobile device to display its location; and even make a web server.

One area where JavaScript has improved in leaps and bounds is 3D graphics. Ten years ago, the idea of smooth, real-time, realistic 3D graphics running inside a web browser seemed out of this world. Nowadays it’s becoming commonplace, thanks to modern browsers supporting both the canvas element and WebGL, a library that adds hardware-accelerated, OpenGL-like 3D graphics capabilities to the browser.

Building on top of these layers, some enterprising folks have built JavaScript libraries that make it easy for the average coder — that is, someone without a degree in computer graphics — to create realistic, animated 3D scenes, right in the browser. This is opening up a world of possibilities for online games, interactive videos, web apps, and much more.

One such library that’s gained a lot of traction is Mr. doob’s Three.js. This library gives you all you need to create complex 3D scenes in the browser, using nothing but JavaScript.

While Three.js is relatively easy to use, it can still be daunting if you’re not familiar with the concepts involved in computer-generated imagery. In this tutorial, you’ll get a gentle introduction to Three.js’s basic features, and learn how to put them to practical use by creating a rotatable 3D boxshot scene.

What Exactly Is Three.js?

Three.js is a JavaScript library that lets you create and render objects in a 3D scene. It’s designed to be lightweight and easy to use.

Three.js can render its 3D scenes using the WebGL library built into many modern browsers, such as Chrome and Firefox. This is the recommended approach, since it uses the computer’s GPU (graphics chip) to do the work. This makes rendering much faster, takes the load off the CPU, and also gives you more 3D features.

For browsers that don’t support WebGL, Three.js can also render scenes directly onto an HTML canvas element, or render using SVG. The canvas approach is generally pretty slow, while the SVG approach is somewhat limited (no bitmap textures, for example).

Three.js can do some pretty awesome things — check out some of the demos that come with the library. It is also a major component of The Wilderness Downtown, an interactive video for the band Arcade Fire, and 3 Dreams of Black (below), an interactive film promoting the album Rome.

3 Dreams of Black screenshot
3 Dreams of Black uses Three.js to create a gorgeous immersive experience right in the browser.

Not only can you create 3D objects from scratch using Three.js and JavaScript, you can also import ready-made objects created using 3D apps like Blender straight into Three.js. This opens up a world of possibilities for interactive 3D scenes and games, all running within the browser.

For this tutorial, though, we’ll keep things nice and easy, and create a simple box with some bitmap textures wrapped around it.

Ready? Let’s start building!

Step 1: Install Three.js

The easiest way to install Three.js is to click the download link over at the Three.js homepage. When the popup appears, download the latest package (I used version r110 for this tutorial) and unzip it.

Within the package folder, you’ll see a build folder containing the file three.js. Create a folder called, for example, boxshot somewhere in your local website, and copy the three.js file to the boxshot folder.

Step 2: Install OrbitControls.js

The other library file you need is OrbitControls.js. This gives you a ready-made set of controls that allow the user to move the camera around by clicking and dragging (touching and dragging on mobile).

You’ll find the OrbitControls.js file inside the examples/js/controls folder in the package folder. Copy this file to the boxshot folder you created in Step 1.

Step 3: Set Things Up

OK, you’re now ready to create your basic page for the boxshot, and initialize things. Save the following file as book.html in your boxshot folder:

<!doctype html>
<html>
<head>
<title>A Rotatable 3D Product Boxshot with Three.js</title>
<script type="text/javascript" src="Three.js"></script>
<script type="text/javascript" src="OrbitControls.js"></script>
<style>
body { margin: 0; padding: 0; }
</style>

<script type="text/javascript">

window.onload = function() {

}

</script>
</head>
<body></body>
</html>

Here you’ve created a simple HTML5 page with an empty body element. In the head, you’ve included the Three.js and OrbitControls.js libraries, and you’ve started to build a window.onload event handler function. We’ll add code to this function in the next few steps to build our boxshot scene.

Step 4: Create the Renderer

The next step is to create a Three.js renderer object. The renderer is in charge of painting the entire scene onto a canvas element.

Here’s the code to create the renderer — add it to the end of the window.onload handler function you created in Step 3, before the function’s closing brace:

  // Create the renderer and add it to the page's body element
  var renderer = new THREE.WebGLRenderer( { alpha: true } );
  renderer.setPixelRatio( window.devicePixelRatio );
  renderer.setSize( window.innerWidth, window.innerHeight );
  document.body.appendChild( renderer.domElement );

This code starts by creating a THREE.WebGLRenderer object, setting the alpha parameter to true (this gives the scene a transparent background colour instead of black). Then it sets the renderer’s pixelRatio property to match the device’s pixel ratio — this will make sure that the scene looks sharp on both regular and HiDPI (Retina) displays.

Next, the code sets the renderer’s size to the width and height of the browser window so that it fills the whole browser. Finally it adds the renderer’s canvas element — stored in the domElement property — to the document body so that it appears in the page.

Step 5: Create the Scene and Camera

You’re now ready to set up the scene and camera. The scene object holds all the objects and lights that make up the 3D scene, while the camera object specifies how the scene looks through the virtual camera that points “into” the page and views the scene.

Here’s the code to create the scene and camera — as before, add it to the end of your window.onload handler:

  // Create the scene to hold the object
  var scene = new THREE.Scene();

  // Create the camera
  var camera = new THREE.PerspectiveCamera(
    35,                                     // Field of view
    window.innerWidth / window.innerHeight, // Aspect ratio
    0.1,                                    // Near plane distance
    1000                                    // Far plane distance
  );

  // Position the camera
  camera.position.set( -15, 10, 20 );

First the code creates a new THREE.Scene object and stores it in the variable scene. Next it creates a THREE.PerspectiveCamera object and stores it in camera. The constructor takes a number of parameters that define how the scene looks through the camera:

  • Field of view defines how much of the scene can be viewed through the camera. It’s measured in degrees. Large numbers make the scene look farther away (180 degrees makes it infinitely far away). Small numbers make it look closer. 35 degrees is a good compromise.
  • Aspect ratio defines the camera’s aspect ratio — the ratio of the width of the camera window to its height. Usually you want this to be the same as the renderer’s aspect ratio.
  • The Near and far plane distances define the distance from the camera to the near and far planes. Imagine a pyramid, with the pointy end at the camera and the base off in the distance. This pyramid is then intersected by the two planes, which are parallel to the page. The visible scene is the space inside the pyramid and between the two planes. Any part of the scene that falls outside this space is clipped (not shown). Usually you want to use a very small value for the near plane, and a very large value for the far plane.
    Technically this space is known as a viewing frustum. Now there’s something to impress your friends with!

Once we’ve created our camera, we position it in the scene by calling the set() method of the camera’s position property. We place it 15 units to the left of the origin, 10 units above the origin, and 20 units in front of the origin.

Step 6: Add the Lights

Lights allow you to add realism to a scene by creating areas of light and shadow. If you don’t add lights then the scene looks rather flat.

Let’s add a couple of lights to the scene: a point light — which simulates a single point of light such as a light bulb — and an ambient light, which provides a base level of light to illuminate the whole scene and soften the shadows.

Here’s the code — add it to the window.onload handler, after the code you added in Step 5:

  // Add the lights

  var light = new THREE.PointLight( 0xffffff, .4 );
  light.position.set( 10, 10, 10 );
  scene.add( light );

  ambientLight = new THREE.AmbientLight( 0xbbbbbb );
  scene.add( ambientLight );

First we create our point light as a THREE.PointLight object. We give the light a white colour and an intensity of 0.4, which is fairly low (the default is 1). This stops the light from adding too much glare to the book. We then position the light by calling the set() method of the light’s position property. We position it 10 units to the right of the origin, 10 units above the origin, and 10 units in front of the origin. Finally, we add it to the scene by calling scene.add(), passing in the light object.

Our ambient light is simpler to set up. We give it a light grey colour so that it doesn’t wash out the scene, then we add it to the scene using scene.add().

Step 7: Create the Materials

Now comes the part where we make the materials that govern the appearance of the book. In the next step, we’ll wrap these materials around a cuboid shape to create the book object.

Here’s the code to create the materials — again, add it to the end of the window.onload handler:

  // Load the textures (book images)
  var textureLoader = new THREE.TextureLoader();
  var bookCoverTexture = textureLoader.load( 'southern-gems-cover.png' );
  var bookSpineTexture = textureLoader.load( 'southern-gems-spine.png' );
  var bookBackTexture = textureLoader.load( 'southern-gems-back.png' );
  var bookPagesTexture = textureLoader.load( 'southern-gems-pages.png' );
  var bookPagesTopBottomTexture = textureLoader.load( 'southern-gems-pages-topbottom.png' );


  // Use the linear filter for the textures to avoid blurriness
  bookCoverTexture.minFilter
    = bookSpineTexture.minFilter
    = bookBackTexture.minFilter
    = bookPagesTexture.minFilter
    = bookPagesTopBottomTexture.minFilter
    = THREE.LinearFilter;


  // Create the materials

  var bookCover = new THREE.MeshLambertMaterial( { color: 0xffffff, map: bookCoverTexture } );
  var bookSpine = new THREE.MeshLambertMaterial( { color: 0xffffff, map: bookSpineTexture } );
  var bookBack = new THREE.MeshLambertMaterial( { color: 0xffffff, map: bookBackTexture } );
  var bookPages = new THREE.MeshLambertMaterial( { color: 0xffffff, map: bookPagesTexture } );
  var bookPagesTopBottom = new THREE.MeshLambertMaterial( { color: 0xffffff, map: bookPagesTopBottomTexture } );

  var materials = [
    bookPages,          // Right side
    bookSpine,          // Left side
    bookPagesTopBottom, // Top side
    bookPagesTopBottom, // Bottom side
    bookCover,          // Front side
    bookBack            // Back side
  ];

First, the code loads the images for the different sides of the book (these images are known as textures). It then sets all of these textures to use the LinearFilter filter when they’re rendered, instead of the default LinearMipmapLinearFilter. This filter is better for displaying details such as the cover of the book while reducing blurriness.

Now we’ve loaded the textures, we can create the materials based on these textures. We use MeshLambertMaterial, which is nice and fast if you don’t need specular (shiny) highlights on your object, which we don’t in this case.

For each material, we first pass a fallback colour of 0xffffff (white) as the color argument to the constructor, followed by a map argument, which is the texture to use for the material.

Make sure you put all the texture image files in your boxshot folder so that the texture loader can find them.

Once we’ve created all our materials, we group them into an array called materials so that we can apply them to our book object in the next step.

Here’s how the various texture files look — you’ll find all of the image files in the code download if you want to play with them:

The textures that we’ll wrap around the book object. Clockwise from top left: bookBack, bookCover, bookSpine, bookPages, and bookPagesTopBottom.

Step 8: Create the Book

Now that we’ve set up the scene and created the materials, adding the book itself is relatively easy. Here’s the code — add it to the end of your window.onload handler as usual:

  // Create the book and add it to the scene
  var book = new THREE.Mesh( new THREE.BoxGeometry( 7, 10, 1.2, 4, 4, 1 ), materials );
  scene.add( book );

First we create the cuboid shape for our book by creating a THREE.BoxGeometry object. We pass it 6 arguments, as follows:

  • The width, height, and depth of the book (7, 10, and 1.2 units respectively).
  • The number of segments to use across the width, height and depth of the cuboid (4, 4, and 1 respectively). This defines how many smaller shapes make up the overall cuboid. Too few segments, and the book’s texture can look warped. Too many, and the poor computer grinds to a halt!

Once we have our THREE.BoxGeometry object, we use it, along with the materials array we created in Step 7, to create a new THREE.Mesh object, book. This is the object that is actually rendered in the scene.

Three.js maps the materials to the six faces of the cuboid in the following order: right, left, top, bottom, front, and back. It also stretches each material so it fills the entire face.

Finally, we add our book mesh to the scene by calling scene.add().

Step 9: Create the Controls

Remember the OrbitControls library we included at the start? Now it’s time to use that library to create the controls that will allow the user to spin the camera around the book. Here’s the code:

  // Create the orbit controls for the camera
  controls = new THREE.OrbitControls( camera, renderer.domElement );
  controls.enableDamping = true;
  controls.dampingFactor = 0.25;
  controls.enablePan = false;
  controls.enableZoom = false;

First we create the OrbitControls object, passing in the object to control (the camera), as well as the HTML element that will receive the click/touch events (in this case, the canvas element that renders our scene).

Then we set enableDamping to true, which adds some inertia to the spinning action, and set the damping factor to 0.25, which gives the inertia a good feel.

Finally we disable panning and zooming so that the user can only spin the book around, to keep things simple.

Step 10: Start Animating

We’ve now built our whole scene. All that’s left to do is render it! Here’s the code to do just that — again, add all these lines of code inside your window.onload handler, at the end:

  // Begin the animation
  animate();

  /*
    Animate a frame
  */

  function animate() {

    // Update the orbit controls
    controls.update();

    // Render the frame
    renderer.render( scene, camera );

    // Keep the animation going
    requestAnimationFrame( animate );
  }

Since we want the user to be able to rotate the book, we need to render it not once, but continuously. To that end, we first call a function, animate(), to kick-start the animation process. Within animate(), we call the update() method on the OrbitControls object we created earlier, to allow the controls to update the camera position if necessary,

Then, we call the renderer object’s render() method to render the scene, passing in the scene to render and the camera to use (we created these in Step 5). Finally, the function calls requestAnimationFrame(), passing in its own function name, so that the function is called again when a new frame is available, and the animation repeats indefinitely.

The End Result

Here’s the boxshot again. It’s fast, it looks great, and — thanks to the joys of Three.js — it was easy to create, too!

3D Product Boxshot with Three.js
The final scene in all its glory.
If you’ve downloaded the demo files to your own computer, you’ll need to view book.html via your local web server — for example, http://localhost/boxshot/book.html. If you try to open the book.html file directly in your browser, it won’t work. This is because browsers, by default, block JavaScript code from loading other local files for security reasons. This means that the texture images won’t load, and you’ll see a JavaScript error to that effect.

Summary

In this article you’ve explored the basics of the awesome Three.js JavaScript 3D engine. You learned how to set up Three.js, and how to create renderers, scenes, cameras, lights, textures, materials, cuboids and meshes. You also saw how to animate 3D scenes using render() and requestAnimationFrame(), and how to use the OrbitControls library to provide an easy way for the user to spin the camera around.

There’s an awful lot more to Three.js than we’ve seen here — the best way to learn is to explore the examples that come with the library — but hopefully this tutorial has given you a useful introduction to Three.js and the world of JavaScript 3D rendering.

Have fun!

Filed Under: JavaScript Tagged With: 3D graphics, getting started, javascript, requestAnimationFrame, rotate, spin, three.js, Tutorial

Reader Interactions

Comments

  1. Crazyhunk says

    21 August 2011 at 12:15 am

    quite interesting, love the easy interface…

    though I have a small question, I saw the box rotates when I move the mouse, can this be limited to just the a section of the screen or are we talking about the whole page.

    Reply
  2. matt says

    23 August 2011 at 10:50 pm

    @Crazyhunk: Sure, you could grab the canvas element that the renderer uses with renderer.domElement, then attach the mousemove handler to the canvas element instead of the document. Then the box should only rotate when the mouse is moved inside the canvas element.

    Reply
  3. fred says

    13 March 2012 at 12:11 pm

    Nice work there, but i found out the script is consuming too much gpu resources, you can check this easly with gpu-z.
    To work around this issue, change one couple lines
    // animate(); and inside animate function //requestAnimFrame( animate );
    to call the script just put setInterval( animate, 1000 / 60 ); before mouse event, gpu resource will drop from 60% to 0% and when you move the object he goes max 10% -15%.

    keep going, nice tutorial.

    Reply
  4. matt says

    23 March 2012 at 1:18 am

    @fred: Interesting – thanks for the suggestion! 🙂

    Reply
  5. miguelrivero says

    17 July 2013 at 7:37 am

    Great tutorial. It helped me do this.

    2 things:
    1) Why does the cube shows those wires?
    2) Can you extend on how to change the mousemove to a mouseover (in this case the cover div)?

    Thanks!

    Reply
  6. matt says

    12 August 2013 at 11:57 pm

    @miguelrivero:

    1) Try overdraw: true:

    https://github.com/mrdoob/three.js/issues/1044

    2) Why would you want to use a mouseover event instead of mousemove? That would only trigger when the user moved their mouse into the div.

    Reply
  7. georgeCampos says

    3 March 2016 at 4:55 pm

    Great code. On a smartphone the mouse events don’t translate to touch events. If I load jquery and jquerymobile the code stops working. Any guidance or suggestions?

    Reply
    • Matt Doyle says

      14 November 2019 at 2:49 am

      It’s been a while, but as of today the code now works on mobile too 🙂

      Reply
  8. Tobias says

    10 May 2020 at 9:39 am

    Thanks for the description – it looks very good and simple – I will try it directly.

    We would like to let the 3d object rotate on its own right from the start. How can we create that? Do you have experience with it?

    Thanks for your feedback.

    Reply
  9. Marcus says

    2 November 2020 at 4:35 am

    Are the lights working?

    Reply
    • Matt Doyle says

      2 November 2020 at 5:15 am

      I think so, yes.

      Reply
  10. Dan Ware says

    3 March 2021 at 4:27 pm

    The lights will work if you use scene.add( light ).

    There is no function addLight() so it won’t work as shown in the article.

    Reply
    • Matt Doyle says

      4 March 2021 at 12:43 am

      Thanks Dan. addLight() was the old Three.js method, superseded by add(). I had updated the demo, but not the article! Should be fixed up now.

      Reply

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

To include a block of code in your comment, surround it with <pre> ... </pre> tags. You can include smaller code snippets inside some normal text by surrounding them with <code> ... </code> tags.

Allowed tags in comments: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong> <pre> .

Primary Sidebar

Hire Matt!

Matt Doyle headshot

Need a little help with your website? I have over 20 years of web development experience under my belt. Let’s chat!

Matt Doyle - Codeable Expert Certificate

Hire Me Today

Call Me: +61 2 8006 0622

Stay in Touch!

Subscribe to get a quick email whenever I add new articles, free goodies, or special offers. I won’t spam you.

Subscribe

Recent Posts

  • Make a Rotatable 3D Product Boxshot with Three.js
  • Speed Up Your WordPress Website: 11 Simple Steps to a Faster Site
  • Reboot!
  • Wordfence Tutorial: How to Keep Your WordPress Site Safe from Hackers
  • How to Make Awesome-Looking Images for Your Website

Footer

Contact Matt

  • Email Me
  • Call Me: +61 2 8006 0622

Follow Matt

  • E-mail
  • Facebook
  • GitHub
  • LinkedIn
  • Twitter

Copyright © 1996-2023 Elated Communications. All rights reserved.
Affiliate Disclaimer | Privacy Policy | Terms of Use | Service T&C | Credits