<div class="scene">
  <div class="cube">
    <div class="cube__face cube__face--front">front</div>
    <div class="cube__face cube__face--back">back</div>
    <div class="cube__face cube__face--right">right</div>
    <div class="cube__face cube__face--left">left</div>
    <div class="cube__face cube__face--top">top</div>
    <div class="cube__face cube__face--bottom">bottom</div>
  </div>
</div>
<p>
  <label>
    perspective
    <input class="perspective-range" type="range" min="1" max="1000" value="400" data-units="px" />
  </label>
</p>
<p>
  <label>
    perspective-origin x
    <input class="origin-x-range" type="range" min="0" max="100" value="50" data-units="%" />
  </label>
</p>
<p>
  <label>
    perspective-origin y
    <input class="origin-y-range" type="range" min="0" max="100" value="50" data-units="%" />
  </label>
</p>
<p>
  <label>
    Spin cube
    <input class="spin-cube-checkbox" type="checkbox" />
  </label>
</p>
<p>
  <label>
    Backface visible
    <input class="backface-checkbox" type="checkbox" checked />
  </label>
</p>
* { box-sizing: border-box; }

body { font-family: sans-serif; }

.scene {
  width: 200px;
  height: 200px;
  border: 1px solid #CCC;
  margin: 80px;
  perspective: 400px;
}

.cube {
  width: 200px;
  height: 200px;
  position: relative;
  transform-style: preserve-3d;
  transform: translateZ(-100px);
}

.cube.is-spinning {
  animation: spinCube 8s infinite ease-in-out;
}

@keyframes spinCube {
    0% { transform: translateZ(-100px) rotateX(  0deg) rotateY(  0deg); }
  100% { transform: translateZ(-100px) rotateX(360deg) rotateY(360deg); }
}

.cube__face {
  position: absolute;
  width: 200px;
  height: 200px;
  border: 2px solid black;
  line-height: 200px;
  font-size: 40px;
  font-weight: bold;
  color: white;
  text-align: center;
}

.cube__face--front  { background: hsla(  0, 100%, 50%, 0.7); }
.cube__face--right  { background: hsla( 60, 100%, 50%, 0.7); }
.cube__face--back   { background: hsla(120, 100%, 50%, 0.7); }
.cube__face--left   { background: hsla(180, 100%, 50%, 0.7); }
.cube__face--top    { background: hsla(240, 100%, 50%, 0.7); }
.cube__face--bottom { background: hsla(300, 100%, 50%, 0.7); }

.cube__face--front  { transform: rotateY(  0deg) translateZ(100px); }
.cube__face--right  { transform: rotateY( 90deg) translateZ(100px); }
.cube__face--back   { transform: rotateY(180deg) translateZ(100px); }
.cube__face--left   { transform: rotateY(-90deg) translateZ(100px); }
.cube__face--top    { transform: rotateX( 90deg) translateZ(100px); }
.cube__face--bottom { transform: rotateX(-90deg) translateZ(100px); }

.cube.is-backface-hidden .cube__face {
  backface-visibility: hidden;
}
/* ==================== RangeDisplay ==================== */

// displays the value of a range input
// why isn't this in the HTML5 spec?
function RangeDisplay( input ) {
  this.input = input;
  this.output = document.createElement('span');
  this.output.className = 'range-display';
  this.units = this.input.getAttribute('data-units') || '';
  // events
  var onChange = this.update.bind( this );
  this.input.addEventListener( 'change', onChange );
  this.input.addEventListener( 'input', onChange );
  // set initial output
  this.update();
  this.input.parentNode.appendChild( this.output );
}

RangeDisplay.prototype.update = function() {
  this.output.textContent = this.input.value + this.units;
};

/* ==================== init ==================== */

// init RangeDisplays
var ranges = document.querySelectorAll('input[type="range"]');
for ( var i=0; i < ranges.length; i++ ) {
  new RangeDisplay( ranges[i] );
}



var scene = document.querySelector('.scene');
var cube = document.querySelector('.cube');
var originX = 50;
var originY = 50;

function updatePerspectiveOrigin() {
  scene.style.perspectiveOrigin = originX + '% ' + originY + '%';
}

// perspective
var perspectiveRange = document.querySelector('.perspective-range');
var perspectiveDisplay = perspectiveRange.parentNode.querySelector('.range-display');
perspectiveRange.onchange = perspectiveRange.oninput = function() {
  var value = perspectiveRange.value + 'px';
  // set to none at max
  if ( value == '1000px' ) {
    value = 'none';
    perspectiveDisplay.textContent = 'none';
  }
  scene.style.perspective = value;
};
perspectiveRange.onchange();

// origin x
var originXRange = document.querySelector('.origin-x-range');
originXRange.onchange = originXRange.oninput = function() {
  originX = originXRange.value;
  updatePerspectiveOrigin();
};
originXRange.onchange();

// origin y
var originYRange = document.querySelector('.origin-y-range');
originYRange.onchange = originYRange.oninput = function() {
  originY = originYRange.value;
  updatePerspectiveOrigin();
};
originYRange.onchange();

// spin cube
var spinCubeCheckbox = document.querySelector('.spin-cube-checkbox');
spinCubeCheckbox.onchange = function() {
  cube.classList.toggle( 'is-spinning', spinCubeCheckbox.checked );
};
spinCubeCheckbox.onchange();

// backface visibility
var backfaceCheckbox = document.querySelector('.backface-checkbox');
backfaceCheckbox.onchange = function() {
  cube.classList.toggle( 'is-backface-hidden', !backfaceCheckbox.checked );
};

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.