Skip to content

Instantly share code, notes, and snippets.

Last active Jun 14, 2018
What would you like to do?
Rotating Cube
license: gpl-3.0
<!DOCTYPE html>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
body {
margin: 0;
canvas {
width: 100%;
height: 100%;
#controls {
position: absolute;
padding: 10px;
color: #fff;
font-family: "Helvetica Neue", sans-serif;
background: rgba(255, 255, 255, .1);
#controls .input-wrapper {
display: inline-block;
text-align: center;
<div id="controls">
<div class="input-wrapper">
<div>Vertical rotation</div>
<input id="x" type="range" value="0" min="0" max="1000">
<div class="input-wrapper">
<div>Horizontal rotation</div>
<input id="y" type="range" value="1000" min="0" max="1000">
<script src=""></script>
var scene = new THREE.Scene();
// There are a few different cameras in three.js. For now, let's use a PerspectiveCamera.
// The first attribute is the field of view.
// FOV is the extent of the scene that is seen on the display at any given moment.
// The value is in degrees.
// The second one is the aspect ratio.
// You almost always want to use the width of the element divided by the height,
// or you'll get the same result as when you play old movies on a widescreen TV -
// the image looks squished.
// The next two attributes are the near and far clipping plane.
// What that means, is that objects further away from the camera than the value of far
// or closer than near won't be rendered.
// You don't have to worry about this now, but you may want to use other values in your apps
// to get better performance.
var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
// Next up is the renderer. This is where the magic happens.
// In addition to the WebGLRenderer we use here, three.js comes with a few others,
// often used as fallbacks for users with older browsers
// or for those who don't have WebGL support for some reason.
// In addition to creating the renderer instance,
// we also need to set the size at which we want it to render our app.
// It's a good idea to use the width and height of the area we want to fill with our app -
// in this case, the width and height of the browser window. For performance intensive apps,
// you can also give setSize smaller values, like window.innerWidth/2 and window.innerHeight/2,
// which will make the app render at half size.
// If you wish to keep the size of your app but render it at a lower resolution,
// you can do so by calling setSize with false as updateStyle (the third argument).
// For example, setSize(window.innerWidth/2, window.innerHeight/2, false) will render your app
// at half resolution, given that your <canvas> has 100% width and height.
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
// Last but not least, we add the renderer element to our HTML document.
// This is a <canvas> element the renderer uses to display the scene to us.
// To create a cube, we need a BoxGeometry.
// This is an object that contains all the points (vertices) and fill (faces) of the cube.
// We'll explore this more in the future.
var geometry = new THREE.BoxGeometry(2, 2, 2);
// In addition to the geometry, we need a material to color it.
// Three.js comes with several materials, but we'll stick to the MeshBasicMaterial for now.
// All materials take an object of properties which will be applied to them.
// To keep things very simple, we only supply a color attribute of 0x00ff00, which is green.
// This works the same way that colors work in CSS or Photoshop (hex colors).
var material = new THREE.MeshBasicMaterial({ color: "steelblue" });
// The third thing we need is a Mesh.
// A mesh is an object that takes a geometry, and applies a material to it,
// which we then can insert to our scene, and move freely around.
var cube = new THREE.Mesh(geometry, material);
// By default, when we call scene.add(), the thing we add will be added to the coordinates (0,0,0).
// This would cause both the camera and the cube to be inside each other.
// To avoid this, we simply move the camera out a bit.
camera.position.z = 5;
// From
// If you want to render a wireframe of a given geometry, you can now use this pattern:
// WireframeGeometry will render all edges. EdgesGeometry will render the hard edges only.
var geo = new THREE.EdgesGeometry(geometry); // or WireframeGeometry(geometry)
var mat = new THREE.LineBasicMaterial({ color: "white", linewidth: 1 });
var wireframe = new THREE.LineSegments(geo, mat);
// This will create a loop that causes the renderer to draw the scene 60 times per second.
// This will be run every frame (60 times per second),
// and give the cube (and wireframe) a nice rotation animation.
// Basically, anything you want to move or change while the app is running
// has to go through the animate loop.
// You can of course call other functions from there,
// so that you don't end up with a animate function that's hundreds of lines.
function animate() {
// If you're new to writing games in the browser,
// you might say "why don't we just create a setInterval?"
// The thing is - we could, but requestAnimationFrame has a number of advantages.
// Perhaps the most important one is that it pauses
// hen the user navigates to another browser tab,
// hence not wasting their precious processing power and battery life.
var x = Math.sqrt(document.getElementById("x").value) / 1000;
var y = Math.sqrt(document.getElementById("y").value) / 1000;
cube.rotation.x += x;
cube.rotation.y += y;
wireframe.rotation.x += x;
wireframe.rotation.y += y;
renderer.render(scene, camera);
window.onmousemove = () => {
var mouse_x = event.clientX,
mouse_y = event.clientY,
controls_bounds = document.getElementById("controls").getBoundingClientRect();
if (mouse_x > controls_bounds.width || mouse_y > controls_bounds.height){
document.getElementById("x").value = mouse_y / window.innerHeight * 1000;
document.getElementById("y").value = mouse_x / window.innerWidth * 1000;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment