<div id="scene-container" role="img" aria-label="Memphis kinetic typo extravaganza">
</div>
<div>
  <a class="credits fade-in" href="https://codepen.io/ScavengerFrontend" target="blank">★ art & code ★ <br/>by Anna <br/> Scavenger ↗</a>
</div>

<script type="x-shader/x-vertex" id="matcap-vs">

  varying vec2 vN;

	void main() {

		vec3 e = normalize(vec3(modelViewMatrix * vec4(position, 1.0)));
		vec3 n = normalize(normalMatrix * normal);

		vec3 r = reflect(e, n);
		float m = 2.0 * sqrt(pow(r.x, 2.0) + pow( r.y, 2.0) + pow(r.z + 1.0, 2.0));
		vN = r.xy / m + 0.5;

		gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);

	}

</script>

<script type="x-shader/x-fragment" id="matcap-fs">

  uniform sampler2D tMatCap;

	varying vec2 vN;

	void main() {
		
		vec3 base = texture2D(tMatCap, vN).rgb;
		gl_FragColor = vec4(base, 1.0);

	}
	
</script>
body {
  margin: 0;
  padding: 0;
  overflow: hidden;
  background: #cfd1d1;
  text-align: right;
  font-size: 1rem;
  touch-action: pan-x;
  font-family: Arial;
}

#scene-container {
  position: absolute;
  width: 100vw;
  height: 100vh;
  background: rgb(193,193,193);
  background: linear-gradient(180deg, rgba(193,193,193,1) 0%, rgba(228,195,206,1) 47%, rgba(255,116,166,1) 100%);
}

.credits {
  position: absolute;
  bottom: 0;
  right: 0;
  text-transform: none;
  text-decoration: none;
  text-align: left;
  color: black;
  padding: 0.35rem 0.5rem;
  margin: 0.75rem 0.75rem;
  font-size: 0.75rem;
  border: 1.5px solid black;
  transition: background 200ms ease-in;
  border-radius: 0.2rem;
  opacity: 0.0;
}

a:hover {
  background: rgba(255, 251, 215, 0.5);
}

.fade-in {
  animation: fadeIn 2s ease-out 4.5s forwards;
  -moz-animation: fadeIn 2s ease-out 4.5s forwards;
  -webkit-animation: fadeIn 2s ease-out 4.5s forwards;
}

@keyframes fadeIn {
  
  0% { opacity: 0; }
  100% { opacity: 1; }   
}

@-moz-keyframes fadeIn {
  
  0% { color: rgba(0, 0, 0, 0.1); }
  100% { color: rgba(0, 0, 0, 0.85); }   
}

@-webkit-keyframes fadeIn {
  
  0% { color: rgba(0, 0, 0, 0.1); }
  100% { color: rgba(0, 0, 0, 0.85); } 
  
}

/* @media (max-width: 860px) {
  a.fade-in {
    display: none;
  }
} */
/* 

  ★ Move your mouse over to experience magic ★
  
  Memphis 3D Kinetic Typography
  
  Thanks to @prisoner849 for helping me with the marshmallow geometry.
  
  Thanks to Davide Prati for the Palm Generator:
  https://davideprati.com/projects/palm-generator.html
  
  art & code by
  Anna Scavenger, March 2020
  https://twitter.com/ouchpixels
  
  License: You can remix, adapt, and build upon my code non-commercially.

*/

import { BufferGeometryUtils } from "https://unpkg.com/three@0.120.0/examples/jsm/utils/BufferGeometryUtils.js";

'use strict';

let container, camera, scene, renderer;
let palm;
let letterMLeft, letterMRight, letterM2, sphereM2, sphere2M2;
let coneM, letterE, marshmallowH, cylinderH2, cylinderH3, cylinderH4, letterI, letterS, halfTorusS, halfTorusS2, halfTorusS3, halfTorusS4, discS2;
let halfTorus, halfTorus2, halfTorus3, torusP, sphereP;

let mouseX = 0;
let mouseY = 0;

const a = Math.PI / 2;

const matcapURL = "https://s3-us-west-2.amazonaws.com/s.cdpn.io/911157/matcapGold_256_optimized.jpg";

const isMobile = /(Android|iPhone|iOS|iPod|iPad)/i.test(navigator.userAgent);

window.onload = function() {
  
  init();
  render();
  
};

