animating one million letters

by icodeforlove

HTML

<script src="http://www.html5rocks.com/en/tutorials/webgl/million_letters/Three.js"></script>
<script src="http://www.html5rocks.com/en/tutorials/webgl/million_letters/book.js"></script>
<script src="http://www.html5rocks.com/en/tutorials/webgl/million_letters/dat.gui.min.js"></script>
<script id="vertex" type="text/x-glsl-vert">
      varying float vZ;
      uniform float time;
      uniform float effectAmount;
      varying vec2 vUv;

      mat3 rotateAngleAxisMatrix(float angle, vec3 axis) {
        float c = cos(angle);
        float s = sin(angle);
        float t = 1.0 - c;
        axis = normalize(axis);
        float x = axis.x, y = axis.y, z = axis.z;
        return mat3(
          t*x*x + c,    t*x*y + s*z,  t*x*z - s*y,
          t*x*y - s*z,  t*y*y + c,    t*y*z + s*x,
          t*x*z + s*y,  t*y*z - s*x,  t*z*z + c
        );
      }

      vec3 rotateAngleAxis(float angle, vec3 axis, vec3 v) {
        return rotateAngleAxisMatrix(angle, axis) * v;
      }

      void main() {
        float idx = floor(position.y/1.1)*80.0 + floor(position.x/1.1);
        vec3 corner = vec3(floor(position.x/1.1)*1.1, floor(position.y/1.1)*1.1, 0.0);
        vec3 mid = corner + vec3(0.5, 0.5, 0.0);
        vec3 rpos = rotateAngleAxis(idx+time, vec3(mod(idx,16.0), -8.0+mod(idx,15.0), 1.0), position - mid) + mid;
        vec4 fpos = vec4( mix(position,rpos,effectAmount), 1.0 );
        fpos.x += -35.0;
        fpos.z += ((sin(idx+time*2.0)))*4.2*effectAmount;
        fpos.y += ((cos(idx+time*2.0)))*4.2*effectAmount;
        vec4 mvPosition = modelViewMatrix * fpos;
        mvPosition.y += 10.0*sin(time*0.5+mvPosition.x/25.0)*effectAmount;
        mvPosition.x -= 10.0*cos(time*0.5+mvPosition.y/25.0)*effectAmount;
        vec4 p = projectionMatrix * mvPosition;
        vUv = uv;
        vZ = p.z;
        gl_Position = p;
      }
    </script>
    <script id="fragment" type="text/x-glsl-frag">
      varying float vZ;
      varying vec2 vUv;
      uniform float time;
     ...

JavaScript

var renderer = new THREE.WebGLRenderer({antialias: true});
  renderer.setClearColor( 0xffffff );
  document.body.appendChild(renderer.domElement);

  var camera = new THREE.PerspectiveCamera(45,1,4,40000);
  camera.setLens(35);

  window.onresize = function() {
    renderer.setSize(window.innerWidth, window.innerHeight);
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
  };
  window.onresize();

  var radius = 60;

  var scene = new THREE.Scene();
  camera.position.z = radius;
  scene.add(camera);

  var fontSize = 16;
  var lettersPerSide = 16;
  var c = document.createElement('canvas');
  c.width = c.height = fontSize*lettersPerSide;
  var ctx = c.getContext('2d');
  ctx.font = fontSize+'px Monospace';
  var i=0;

  for (var y=0; y<lettersPerSide; y++) {
    for (var x=0; x<lettersPerSide; x++,i++) {
      var ch = String.fromCharCode(i);
      ctx.fillText(ch, x*fontSize, -(8/32)*fontSize+(y+1)*fontSize);
    }
  }

  var tex = new THREE.Texture(c);
  tex.flipY = false;
  tex.needsUpdate = true;

  var mat = new THREE.MeshBasicMaterial({map: tex});
  mat.transparent = true;

  var geo = new THREE.Geometry();
  var str = BOOK;

  var j=0, ln=0;

  for (i=0; i<str.length; i++) {
    var code = str.charCodeAt(i);
    var cx = code % lettersPerSide;
    var cy = Math.floor(code / lettersPerSide);
    var v,t;
    geo.vertices.push(
      new THREE.Vector3( j*1.1+0.05, ln*1.1+0.05, 0 ),
      new THREE.Vector3( j*1.1+1.05, ln*1.1+0.05, 0 ),
      new THREE.Vector3( j*1.1+1.05, ln*1.1+1.05, 0 ),
      new THREE.Vector3( j*1.1+0.05, ln*1.1+1.05, 0 )
    );
    var face = new THREE.Face3(i*4+0, i*4+1, i*4+2);
    geo.faces.push(face);
    face = new THREE.Face3(i*4+0, i*4+2, i*4+3);
    geo.faces.push(face);
    var ox=(cx+0.05)/lettersPerSide, oy=(cy+0.05)/lettersPerSide, off=0.9/lettersPerSide;
    var sz = lettersPerSide*fontSize;
    geo.faceVertexUvs[0].push([
      new THREE.Vector2( ox, oy+off ),
      new...