JSFiddle - React, Tailwind, and code Playground

HTML

<script src="https://npmcdn.com/gl-matrix@2.3.2/dist/gl-matrix.js"></script>

  <div id="container">
    <div id="stage">
    </div>
    <div id="circle"></div>
  </div>

CSS

#container {
  width: 400px;
  height: 400px;
  background-color: #eee;
  margin: auto;
}
#stage {
  transform-style: preserve-3d;
  -webkit-transform-style: preserve-3d;
  transform: translate3d(0px, 0px, 0px);
  -webkit-transform: translate3d(0px, 0px, 0px);
}
.line {
  display: block; position: absolute;
  width: 300px; height: 0px;
  border-radius: 1px;
  border: 1px solid #444;
  transform-origin: 1px 1px;
  -webkit-transform-origin: 1px 1px;
  pointer-events: none;
}
#circle {
  width: 200px;
  height: 200px;
  border: 1px solid #ccc;
  border-radius: 50%;
  position: absolute;
  top: 211px;
  left: 50%;
  transform: translate(-50%, -50%);
  pointer-events: none;
}
.line.thick {

  transform-origin: 1px 2px;
  border-top-color: black;
  border-top: 2px solid black;
}
.line.back {
  -webkit-transform-origin: 1px 0.5px;
  border-top-width: 0;
  border-color: rgba(0,0,0,0.15);
  border-style: dotted;
}

JavaScript

var PHI = 1.61803398875;

var requestAnimationFrame = window.requestAnimationFrame || webkitRequestAnimationFrame;

var stage = makeIcosahedron( new Stage() );


var worldSpacePoints = createArray( stage.points.length, vec3.create );

var screenSpacePoints = createArray( stage.points.length, vec2.create );


var stageElem = document.getElementById("stage");

var lineElems = createArray( stage.lines.length, function() {

  var line = document.createElement("div");
  line.className = "line";
  stageElem.appendChild( line );

  return line;

} );

var renderTime = 0;

var worldMatrix = mat4.create();

var normal1 = vec4.create(),
    normal2 = vec4.create(),
    screenPosition = vec3.create();

var shapePosition = vec3.fromValues( 0, 0, 70 );

var shapeQuaternion = quat.create( );
var shapeRotationVelocity = vec3.create();

var screenDist = 1 / Math.tan( 30 * Math.PI / 180 );

var previousTime = Date.now();

setupTrackballControls( document.getElementById("container"), shapeQuaternion, shapePosition );

run();


function run() {

  var time = Date.now();
  renderTime += 0.0004 * (time - previousTime > 50 ? 0 : time - previousTime);
  previousTime = time;

  mat4.fromRotationTranslation( worldMatrix, shapeQuaternion, shapePosition );
  mat4.scale( worldMatrix, worldMatrix, [ 10, 10, 10 ] );

  for ( var i = 0; i < stage.points.length; i++ ) {

    var worldSpace = worldSpacePoints[ i ];
    vec3.transformMat4( worldSpace, stage.points[ i ], worldMatrix );

    var screenSpace = vec2.set( screenSpacePoints[ i ], worldSpace[ 0 ], worldSpace[ 1 ] );
    vec2.scale( screenSpace, screenSpace, screenDist / worldSpace[ 2 ] );
    vec2.multiply( screenSpace, screenSpace, [ 200, 200 ] );
    vec2.add( screenSpace, screenSpace, [ 200, 200 ] );

  }

  for ( var i = 0; i < stage.lines.length; i++ ) {


    var point1 = screenSpacePoints[ stage.lines[ i ][ 0 ] ];
    var point2 = screenSpacePoints[ stage.lines[ i ][ 1 ] ];

    var rot = vec2angle( point1, point2 );
    var len...