function init() {
  
  container = document.querySelector("#scene-container");
  scene = new THREE.Scene();

  createCamera();
  createLights();
  createRenderer();
  createGeometries();
  createMeshes();
  
  document.addEventListener("mousemove", onMouseMove, false);
  document.addEventListener("touchmove", onTouch, false);
  window.addEventListener("resize", onWindowResize);
  
}

function createCamera() {

  camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 100);
  camera.position.set(0, 2, 21);
  camera.lookAt(0, 0.25, 0);
  camera.aspect = window.innerWidth / window.innerHeight;
  const cameraZ = 21;

  if (camera.aspect < 1 && camera.aspect > 0.75) {
    
    camera.position.z = cameraZ * 1.5;
    
  } else if (camera.aspect < 0.75) {
    
    camera.position.z = cameraZ * 2.4;
    
  } else {
    
    camera.position.z = cameraZ;
    
  }

}

function createLights() {

  const ambientLight = new THREE.HemisphereLight(0xddeeff, 0x202020, 2.25);
  ambientLight.position.set(-10, 200, -1000);
  
  const mainLight = new THREE.DirectionalLight(0xffffff, 1.0);
  mainLight.position.set(-50, 100, 10);
  
  const mainLight2 = new THREE.DirectionalLight(0xFF002d, 8);
  mainLight2.position.set(50, 10, 10);
  mainLight2.target.position.set(1, 100, 0);
  
  scene.add(ambientLight, mainLight, mainLight2);
  
}

function createGeometries() {
  
  const cylinder = new THREE.CylinderGeometry( 0.75, 0.75, 3, 26, 20 );
  const cylinderWide = new THREE.CylinderGeometry( 1.1, 1.1, 0.75, 35, 5 );
  const cylinderThin = new THREE.CylinderBufferGeometry( 0.35, 0.35, 3, 20 );
  const disc = new THREE.CylinderGeometry( 0.75, 0.75, 0.25, 32 );
  disc.applyMatrix4(new THREE.Matrix4().makeTranslation( 0, 0.125, 0 ));
  const sphere = new THREE.SphereBufferGeometry( 0.75, 22, 22 );
  const halfSphere = new THREE.SphereBufferGeometry( 0.75, 15, 15, 0, Math.PI );
  const torus = new THREE.TorusBufferGeometry( 1.25, 0.25, 16, 40 );
  const halfTorus = new THREE.TorusBufferGeometry( 1.25, 0.35, 16, 20, Math.PI );
  const cone = new THREE.ConeBufferGeometry( 1.25, 2.25, 38 );
  cone.applyMatrix4(new THREE.Matrix4().makeTranslation( 0, 1.125, 0 ));
  const boxFlat = new THREE.BoxBufferGeometry( 1.25, 0.35, 1.25 );
  
  return {
    
    cylinder,
    cylinderThin,
    cylinderWide,
    disc,
    sphere,
    halfSphere,
    halfTorus,
    torus,
    cone,
    boxFlat
    
  };

}

function createMaterials() {
  
  const white = new THREE.MeshStandardMaterial({
    color: 0xffffff,
    roughness: 0.9,
    metalness: 0.2,
    flatShading: false,
    side: THREE.DoubleSide
  });
  white.color.convertSRGBToLinear();
  
  const black = new THREE.MeshStandardMaterial({
    
    color: 0x000000,
    roughness: 0.2,
    metalness: 0.6,
    flatShading: false,
    side: THREE.DoubleSide
    
	});
  black.color.convertSRGBToLinear();

	const blue = new THREE.MeshStandardMaterial({
    
    color: 0x8fcbea,
    roughness: 0.9,
    metalness: 0.1,
    flatShading: false,
    side: THREE.DoubleSide
    
	});
	blue.color.convertSRGBToLinear();

  const pink = new THREE.MeshPhongMaterial({
    
    color: 0xffc0dd
    
  }); 
  pink.color.convertSRGBToLinear();
  
  const green2 = new THREE.MeshStandardMaterial({
    color: 0xbffbcb, 
    roughness: 0.1,
    metalness: 0.3,
    flatShading: true,
    side: THREE.DoubleSide
	});
  green2.color.convertSRGBToLinear();
  
  const textureLoader = new THREE.TextureLoader();
  const matcapTex = textureLoader.load(matcapURL);
  const matcapGold = new THREE.ShaderMaterial({
    
    transparent: false,
    side: THREE.DoubleSide,
    uniforms: {
      tMatCap: {
        type: "t",
        value: matcapTex
      }
    },
    vertexShader: document.querySelector("#matcap-vs").textContent,
    fragmentShader: document.querySelector("#matcap-fs").textContent,
    flatShading: false
    
  });

  return {
    
    white,
    pink,
    green2,
    blue,
    black,
    matcapGold
    
  };

}

