title: Threejs/Physijs Game MMO Character controlls
body: <p>I'm working on a third person character control for a game i'm developing. I'm happy with the results so far. The character controls have a lot of neat features like: if an object is in front of the camera it will move forward so you can still see the character, however the camera stutters horribly when I rotate it to the side and then turn my player away from it. I uploaded a test on JSFiddle: <a href="http://jsfiddle.net/nA8SV/" rel="nofollow">http://jsfiddle.net/nA8SV/</a> I have only tested this in chrome, and for some reason the results part doesn't get the keypresses until you click on the white space bordering the canvas on that frame.</p>
  
  <p><strong>[also i started chrome with "--disable-web-security" to ignore the cross origin]</strong></p>
  
  <p>But once you click the page the key presses work. The controls are a modified version of the orbital controls. So you can left click and rotate the view. Additionally you can use the wasd keys to move around and the camera view should return behind the player when you are moving/rotating.</p>
  
  <p>I apologize for the buggyness this was very difficult to get working on JSFiddle.
  (But the rotation bug is happening so it at least reproduces that.)</p>
  
  <p>Basically I'm trying to get my camera rotation back behind my character, so i have some code that fixes the rotation on line 250, but the camera stutters as the character moves.</p>
  
  <p>Here are my theories I think the camera overall jerkyness has something to do with the physics simulation bouncing the player around slightly, but I'm not sure what to do to solve this, any help would be appreciated.</p>
  
  <p>here is the code for completeness but I would recommend the JSFiddle link, I'ts much easier to see it work.</p>
  
  <pre><code>THREE.PlayerControls = function (anchor, scene, player, camera, domElement) {
  
  this.walking = false;
  this.occ = false;
  this.scene = scene;
  this.occLastZoom = 0;
  this.jumpRelease = true;
  this.jumping = false;
  this.falling = false;
  this.moving = false;
  this.turning = false;
  this.anchor = anchor;
  this.player = player;
  this.camera = camera;
  this.camera.position.set(0, 8.25, -20);
  this.domElement = (domElement !== undefined) ? domElement : document;
  
  this.anchor.add(this.camera);
  
  // API
  this.enabled = true;
  
  this.center = new THREE.Vector3(0, 4, 0);
  
  this.userZoom = true;
  this.userZoomSpeed = 2.0;
  
  this.userRotate = true;
  this.userRotateSpeed = 1.0;
  
  this.minPolarAngle = 0; // radians
  this.maxPolarAngle = Math.PI; // radians
  
  this.minDistance = 2;
  this.maxDistance = 30;
  
  this.keys = {
      LEFT: 65,
      STRAFFLEFT: 81,
      UP: 87,
      RIGHT: 68,
      STRAFFRIGHT: 69,
      DOWN: 83,
      JUMP: 32,
      SLASH: 191
  };
  
  // internals
  var scope = this;
  
  var EPS = 0.000001;
  var PIXELS_PER_ROUND = 1800;
  
  var rotateStart = new THREE.Vector2();
  var rotateEnd = new THREE.Vector2();
  var rotateDelta = new THREE.Vector2();
  
  var zoomStart = new THREE.Vector2();
  var zoomEnd = new THREE.Vector2();
  var zoomDelta = new THREE.Vector2();
  
  var phiDelta = 0;
  var thetaDelta = 0;
  var scale = 1;
  
  var lastPosition = new THREE.Vector3();
  
  var STATE = {
      NONE: -1,
      ROTATE: 0,
      ZOOM: 1
  };
  var state = STATE.NONE;
  var key_state = [];
  
  // events
  var changeEvent = {
      type: 'change'
  };
  
  
  this.rotateLeft = function (angle) {
      thetaDelta -= angle;
  };
  
  this.rotateRight = function (angle) {
      thetaDelta += angle;
  };
  
  this.rotateUp = function (angle) {
      phiDelta -= angle;
  };
  
  this.rotateDown = function (angle) {
      phiDelta += angle;
  };
  
  this.zoomIn = function (zoomScale) {
      if (zoomScale === undefined) {
          zoomScale = getZoomScale();
      }
      scale /= zoomScale;
  };
  
  this.zoomOut = function (zoomScale) {
      if (zoomScale === undefined) {
          zoomScale = getZoomScale();
      }
      scale *= zoomScale;
  };
  
  this.update = function (delta) {
      // detect falling
      if (this.scene.children.length &gt; 0) {
          var originPoint = this.anchor.position.clone();
          var ray = new THREE.Raycaster(originPoint, new THREE.Vector3(0, -1, 0));
          var collisionResults = ray.intersectObjects(this.scene.children.filter(function (child) {
              return child.occ;
          }));
          if (collisionResults.length &gt; 0) {
              if (collisionResults[0].distance &lt; 1.25 &amp;&amp; this.falling) {
                  this.falling = false;
                  this.jumping = false;
              } else if (collisionResults[0].distance &gt; 2 + (this.jumping ? 1 : 0) &amp;&amp; !this.falling) {
                  this.falling = true;
              }
          }
      }
  
      // handle movement
      if (!this.falling) {
          if (key_state.indexOf(this.keys.JUMP) &gt; -1 &amp;&amp; this.jumpRelease &amp;&amp; !this.jumping) {
              // jump
              var lv = this.anchor.getLinearVelocity();
              this.anchor.setLinearVelocity(new THREE.Vector3(lv.x, 15, lv.z));
              this.jumpRelease = false;
              this.jumping = true;
              //jump
          } else if (!this.jumping) {
              // move
              if (key_state.indexOf(this.keys.UP) &gt; -1) {
  
                  var rotation_matrix = new THREE.Matrix4().extractRotation(this.anchor.matrix);
  
                  var speed = this.walking ? 2.5 : 10;
                  var force_vector;
  
                  // straffing?
                  if (key_state.indexOf(this.keys.STRAFFLEFT) &gt; -1 &amp;&amp; key_state.indexOf(this.keys.STRAFFRIGHT) &lt; 0) {
                      force_vector = new THREE.Vector3((2 * speed / 3), 0, (2 * speed / 3)).applyMatrix4(rotation_matrix);
                      this.player.rotation.set(0, Math.PI / 4, 0);
                  } else if (key_state.indexOf(this.keys.STRAFFRIGHT) &gt; -1) {
                      force_vector = new THREE.Vector3((-2 * speed / 3), 0, (2 * speed / 3)).applyMatrix4(rotation_matrix);
                      this.player.rotation.set(0, -Math.PI / 4, 0);
                  } else {
                      force_vector = new THREE.Vector3(0, 0, speed).applyMatrix4(rotation_matrix);
                      this.player.rotation.set(0, 0, 0);
                  }
  
                  this.anchor.setLinearVelocity(force_vector);
                  this.moving = true;
  
                  // forward
              } else if (key_state.indexOf(this.keys.DOWN) &gt; -1) {
                  var rotation_matrix = new THREE.Matrix4().extractRotation(this.anchor.matrix);
  
                  var speed = this.walking ? -2.5 : -5;
                  var force_vector;
  
                  // straffing?
                  if (key_state.indexOf(this.keys.STRAFFLEFT) &gt; -1 &amp;&amp; key_state.indexOf(this.keys.STRAFFRIGHT) &lt; 0) {
                      force_vector = new THREE.Vector3((-2 * speed / 3), 0, (2 * speed / 3)).applyMatrix4(rotation_matrix);
                      this.player.rotation.set(0, -Math.PI / 4, 0);
                  } else if (key_state.indexOf(this.keys.STRAFFRIGHT) &gt; -1) {
                      force_vector = new THREE.Vector3((2 * speed / 3), 0, (2 * speed / 3)).applyMatrix4(rotation_matrix);
                      this.player.rotation.set(0, Math.PI / 4, 0);
                  } else {
                      force_vector = new THREE.Vector3(0, 0, speed).applyMatrix4(rotation_matrix);
                      this.player.rotation.set(0, 0, 0);
                  }
  
                  this.anchor.setLinearVelocity(force_vector);
                  this.moving = true;
  
                  //back
              } else if (key_state.indexOf(this.keys.STRAFFLEFT) &gt; -1) {
                  var rotation_matrix = new THREE.Matrix4().extractRotation(this.anchor.matrix);
  
                  var speed = this.walking ? 2.5 : 10;
                  var force_vector = new THREE.Vector3(speed, 0, 0).applyMatrix4(rotation_matrix);
                  this.player.rotation.set(0, Math.PI / 2, 0);
  
                  this.anchor.setLinearVelocity(force_vector);
                  this.moving = true;
  
                  //straff
              } else if (key_state.indexOf(this.keys.STRAFFRIGHT) &gt; -1) {
                  var rotation_matrix = new THREE.Matrix4().extractRotation(this.anchor.matrix);
  
                  var speed = this.walking ? 2.5 : 10;
                  var force_vector = new THREE.Vector3(-speed, 0, 0).applyMatrix4(rotation_matrix);
                  this.player.rotation.set(0, -Math.PI / 2, 0);
  
                  this.anchor.setLinearVelocity(force_vector);
                  this.moving = true;
  
                  //straff
              } else if (this.moving) {
                  this.player.rotation.set(0, 0, 0);
                  this.anchor.setLinearVelocity(new THREE.Vector3(0, 0, 0));
                  this.moving = false;
              }
  
              //turn
              if (key_state.indexOf(this.keys.LEFT) &gt; -1 &amp;&amp; key_state.indexOf(this.keys.RIGHT) &lt; 0) {
                  this.anchor.setAngularVelocity(new THREE.Vector3(0, 1.5, 0));
                  this.turning = true;
                  //turning
              } else if (key_state.indexOf(this.keys.RIGHT) &gt; -1) {
                  this.anchor.setAngularVelocity(new THREE.Vector3(0, -1.5, 0));
                  this.turning = true;
                  //turning
              } else if (this.turning) {
                  this.anchor.setAngularVelocity(new THREE.Vector3(0, 0, 0));
                  this.turning = false;
              }
  
              //idle
          }
  
          if (key_state.indexOf(this.keys.JUMP) == -1) {
              this.jumpRelease = true;
          }
      } else {
          //falling
      }
  
      var position = this.camera.position;
      var offset = position.clone().sub(this.center);
  
      // angle from z-axis around y-axis
      var theta = Math.atan2(offset.x, offset.z);
  
      // angle from y-axis
      var phi = Math.atan2(Math.sqrt(offset.x * offset.x + offset.z * offset.z), offset.y);
  
      theta += thetaDelta;
      phi += phiDelta;
  
      if ((this.moving || this.turning) &amp;&amp; state != STATE.ROTATE) {
          // fix camera rotation
          if (theta &lt; 0) theta -= Math.max(delta, (-1 * Math.PI) + theta);
          else theta += Math.min(delta, Math.PI - theta);
  
          // fix pitch (should be an option or it could get anoying)
          //phi = 9*Math.PI/24;
      }
  
      // restrict phi to be between desired limits
      phi = Math.max(this.minPolarAngle, Math.min(this.maxPolarAngle, phi));
  
      // restrict phi to be betwee EPS and PI-EPS
      phi = Math.max(EPS, Math.min(Math.PI - EPS, phi));
  
      var radius;
      if (this.occ) {
          this.occLastZoom = Math.max(this.minDistance, Math.min(this.maxDistance, this.occLastZoom * scale));
          radius = this.occLastZoom;
      } else {
          radius = offset.length() * scale;
      }
  
      // restrict radius to be between desired limits
      radius = Math.max(this.minDistance, Math.min(this.maxDistance, radius));
  
      // check for objects infront of camera
      var projector = new THREE.Projector();
      var vector = new THREE.Vector3(0, 0, 1);
      projector.unprojectVector(vector, camera);
      var point = new THREE.Vector3(this.anchor.position.x + this.center.x, this.anchor.position.y + this.center.y, this.anchor.position.z + this.center.z);
      var vec = camera.position.clone().sub(vector).normalize()
  
      var checkray = new THREE.Raycaster(point, vec, this.minDistance, this.maxDistance);
      var checkcollisionResults = checkray.intersectObjects(this.scene.children.filter(function (child) {
          return child.occ;
      }));
      if (checkcollisionResults.length &gt; 0) {
          var min = radius;
          for (var i = 0; i &lt; checkcollisionResults.length; i++) {
              if (min &gt; checkcollisionResults[i].distance) min = checkcollisionResults[i].distance;
          }
          if (min &lt; radius) {
              if (!this.occ) {
                  this.occ = true;
                  this.occLastZoom = radius;
              }
              radius = min;
          } else {
              this.occ = false;
          }
      }
  
      offset.x = radius * Math.sin(phi) * Math.sin(theta);
      offset.y = radius * Math.cos(phi);
      offset.z = radius * Math.sin(phi) * Math.cos(theta);
  
      if (radius &lt; 5) {
          this.player.material.opacity = Math.max(0, radius / 5.0);
          this.center.y = 4 + ((5 - radius) / 2.5);
      } else {
          if (this.player.material.opacity != 1.0) {
              this.player.material.opacity = 1.0;
              this.center.y = 4;
          }
      }
  
      position.copy(this.center).add(offset);
      this.camera.lookAt(this.center);
  
      thetaDelta = 0;
      phiDelta = 0;
      scale = 1;
  
      if (lastPosition.distanceTo(this.camera.position) &gt; 0) {
          this.dispatchEvent(changeEvent);
          lastPosition.copy(this.camera.position);
      }
  };
  
  function roundTothree(num) {
      return +(Math.round(num + "e+3") + "e-3");
  }
  
  function getZoomScale() {
      return Math.pow(0.95, scope.userZoomSpeed);
  }
  
  function onMouseDown(event) {
      if (scope.enabled === false) return;
      if (scope.userRotate === false) return;
  
      event.preventDefault();
  
      if (state === STATE.NONE) {
          if (event.button === 0) state = STATE.ROTATE;
      }
  
      if (state === STATE.ROTATE) {
          rotateStart.set(event.clientX, event.clientY);
      }
  
      document.addEventListener('mousemove', onMouseMove, false);
      document.addEventListener('mouseup', onMouseUp, false);
  }
  
  function onMouseMove(event) {
      if (scope.enabled === false) return;
      event.preventDefault();
  
      if (state === STATE.ROTATE) {
          rotateEnd.set(event.clientX, event.clientY);
          rotateDelta.subVectors(rotateEnd, rotateStart);
          scope.rotateLeft(2 * Math.PI * rotateDelta.x / PIXELS_PER_ROUND * scope.userRotateSpeed);
          scope.rotateUp(2 * Math.PI * rotateDelta.y / PIXELS_PER_ROUND * scope.userRotateSpeed);
          rotateStart.copy(rotateEnd);
      } else if (state === STATE.ZOOM) {
          zoomEnd.set(event.clientX, event.clientY);
          zoomDelta.subVectors(zoomEnd, zoomStart);
          if (zoomDelta.y &gt; 0) {
              scope.zoomIn();
          } else {
              scope.zoomOut();
          }
          zoomStart.copy(zoomEnd);
      }
  }
  
  function onMouseUp(event) {
      if (scope.enabled === false) return;
      if (scope.userRotate === false) return;
  
      document.removeEventListener('mousemove', onMouseMove, false);
      document.removeEventListener('mouseup', onMouseUp, false);
  
      state = STATE.NONE;
  }
  
  function onMouseWheel(event) {
      if (scope.enabled === false) return;
      if (scope.userZoom === false) return;
  
      var delta = 0;
  
      if (event.wheelDelta) { // WebKit / Opera / Explorer 9
          delta = event.wheelDelta;
      } else if (event.detail) { // Firefox
          delta = -event.detail;
      }
  
      if (delta &gt; 0) {
          scope.zoomOut();
      } else {
          scope.zoomIn();
      }
  }
  
  function onKeyDown(event) {
      console.log('onKeyDown')
      if (scope.enabled === false) return;
      switch (event.keyCode) {
          case scope.keys.UP:
              var index = key_state.indexOf(scope.keys.UP);
              if (index == -1) key_state.push(scope.keys.UP);
              break;
          case scope.keys.DOWN:
              var index = key_state.indexOf(scope.keys.DOWN);
              if (index == -1) key_state.push(scope.keys.DOWN);
              break;
          case scope.keys.LEFT:
              var index = key_state.indexOf(scope.keys.LEFT);
              if (index == -1) key_state.push(scope.keys.LEFT);
              break;
          case scope.keys.STRAFFLEFT:
              var index = key_state.indexOf(scope.keys.STRAFFLEFT);
              if (index == -1) key_state.push(scope.keys.STRAFFLEFT);
              break;
          case scope.keys.RIGHT:
              var index = key_state.indexOf(scope.keys.RIGHT);
              if (index == -1) key_state.push(scope.keys.RIGHT);
              break;
          case scope.keys.STRAFFRIGHT:
              var index = key_state.indexOf(scope.keys.STRAFFRIGHT);
              if (index == -1) key_state.push(scope.keys.STRAFFRIGHT);
              break;
          case scope.keys.JUMP:
              var index = key_state.indexOf(scope.keys.JUMP);
              if (index == -1) key_state.push(scope.keys.JUMP);
              break;
      }
  }
  
  function onKeyUp(event) {
      switch (event.keyCode) {
          case scope.keys.UP:
              var index = key_state.indexOf(scope.keys.UP);
              if (index &gt; -1) key_state.splice(index, 1);
              break;
          case scope.keys.DOWN:
              var index = key_state.indexOf(scope.keys.DOWN);
              if (index &gt; -1) key_state.splice(index, 1);
              break;
          case scope.keys.LEFT:
              var index = key_state.indexOf(scope.keys.LEFT);
              if (index &gt; -1) key_state.splice(index, 1);
              break;
          case scope.keys.STRAFFLEFT:
              var index = key_state.indexOf(scope.keys.STRAFFLEFT);
              if (index &gt; -1) key_state.splice(index, 1);
              break;
          case scope.keys.RIGHT:
              var index = key_state.indexOf(scope.keys.RIGHT);
              if (index &gt; -1) key_state.splice(index, 1);
              break;
          case scope.keys.STRAFFRIGHT:
              var index = key_state.indexOf(scope.keys.STRAFFRIGHT);
              if (index &gt; -1) key_state.splice(index, 1);
              break;
          case scope.keys.JUMP:
              var index = key_state.indexOf(scope.keys.JUMP);
              if (index &gt; -1) key_state.splice(index, 1);
              break;
          case scope.keys.SLASH:
              scope.walking = !scope.walking;
              break;
  
      }
  }
  
  this.domElement.addEventListener('contextmenu', function (event) {
      event.preventDefault();
  }, false);
  this.domElement.addEventListener('mousedown', onMouseDown, false);
  this.domElement.addEventListener('mousewheel', onMouseWheel, false);
  this.domElement.addEventListener('DOMMouseScroll', onMouseWheel, false); // firefox
  window.addEventListener('keydown', onKeyDown, false);
  window.addEventListener('keyup', onKeyUp, false);
  };
  
  THREE.PlayerControls.prototype = Object.create(THREE.EventDispatcher.prototype);
  
  
  // end player controlls
  Physijs.scripts.worker = 'https://rawgithub.com/chandlerprall/Physijs/master/physijs_worker.js';
  Physijs.scripts.ammo = 'http://chandlerprall.github.io/Physijs/examples/js/ammo.js';
  
  // standard global variables
  var container, scene, camera, renderer, controls;
  //var keyboard = new THREEx.KeyboardState();
  var clock = new THREE.Clock();
  
  // MAIN //
  window.onload = function() {
  console.log('loaded')
  
  // SCENE //
  scene = new Physijs.Scene();
  scene.setGravity(new THREE.Vector3(0, -32, 0));
  scene.addEventListener(
      'update',
  
  function () {
      scene.simulate();
  });
  
  // CAMERA //
  var SCREEN_WIDTH = window.innerWidth,
      SCREEN_HEIGHT = window.innerHeight;
  var VIEW_ANGLE = 45,
      ASPECT = SCREEN_WIDTH / SCREEN_HEIGHT,
      NEAR = 1,
      FAR = 1000;
  camera = new THREE.PerspectiveCamera(VIEW_ANGLE, ASPECT, NEAR, FAR);
  
  // RENDERER //
  renderer = new THREE.WebGLRenderer({
      antialias: true
  });
  renderer.shadowMapEnabled = true;
  // to antialias the shadow
  renderer.shadowMapSoft = true;
  
  renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT);
  
  container = document.getElementById('container');
  container.appendChild(renderer.domElement);
  
  // EVENTS //
  //THREEx.WindowResize(renderer, camera);
  
  // LIGHT //
  var hemiLight = new THREE.HemisphereLight(0xffffff, 0xffffff, 0.6);
  hemiLight.color.setHSL(0.6, 1, 0.6);
  hemiLight.groundColor.setHSL(0.095, 1, 0.75);
  hemiLight.position.set(0, 500, 0);
  scene.add(hemiLight);
  
  var light = new THREE.DirectionalLight(0xffffff, 1);
  light.color.setHSL(0.1, 1, 0.95);
  light.position.set(-1, 1.75, 1);
  light.position.multiplyScalar(50);
  light.castShadow = true;
  light.shadowMapWidth = 2048;
  light.shadowMapHeight = 2048;
  light.shadowDarkness = 0.5;
  var d = 50;
  light.shadowCameraLeft = -d;
  light.shadowCameraRight = d;
  light.shadowCameraTop = d;
  light.shadowCameraBottom = -d;
  light.shadowCameraFar = 3500;
  light.shadowBias = -0.0001;
  light.shadowDarkness = 0.35;
  scene.add(light);
  
  // GEOMETRY //
  var checkerboard = new THREE.ImageUtils.loadTexture('http://www.cns.nyu.edu/lcv/texture/artificial-periodic/checkerboard.o.jpg');
  checkerboard.wrapS = checkerboard.wrapT = THREE.RepeatWrapping;
  checkerboard.repeat.set(4, 4);
  
  var checkerboard2 = new THREE.ImageUtils.loadTexture('http://www.cns.nyu.edu/lcv/texture/artificial-periodic/checkerboard.o.jpg');
  
  var cubeMaterial = Physijs.createMaterial(
  new THREE.MeshLambertMaterial({
      map: checkerboard2
  }),
  1.0, // high friction
  0.0 // low restitution
  );
  var cubeGeometry = new THREE.CubeGeometry(10, 5, 10, 1, 1, 1);
  var cube = new Physijs.BoxMesh(
  cubeGeometry,
  cubeMaterial,
  1);
  
  cube.position.set(-10, 1, -10);
  cube.castShadow = true;
  cube.receiveShadow = true;
  cube.occ = true;
  scene.add(cube);
  
  
  var cubeMaterial2 = Physijs.createMaterial(
  new THREE.MeshLambertMaterial({
      map: checkerboard2
  }),
  1.0, // high friction
  0.0 // low restitution
  );
  var cubeGeometry2 = new THREE.CubeGeometry(10, 5, 10, 1, 1, 1);
  var cube2 = new Physijs.BoxMesh(
  cubeGeometry2,
  cubeMaterial2,
  1);
  
  cube2.position.set(-10, 7, -1);
  cube2.castShadow = true;
  cube2.receiveShadow = true;
  cube2.occ = true;
  scene.add(cube2);
  
  var cubeMaterial3 = Physijs.createMaterial(
  new THREE.MeshLambertMaterial({
      map: checkerboard2
  }),
  1.0, // high friction
  0.0 // low restitution
  );
  var cubeGeometry3 = new THREE.CubeGeometry(10, 5, 10, 1, 1, 1);
  var cube3 = new Physijs.BoxMesh(
  cubeGeometry3,
  cubeMaterial3,
  1);
  
  cube3.position.set(-10, 13, 8);
  cube3.castShadow = true;
  cube3.receiveShadow = true;
  cube3.occ = true;
  scene.add(cube3);
  
  var cone = new Physijs.ConeMesh(
  new THREE.CylinderGeometry(0, 5, 4, 30, 30, true),
  Physijs.createMaterial(
  new THREE.MeshLambertMaterial({
      map: checkerboard2
  }),
  1.0, // high friction
  0.0 // low restitution
  ),
  0);
  cone.position.set(0, 2, 0);
  scene.castShadow = true;
  scene.receiveShadow = true;
  cone.occ = true;
  scene.add(cone);
  
  
  // FLOOR //
  var floorMaterial = new THREE.MeshLambertMaterial({
      map: checkerboard
  });
  var floorGeometry = new THREE.PlaneGeometry(100, 100, 1, 1);
  var floor = new Physijs.PlaneMesh(floorGeometry, floorMaterial);
  floor.rotation.x = -Math.PI / 2;
  floor.castShadow = false;
  floor.receiveShadow = true;
  floor.occ = true;
  scene.add(floor);
  
  // SKY //
  var skyBoxGeometry = new THREE.CubeGeometry( 1000, 1000, 1000 );
  var skyBox = new THREE.Mesh(skyBoxGeometry, new THREE.MeshLambertMaterial({
      color: '#3333bb'
  }));
  scene.add(skyBox);
  
  // fog must be added to scene before first render
  scene.fog = new THREE.FogExp2(0x999999, 0.001);
  
  
  var bounding = new Physijs.SphereMesh(
  new THREE.SphereGeometry(0.75, 4, 4),
  Physijs.createMaterial(
  new THREE.MeshBasicMaterial({
      color: '#ff0000'
  }),
  1.0, // high friction
  0.0 // low restitution
  ),
  0.1);
  
  var player = new THREE.Mesh(
  new THREE.CubeGeometry(1, 6, 1, 1, 1, 1),
  new THREE.MeshLambertMaterial({
      color: '#00ff00'
  }),
  1);
  player.position.set(0, 3, 0);
  
  bounding.position.set(10, 0.75, -10);
  bounding.add(player);
  
  scene.add(bounding);
  bounding.setAngularFactor(new THREE.Vector3(0, 0, 0));
  controls = new THREE.PlayerControls(bounding, scene, player, camera, renderer.domElement);
  
  // animation loop / game loop
  scene.simulate();
  animate();
  };
  
  function animate() {
  requestAnimationFrame(animate);
  render();
  update();
  }
  
  function update() {
  // delta = change in time since last call (in seconds)
  var delta = clock.getDelta();
  THREE.AnimationHandler.update(delta);
  if (controls) controls.update(delta);
  }
  
  function render() {
      renderer.render(scene, camera);
  }
  </code></pre>
  
  <p>Thank you!!!</p>