JSFiddle - React, Tailwind, and code Playground

by Michael Prosser

HTML

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<div class="sphere">
<div class="sphere-inner">
<div class="sphere-inner-inner">
  <div class="sphere-inner-top-10"></div>
  <div class="sphere-inner-top-9"></div>
  <div class="sphere-inner-top-8"></div>
  <div class="sphere-inner-top-7"></div>
  <div class="sphere-inner-top-6"></div>
  <div class="sphere-inner-top-5"></div>
  <div class="sphere-inner-top-4"></div>
  <div class="sphere-inner-top-3"></div>
  <div class="sphere-inner-top-2"></div>
  <div class="sphere-inner-top-1"></div>
  <div class="sphere-inner-middle"></div>
  <div class="sphere-inner-bottom-1"></div>
  <div class="sphere-inner-bottom-2"></div>
  <div class="sphere-inner-bottom-3"></div>
  <div class="sphere-inner-bottom-4"></div>
  <div class="sphere-inner-bottom-5"></div>
  <div class="sphere-inner-bottom-6"></div>
  <div class="sphere-inner-bottom-7"></div>
  <div class="sphere-inner-bottom-8"></div>
  <div class="sphere-inner-bottom-9"></div>
  <div class="sphere-inner-bottom-10"></div>
</div>
</div>
</div>

CSS

html{
  height:100%;
}
body{
  height:100%;
  perspective:500px;
  background-color:#000;
}
.sphere{
  position:absolute;
  top:50%;
  left:50%;
  width:0px;
  height:0px;
  transform-style:preserve-3d;
  /*animation: rolling infinite linear 10s;*/
}
.sphere-inner{
  position:absolute;
  top:50%;
  left:50%;
  width:0px;
  height:0px;
  transform-style:preserve-3d;
  /*animation: rolling infinite linear 10s;*/
}
.sphere-inner-inner{
  position:absolute;
  top:50%;
  left:50%;
  width:0px;
  height:0px;
  transform-style:preserve-3d;
  /*animation: rolling infinite linear 10s;*/
}
@keyframes rolling{
  0%{ transform: rotateX(30deg) rotateY(0deg); }
  100%{ transform: rotateX(30deg) rotateY(360deg); }
}
.sphere-inner-inner>div{
  position:absolute;
  width:100px;
  height:100px;
  margin-top:-50px;
  margin-left:-50px;
  border-radius:50%;
  background-color:rgba(255,0,0,0.1);
  
}
.sphere-inner-top-1{ transform:translateZ(5px) scale(.98); }
.sphere-inner-top-2{ transform:translateZ(10px) scale(.96); }
.sphere-inner-top-3{ transform:translateZ(15px) scale(.94); }
.sphere-inner-top-4{ transform:translateZ(20px) scale(.90); }
.sphere-inner-top-5{ transform:translateZ(25px) scale(.84); }
.sphere-inner-top-6{ transform:translateZ(30px) scale(.76); }
.sphere-inner-top-7{ transform:translateZ(35px) scale(.68); }
.sphere-inner-top-8{ transform:translateZ(40px) scale(.56); }
.sphere-inner-top-9{ transform:translateZ(45px) scale(.42); }
.sphere-inner-top-10{ transform:translateZ(50px) scale(.18); }
.sphere-inner-bottom-1{ transform:translateZ(-5px) scale(.98); }
.sphere-inner-bottom-2{ transform:translateZ(-10px) scale(.96); }
.sphere-inner-bottom-3{ transform:translateZ(-15px) scale(.94); }
.sphere-inner-bottom-4{ transform:translateZ(-20px) scale(.90); }
.sphere-inner-bottom-5{ transform:translateZ(-25px) scale(.84); }
.sphere-inner-bottom-6{ transform:translateZ(-30px) scale(.76); }
.sphere-inner-bottom-7{ transform:translateZ(-35px) scale(.68); }
.sphere-inner-bottom-8{...

JavaScript

var sphere = {
	
  radius: 50,
  circumfrance: 0,
  rotation_to_pixels: 0,
  
  element_left: $('.sphere'),
  element_right: $('.sphere'),
  
  element_left_inner: $('.sphere-inner'),
  element_right_inner: $('.sphere-inner'),
  
  element_left_inner_inner: $('.sphere-inner-inner'),
  element_right_inner_inner: $('.sphere-inner-inner'),
  
  direction_x: '',
  direction_z: '',
  
  speed: 4,
  
  position: { rx:0, ry:0, rz:0, x:0, y:0, z:0 },
	last_position: { rx:0, ry:0, rz:0, x:0, y:0, z:0 },
	
  to_lowest_degree:function(num){
  
  	if(num < 0){
    
    	while(num < 0){
      
      	num+=360;
      
      }
    	
      return num;
      
    } else if(num > 0){
    
    	while(num > 360){
      
      	num-=360;
      
      }
      
      return num;
    
    } else {
    
    	return 0;
    
    }
  
  },
  nextPosition: function(x,y,z,rx,ry,rz,d){
			
		var angle_rx_inradians = rx * Math.PI / 180;
		var angle_ry_inradians = ry * Math.PI / 180;

		var z2 = z + Math.cos(angle_ry_inradians) * (d*-1);
		var x2 = x + Math.sin(angle_ry_inradians) * (d*-1);
		var y2 = y + Math.tan(angle_rx_inradians) * (d*-1);

		return { x: x2, y: y2, z: z2, rx: rx, ry: ry, rz: rz };
		
	},
  angleBetweenPoints: function(p1,p2){

		return Math.atan2(p2.z - p1.z, p2.x - p1.x) * 180 / Math.PI;
		
	},
  
  render: function(){
  	
    sphere.position.rx = sphere.position.z*sphere.rotation_to_pixels*-1;
    sphere.position.ry = sphere.position.x*sphere.rotation_to_pixels*-1;
  	
    switch(sphere.direction_z){
    
    	case 'forwards':
      sphere.position.z-=sphere.speed;
      break;
      
    	case 'backwards':
      sphere.position.z+=sphere.speed;
      break;
      
    }
    
    switch(sphere.direction_x){
    
      case 'left':
      sphere.position.x-=sphere.speed;
      break;
      
      case 'right':
      sphere.position.x+=sphere.speed;
      break;
    
    }
    
    sphere.element_left.css('transform','translateX(' + sphere.position.x + 'px) translateY(' +...