function createMeshes() {

  const geometries = createGeometries();
  const materials = createMaterials();
  
  palm = createPalm();
  scene.add(palm);

  const pixelMat = new THREE.MeshStandardMaterial({
      color: 0xffffff,
      roughness: 0.1,
      metalness: 0.25,
      vertexColors: THREE.FaceColors,
      flatShading: false
  });
  
  // letter M
  const letterM = new THREE.Group();
  
  const halfSphere = new THREE.Mesh(geometries.halfSphere, materials.black);
  halfSphere.position.y = 3.25;
  halfSphere.rotation.x = -Math.PI / 2;
  halfSphere.matrixAutoUpdate = false;
  halfSphere.updateMatrix();
  
  const cylinder = new THREE.Mesh(geometries.cylinder, pixelMat);
  colorVertices(geometries.cylinder);
  cylinder.position.y = 1.75;
  
  const disc = new THREE.Mesh(geometries.disc, materials.black);
  disc.matrixAutoUpdate = false;
  disc.updateMatrix();
  
  const sphere = new THREE.Mesh(geometries.sphere, materials.blue);
  sphere.position.y = -0.5;
  sphere.matrixAutoUpdate = false;
  sphere.updateMatrix();
  
  const disc2 = new THREE.Mesh(geometries.disc, materials.matcapGold);
  disc2.position.y = -1.25;
  disc2.matrixAutoUpdate = false;
  disc2.updateMatrix();

  letterMLeft = new THREE.Group();
  letterMLeft.add(halfSphere, cylinder, disc, disc2, sphere);
  letterMLeft.position.x = -2;
  
  letterMRight = letterMLeft.clone();
  letterMRight.position.x = 2;
  
  coneM = new THREE.Mesh(geometries.cone, materials.matcapGold);
  coneM.rotation.x = Math.PI;
  coneM.position.y = 3.25;
  
  letterM.add(letterMLeft, coneM, letterMRight);
  letterM.scale.set(1.05, 1.05, 1.05);
  
  // letter E 
  letterE = new THREE.Group();
  
  const halfTorusE = new THREE.Mesh(geometries.halfTorus, materials.white);
  
  const halfTorus2E = halfTorusE.clone();
  halfTorus2E.rotation.z = Math.PI / 2;
  
  const halfTorus3E = halfTorusE.clone();
  halfTorus3E.rotation.z = Math.PI / 2;
  halfTorus3E.position.y = -2.5;
  
  const halfTorus4E = halfTorus3E.clone();
  halfTorus4E.rotation.z = Math.PI;

  const discE = new THREE.Mesh(geometries.disc, materials.matcapGold);
  discE.position.set(1.25, -0.1, 0);
  discE.scale.set(0.45, 0.45, 0.45);
  
  const disc2E = discE.clone();
  disc2E.position.y = -2.5;
  
  const halfSphereE = new THREE.Mesh(geometries.halfSphere, materials.black);
  halfSphereE.position.set(0, -1.25, 0);
  halfSphereE.scale.set(0.45, 0.45, 0.45);
  halfSphereE.rotation.y = Math.PI / 2;

  letterE.add(halfTorusE, halfTorus2E, halfTorus3E, halfTorus4E);
  letterE.add(discE, disc2E, halfSphereE);
  
  // letter M2
  letterM2 = new THREE.Group();
  
  halfTorus = new THREE.Mesh(geometries.halfTorus, materials.matcapGold);
  
  halfTorus2 = halfTorus.clone();
  halfTorus2.position.x = 2.5;
  
  halfTorus3 = halfTorus.clone();
  halfTorus3.position.x = 5;
  
  const cylinderThinM = new THREE.Mesh(geometries.cylinderThin, materials.matcapGold);
  cylinderThinM.position.set(1.25, -1.5, 0);
  
  const cylinderThinM2 = cylinderThinM.clone();
  cylinderThinM2.position.x = 3.75;
  
  const cylinderThinM3 = cylinderThinM.clone();
  cylinderThinM3.position.x = 6.25;
  
  const halfTorus4 = halfTorus.clone();
  halfTorus4.position.set(7.5, -3, 0);
  halfTorus4.rotation.x = Math.PI;
  halfTorus4.matrixAutoUpdate = false;
  halfTorus4.updateMatrix();
  
  sphereM2 = new THREE.Mesh(geometries.sphere, materials.black);
  sphereM2.position.set(2.5, 4, 0);
  sphereM2.scale.set(1.1, 1.1, 1.1);
  
  sphere2M2 = new THREE.Mesh(geometries.sphere, materials.green2);
  sphere2M2.scale.set(0.7, 0.7, 0.7);
  sphere2M2.position.set(8.75, -2.5, 0);
  
  const halfSphereM2 = new THREE.Mesh(geometries.halfSphere, materials.black);
  halfSphereM2.rotation.x = -Math.PI / 2;
  halfSphereM2.scale.set(0.45, 0.45, 0.45);
  halfSphereM2.position.set(1.25, 0, 0);
  
  const halfSphere2M2 = halfSphereM2.clone();
  halfSphere2M2.position.set(3.75, 0, 0);
  halfSphere2M2.matrixAutoUpdate = false;
  halfSphere2M2.updateMatrix();
  
  const halfSphere3M2 = halfSphereM2.clone();
  halfSphere3M2.position.set(6.25, 0, 0);
  halfSphere3M2.matrixAutoUpdate = false;
  halfSphere3M2.updateMatrix();
  
  letterM2.add(halfTorus, halfTorus2, halfTorus3, sphereM2, sphere2M2);
  letterM2.add(cylinderThinM, cylinderThinM2, cylinderThinM3, halfTorus4);
  letterM2.add(halfSphereM2, halfSphere2M2, halfSphere3M2);
  letterM2.scale.set(0.8, 0.8, 0.8);
  
  // letter P
  const letterP = new THREE.Group();
  
  torusP = new THREE.Mesh(geometries.torus, materials.matcapGold);
  
  const cylinderThinP = new THREE.Mesh(geometries.cylinderThin, materials.matcapGold);
  cylinderThinP.position.set(-1.25, -1.5, 0);
  cylinderThinP.matrixAutoUpdate = false;
  cylinderThinP.updateMatrix();
 
  const boxFlatP = new THREE.Mesh(geometries.boxFlat, materials.black);
  boxFlatP.position.set(-1.25, -3.1, 0);
  boxFlatP.matrixAutoUpdate = false;
  boxFlatP.updateMatrix();
  
  sphereP = new THREE.Mesh(geometries.sphere, materials.white);
  sphereP.position.set(0, -2.75, 0);
  sphereP.scale.set(0.9, 0.9, 0.9);
  
  letterP.add(torusP, sphereP);
  letterP.add(cylinderThinP, boxFlatP);
  letterP.scale.set(1.1, 1.1, 1.1);
  
  // letter H
  const letterH = new THREE.Group();
  
  marshmallowH = new THREE.Mesh(createMarshmallow(), [materials.blue, materials.pink, materials.blue, materials.black]);
  marshmallowH.scale.set(0.8, 0.65, 0.8);
  
  const discH = new THREE.Mesh(geometries.disc, materials.matcapGold);
  discH.scale.set(1.5, 1.5, 1.5);
  discH.position.y = 2.75;
  
  const discH2 = discH.clone();
  discH2.position.y = -2;
  
  const letterHLeft = new THREE.Group();
  letterHLeft.add(marshmallowH, discH, discH2);
  
  const letterHRight = new THREE.Group();
  
  const cylinderVert = new THREE.Mesh(geometries.cylinderThin, materials.matcapGold);
  cylinderVert.scale.set(0.35, 0.75, 0.35);
  cylinderVert.position.set(0, 4, 0);
  cylinderVert.matrixAutoUpdate = false;
  cylinderVert.updateMatrix();
  
  cylinderH2 = new THREE.Mesh(geometries.cylinder, pixelMat);
  cylinderH2.scale.set(1.1, 1.1, 1.1);
  cylinderH2.position.y = 1.25;
  
  cylinderH3 = new THREE.Mesh(geometries.cylinderWide, pixelMat);
  colorVertices(geometries.cylinderWide);
  cylinderH3.position.y = -0.75;
  
  cylinderH4 = cylinderH3.clone();
  cylinderH4.scale.set(1.2, 1.2, 1.2); 
  cylinderH4.position.y = -1.55;
  
  const discH3 = discH.clone();
  discH3.position.set(0, 2.75, 0);

  letterHRight.add(cylinderH2, cylinderH3, cylinderH4, discH3, cylinderVert);
  letterHRight.position.x = 3.5;
  
  const cylinderHoriz = new THREE.Mesh(geometries.cylinderThin, materials.matcapGold);
  cylinderHoriz.rotation.z = Math.PI / 2;
  cylinderHoriz.position.set( 1.8, 0.5, -0.5 );
  cylinderHoriz.scale.set(1.7, 0.75, 1.7);
  cylinderHoriz.matrixAutoUpdate = false;
  cylinderHoriz.updateMatrix();
  
  letterH.add(letterHLeft, letterHRight, cylinderHoriz);
  
  // letter I 
  letterI = letterHLeft.clone();
  letterI.children[1].material = materials.white;
  letterI.children[1].matrixAutoUpdate = false;
  letterI.children[1].updateMatrix();
  
  letterI.children[2].material = materials.white;
  letterI.children[2].matrixAutoUpdate = false;
  letterI.children[2].updateMatrix();
  
  // letter S 
  letterS = new THREE.Group();
  
  halfTorusS = new THREE.Mesh(geometries.halfTorus, materials.green2);
  halfTorusS.rotation.z = Math.PI / 2;
  halfTorusS2 = halfTorusS.clone();
  halfTorusS2.rotation.z = 0;
  halfTorusS3 = halfTorusS.clone();
  halfTorusS3.rotation.z = - Math.PI / 2;
  halfTorusS3.position.y = -2.5;
  halfTorusS4 = halfTorusS.clone();
  halfTorusS4.rotation.z = Math.PI;
  halfTorusS4.position.y = -2.5;
  
  const discS = new THREE.Mesh(geometries.disc, materials.matcapGold);
  discS.scale.set(0.75, 1, 0.75);
  discS.position.set(1.25, -0.225, 0);
  
  discS2 = discS.clone();
  discS2.position.set(-1.25, -2.5, 0);
  
  const halfSphereS = new THREE.Mesh(geometries.halfSphere, materials.matcapGold);
  halfSphereS.scale.set(0.45, 0.45, 0.45);
  halfSphereS.rotation.y = -Math.PI / 2;
  halfSphereS.position.y = -1.25;
  halfSphereS.matrixAutoUpdate = false;
  halfSphereS.updateMatrix();
  
  const halfSphere2S = halfSphereS.clone();
  halfSphere2S.rotation.y = 0;
  halfSphere2S.position.set(1.25, -2.5, 0);
  halfSphere2S.matrixAutoUpdate = false;
  halfSphere2S.updateMatrix();
  
  letterS.add(halfTorusS, halfTorusS2, halfTorusS3, halfTorusS4, discS2);
  letterS.add(halfSphereS, halfSphere2S, discS);
  
  // position all letters
  letterM.position.set(-6.75, 1.25, 0);
  letterE.position.set(-1, 3.75, 0);
  letterM2.position.set(3.5, 2.75, 0);
  letterP.position.set(-7.9, -3.5, 0);
  letterH.position.set(-4.5, -5, 0);
  letterI.position.set(2.25, -5, 0);
  letterS.position.set(6.5, -3, 0);
  palm.position.set(2.25, -2.75, 0);

  scene.add(letterM, letterE, letterM2, letterP, letterH, letterI, letterS);

}

function createMarshmallow() {

  const c = new THREE.CylinderBufferGeometry(0.5, 0.5, 7, 34, 34);

  c.translate(0.5, 1, 0.5);
  let pos = c.attributes.position;
  let vec3 = new THREE.Vector3();
  let axis = new THREE.Vector3(0, 1, 0);

  for (let i = 0; i < pos.count; i++) {
    
    vec3.fromBufferAttribute(pos, i);
    vec3.applyAxisAngle(axis, (vec3.y / c.parameters.height*1) * Math.PI * 2);
    pos.setXYZ(i, vec3.x, vec3.y, vec3.z);
    
  }

  let geoms = [];
  geoms.push(c);
  let mat = new THREE.Matrix4();
  
  for (let i = 1; i < 4; i++) {
    
    let g = c.clone();
    mat.makeRotationAxis(axis, i * Math.PI * 0.5);
    mat.applyToBufferAttribute(g.attributes.position);
    geoms.push(g);
    
  }

  let m = BufferGeometryUtils.mergeBufferGeometries(geoms, true);
  return m;
  
}

function animate() {
  
	requestAnimationFrame(animate);
  
}

requestAnimationFrame(animate);

function createRenderer() {

  renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
  renderer.setClearColor(0x000000, 0); 
  renderer.setSize(container.clientWidth, container.clientHeight);
  renderer.setPixelRatio(window.devicePixelRatio > 1.5 ? 1.5 : 1.2);
  
  renderer.gammaFactor = 2.2;
  renderer.outputEncoding = THREE.GammaEncoding;
  renderer.physicallyCorrectLights = true;

  container.appendChild(renderer.domElement);

}

function render() {
  
  requestAnimationFrame(render);
  renderer.render(scene, camera);
  update();
  
}

function update() {
  
  // letter M
  letterMLeft.children[1].rotation.y = 2 * a * mouseX;
  letterMRight.children[1].rotation.y = - 2 * a * mouseX;
  coneM.rotation.x = 2 * a + a * mouseX;
  
  // letter E
  letterE.rotation.y = - a * mouseX;
  
  // letter M2
  halfTorus.position.y = 0 + Math.abs(2 * mouseX);
  halfTorus2.position.y = 0 + Math.abs(2 * mouseX);
  (Math.abs(mouseX) < 0.075 ? halfTorus2.rotation.x = 0 : halfTorus2.rotation.x = 3.14 * mouseX);
  halfTorus3.position.y = 0 + Math.abs(2 * mouseX);
  sphereM2.position.y = 4 - Math.abs(2.1 * mouseX);
  sphere2M2.rotation.y = 0 + mouseX;

  // letter P
  torusP.rotation.x = - a * mouseX;
  torusP.rotation.z = - a * mouseX;
  torusP.scale.set(1 - Math.abs(0.2 * mouseX),1 - Math.abs(0.2 * mouseX),1 - Math.abs(0.2 * mouseX));
  sphereP.position.y = -2.75 + Math.abs(2.75 * mouseX);
  sphereP.scale.set(0.9 + Math.abs(0.1 * mouseX), 0.9 + Math.abs(0.1 * mouseX),0.9 + Math.abs(0.1 * mouseX));
  
  // letter H
  marshmallowH.rotation.y = a * mouseX;
  cylinderH2.rotation.y = - a * mouseX;
  cylinderH3.rotation.y = a * mouseX;
  cylinderH4.rotation.y = - a * mouseX;
  
  // letter I
  palm.rotation.y += 0.001 * Math.PI * 2;
  letterI.children[0].rotation.y = - a * mouseX;
  
  // letter S
  halfTorusS.rotation.z = a - a * Math.abs(mouseX);
  halfTorusS2.rotation.z = - a * Math.abs(mouseX);
  halfTorusS3.rotation.z = - a + -a * (mouseX);
  discS2.rotation.x = a / 3 * Math.abs(mouseX);

}

function onMouseMove(e) {
  
  mouseX = (e.clientX / window.innerWidth) * 2 - 1;
  mouseY = -(e.clientY / window.innerHeight) * 2 + 1;
  
}

function onTouch(e) {
  
  let x = e.changedTouches[0].clientX;
  let y = e.changedTouches[0].clientY;

  mouseX = (x / window.innerWidth) * 2 - 1;
  mouseY = (y / window.innerWidth) * 2 - 1;
  
}

function onWindowResize() {
  
  camera.aspect = container.clientWidth / container.clientHeight;
  camera.updateProjectionMatrix();
  renderer.setSize(container.clientWidth, container.clientHeight);
  
}

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/three.js/r120/three.min.js
  2. https://s3-us-west-2.amazonaws.com/s.cdpn.io/911157/kineticMemphis.js