diff --git a/src/assets/avatars/Bot_SkinnedWithAnim.glb b/src/assets/avatars/Bot_SkinnedWithAnim.glb
new file mode 100644
index 0000000000000000000000000000000000000000..192bbed19788cb4c5cb71a3f23da428173766948
Binary files /dev/null and b/src/assets/avatars/Bot_SkinnedWithAnim.glb differ
diff --git a/src/components/body-controller.js b/src/components/body-controller.js
deleted file mode 100644
index 05afec55951aff8ba8108bd6ee95e8212d9c8b95..0000000000000000000000000000000000000000
--- a/src/components/body-controller.js
+++ /dev/null
@@ -1,49 +0,0 @@
-AFRAME.registerComponent("body-controller", {
-  schema: {
-    camera: { type: "selector", default: "[camera]" },
-    rotationSpeed: { default: 0.07 },
-    eyeNeckOffset: { type: "vec3" },
-    neckHeight: { type: "number" }
-  },
-
-  init() {
-    this.targetAngleEuler = new THREE.Euler();
-    this.targetAngle = new THREE.Quaternion();
-    this.cameraPositionMatrix = new THREE.Matrix4();
-    const offset = this.data.eyeNeckOffset;
-    this.eyeNeckTransformMatrix = new THREE.Matrix4().makeTranslation(
-      offset.x,
-      offset.y,
-      offset.z
-    );
-    this.neckTransformMatrix = new THREE.Matrix4().makeTranslation(
-      0,
-      this.data.neckHeight,
-      0
-    );
-    this.bodyPositionMatrix = new THREE.Matrix4();
-    this.bodyPositionVector = new THREE.Vector3();
-  },
-
-  tick(time, dt) {
-    const object3D = this.el.object3D;
-    const cameraObject3D = this.data.camera.object3D;
-
-    // Set Rotation
-    this.targetAngleEuler.set(0, cameraObject3D.rotation.y, 0);
-    this.targetAngle.setFromEuler(this.targetAngleEuler);
-    object3D.quaternion.slerp(this.targetAngle, this.data.rotationSpeed);
-    const object3DRotation = object3D.rotation;
-    this.el.setAttribute("rotation", {
-      x: object3DRotation.x * THREE.Math.RAD2DEG,
-      y: object3DRotation.y * THREE.Math.RAD2DEG,
-      z: object3DRotation.z * THREE.Math.RAD2DEG
-    });
-
-    //Set Position
-    this.bodyPositionMatrix.copy(cameraObject3D.matrix);
-    this.bodyPositionMatrix.multiply(this.eyeNeckTransformMatrix);
-    this.bodyPositionVector.setFromMatrixPosition(this.bodyPositionMatrix);
-    this.el.setAttribute("position", this.bodyPositionVector);
-  }
-});
diff --git a/src/components/bone-visibility.js b/src/components/bone-visibility.js
index 17933edcd6ef9d218fd7f048322cb356fbf22d52..5c2e2f2f235f3665f0e9366c3af31be4de5a3d13 100644
--- a/src/components/bone-visibility.js
+++ b/src/components/bone-visibility.js
@@ -1,13 +1,17 @@
 AFRAME.registerComponent("bone-visibility", {
+  schema: {
+    type: "selector"
+  },
   tick() {
-    const visible = this.el.getAttribute("visible");
+    const targetEl = this.data;
+    const visible = this.getAttribute("visible");
 
     if (this.lastVisible !== visible) {
       if (visible) {
-        this.el.object3D.scale.set(1, 1, 1);
+        this.targetEl.object3D.scale.set(1, 1, 1);
       } else {
         // Three.js doesn't like updating matrices with 0 scale, so we set it to a near zero number.
-        this.el.object3D.scale.set(0.00000001, 0.00000001, 0.00000001);
+        this.targetEl.object3D.scale.set(0.00000001, 0.00000001, 0.00000001);
       }
 
       this.lastVisible = visible;
diff --git a/src/components/ik-controller.js b/src/components/ik-controller.js
new file mode 100644
index 0000000000000000000000000000000000000000..58627a064b8cd03a73abc0cc22d3e66e3f98916b
--- /dev/null
+++ b/src/components/ik-controller.js
@@ -0,0 +1,193 @@
+const { Vector3, Quaternion, Matrix4, Euler } = THREE;
+const RAD2DEG = THREE.Math.RAD2DEG;
+
+const degRotation = { x: 0, y: 0, z: 0 };
+function setEntityFromMatrix(entity, matrix) {
+  const object3D = entity.object3D;
+  object3D.position.setFromMatrixPosition(matrix);
+  object3D.rotation.setFromRotationMatrix(matrix);
+  entity.setAttribute("position", object3D.position);
+  const { x, y, z } = object3D.rotation;
+  degRotation.x = x * RAD2DEG;
+  degRotation.y = y * RAD2DEG;
+  degRotation.z = z * RAD2DEG;
+  entity.setAttribute("rotation", degRotation);
+}
+
+function updateEntityPosition(entity) {
+  const object3D = entity.object3D;
+  entity.setAttribute("position", object3D.position);
+}
+
+function updateEntityRotation(entity) {
+  const object3D = entity.object3D;
+  const { x, y, z } = object3D.rotation;
+  degRotation.x = x * RAD2DEG;
+  degRotation.y = y * RAD2DEG;
+  degRotation.z = z * RAD2DEG;
+  entity.setAttribute("rotation", degRotation);
+}
+
+AFRAME.registerComponent("ik-root", {
+  schema: {
+    camera: { type: "string", default: ".camera" },
+    leftController: { type: "string", default: ".left-controller" },
+    rightController: { type: "string", default: ".right-controller" }
+  },
+  update(oldData) {
+    let updated = false;
+
+    if (this.data.camera !== oldData.camera) {
+      this.camera = this.el.querySelector(this.data.camera);
+      updated = true;
+    }
+
+    if (this.data.leftController !== oldData.leftController) {
+      this.leftController = this.el.querySelector(this.data.leftController);
+      updated = true;
+    }
+
+    if (this.data.rightController !== oldData.rightController) {
+      this.rightController = this.el.querySelector(this.data.rightController);
+      updated = true;
+    }
+
+    if (updated) {
+      this.el.querySelector("[ik-controller]").components["ik-controller"].updateIkRoot(this);
+    }
+  }
+});
+
+AFRAME.registerComponent("ik-controller", {
+  schema: {
+    leftEye: { type: "string", default: ".LeftEye" },
+    rightEye: { type: "string", default: ".RightEye" },
+    middleEye: { type: "string", default: ".middle-eye" },
+    head: { type: "string", default: ".Head" },
+    neck: { type: "string", default: ".Neck" },
+    leftHand: { type: "string", default: ".LeftHand" },
+    rightHand: { type: "string", default: ".RightHand" },
+    chest: { type: "string", default: ".Chest" },
+    hips: { type: "string", default: ".Hips" },
+    rotationSpeed: { default: 5 },
+    debug: { type: "boolean", default: true }
+  },
+
+  init() {
+    this.flipY = new Matrix4().makeRotationY(Math.PI);
+
+    this.cameraForward = new Matrix4();
+    this.headTransform = new Matrix4();
+    this.hipsPosition = new Vector3();
+
+    this.curTransform = new Matrix4();
+
+    this.iHipsToHeadVector = new Vector3();
+
+    this.iMiddleEyeToHead = new Matrix4();
+    this.iHeadToHip = new Matrix4();
+
+    this.cameraYRotation = new Euler();
+    this.cameraYQuaternion = new Quaternion();
+
+    this.hipsQuaternion = new Quaternion();
+    this.headQuaternion = new Quaternion();
+  },
+
+  update(oldData) {
+    if (this.data.leftEye !== oldData.leftEye) {
+      this.leftEye = this.el.querySelector(this.data.leftEye);
+    }
+
+    if (this.data.rightEye !== oldData.rightEye) {
+      this.rightEye = this.el.querySelector(this.data.rightEye);
+    }
+
+    if (this.data.middleEye !== oldData.middleEye) {
+      this.middleEye = this.el.querySelector(this.data.middleEye);
+    }
+
+    if (this.data.head !== oldData.head) {
+      this.head = this.el.querySelector(this.data.head);
+    }
+
+    if (this.data.neck !== oldData.neck) {
+      this.neck = this.el.querySelector(this.data.neck);
+    }
+
+    if (this.data.leftHand !== oldData.leftHand) {
+      this.leftHand = this.el.querySelector(this.data.leftHand);
+    }
+
+    if (this.data.rightHand !== oldData.rightHand) {
+      this.rightHand = this.el.querySelector(this.data.rightHand);
+    }
+
+    if (this.data.chest !== oldData.chest) {
+      this.chest = this.el.querySelector(this.data.chest);
+    }
+
+    if (this.data.hips !== oldData.hips) {
+      this.hips = this.el.querySelector(this.data.hips);
+    }
+
+    // Set middleEye's position to be right in the middle of the left and right eyes.
+    const middleEyePosition = this.middleEye.object3D.position;
+    middleEyePosition.addVectors(this.leftEye.object3D.position, this.rightEye.object3D.position);
+    middleEyePosition.divideScalar(2);
+    this.middleEye.object3D.updateMatrix();
+    this.iMiddleEyeToHead.getInverse(this.middleEye.object3D.matrix);
+
+    this.iHipsToHeadVector
+      .addVectors(this.chest.object3D.position, this.neck.object3D.position)
+      .add(this.head.object3D.position)
+      .negate();
+  },
+
+  updateIkRoot(ikRoot) {
+    this.ikRoot = ikRoot;
+  },
+
+  tick(time, dt) {
+    if (!this.ikRoot) {
+      return;
+    }
+
+    const { camera, leftController, rightController } = this.ikRoot;
+    const {
+      hips,
+      head,
+      cameraForward,
+      headTransform,
+      iMiddleEyeToHead,
+      iHipsToHeadVector,
+      flipY,
+      hipsPosition,
+      cameraYRotation,
+      cameraYQuaternion,
+      hipsQuaternion,
+      headQuaternion
+    } = this;
+
+    // Camera faces the -Z direction. Flip it along the Y axis so that it is +Z.
+    camera.object3D.updateMatrix();
+    cameraForward.multiplyMatrices(camera.object3D.matrix, flipY);
+
+    headTransform.multiplyMatrices(cameraForward, iMiddleEyeToHead);
+    hipsPosition.setFromMatrixPosition(headTransform).add(iHipsToHeadVector);
+    hips.setAttribute("position", hipsPosition);
+
+    cameraYRotation.setFromRotationMatrix(cameraForward, "YXZ");
+    cameraYRotation.x = 0;
+    cameraYRotation.z = 0;
+    cameraYQuaternion.setFromEuler(cameraYRotation);
+    Quaternion.slerp(hips.object3D.quaternion, cameraYQuaternion, hipsQuaternion, this.data.rotationSpeed * dt / 1000);
+    hips.object3D.quaternion.copy(hipsQuaternion);
+    updateEntityRotation(hips);
+
+    headQuaternion.setFromRotationMatrix(headTransform).premultiply(hipsQuaternion.inverse());
+
+    head.object3D.quaternion.copy(headQuaternion);
+    updateEntityRotation(head);
+  }
+});
diff --git a/src/network-schemas.js b/src/network-schemas.js
index e211090073a2ee8201d810a2f9dd673aebd30581..73c00c0310b0e005d28a9ebe933de08e18e7f9a7 100644
--- a/src/network-schemas.js
+++ b/src/network-schemas.js
@@ -5,35 +5,35 @@ function registerNetworkSchemas() {
       "position",
       "rotation",
       {
-        selector: ".Head",
+        selector: ".camera",
         component: "position"
       },
       {
-        selector: ".Head",
+        selector: ".camera",
         component: "rotation"
       },
       {
-        selector: ".LeftHand",
+        selector: ".left-controller",
         component: "position"
       },
       {
-        selector: ".LeftHand",
+        selector: ".left-controller",
         component: "rotation"
       },
       {
-        selector: ".LeftHand",
+        selector: ".left-controller",
         component: "visible"
       },
       {
-        selector: ".RightHand",
+        selector: ".right-controller",
         component: "position"
       },
       {
-        selector: ".RightHand",
+        selector: ".right-controller",
         component: "rotation"
       },
       {
-        selector: ".RightHand",
+        selector: ".right-controller",
         component: "visible"
       },
       {
diff --git a/src/room.js b/src/room.js
index 7b789b4b59f253ae7b78ee366ac9d01dff9819d9..d5fc0957706aa7f42d49da3a9c0988c5fe763d90 100644
--- a/src/room.js
+++ b/src/room.js
@@ -26,7 +26,7 @@ import "./components/nametag-transform";
 import "./components/bone-mute-state-indicator";
 import "./components/2d-mute-state-indicator";
 import "./components/virtual-gamepad-controls";
-import "./components/body-controller";
+import "./components/ik-controller";
 import "./components/hand-controls2";
 import "./components/character-controller";
 import "./components/haptic-feedback";
diff --git a/templates/room.hbs b/templates/room.hbs
index 087a81f8ef09841301ee79c34fa64856398de798..8137c23aa705f7858ff21d4e6c272b8e2e9def25 100644
--- a/templates/room.hbs
+++ b/templates/room.hbs
@@ -44,7 +44,7 @@
         light="defaultLightsEnabled: false">
 
         <a-assets>
-            <a-asset-item id="bot-skinned-mesh" response-type="arraybuffer" src="{{asset "assets/avatars/Bot_Skinned.glb" }}"></a-asset-item>
+            <a-asset-item id="bot-skinned-mesh" response-type="arraybuffer" src="{{asset "assets/avatars/Bot_SkinnedWithAnim.glb" }}"></a-asset-item>
             <a-asset-item id="watch-model" response-type="arraybuffer" src="{{asset "assets/hud/watch.glb"}}"></a-asset-item>
 
             <a-asset-item id="meeting-space1-mesh" response-type="arraybuffer" src="{{asset "assets/environments/MeetingSpace1_mesh.glb"}}"></a-asset-item>
@@ -60,109 +60,99 @@
                 <a-entity class="video" geometry="primitive: plane;" material="side: double" networked-video-player></a-entity>
             </template>
 
-            <template id="remote-avatar-template">
-                <a-gltf-entity src="#bot-skinned-mesh" inflate="true">
-                    <template data-selector=".Head">
-                        <a-entity
-                            networked-audio-source
-                            networked-audio-analyser
-                            matcolor-audio-feedback="objectName: Head_Mesh"
-                            scale-audio-feedback
-                            personal-space-invader
-                            animation-mixer
-                        >
+            <template id="remote-avatar-template">                
+                <a-entity>
+                    <a-gltf-entity src="#bot-skinned-mesh" inflate="true" ik-controller >
+                        <template data-selector=".Head">
                             <a-entity
-                                class="nametag"
-                                nametag-transform="follow: .Head"
-                                text="side: double; align: center; color: #ddd"
-                                position="0 2.5 0"
-                                scale="6 6 6"
-                            ></a-entity>
-                        </a-entity>
-                    </template>
-
-                    <template data-selector=".Body">
-                        <a-entity body-controller="eyeNeckOffset: 0 -0.11 0.09; neckHeight: 0.05" ></a-entity>
-                    </template>
-
-                    <template selector=".LeftHand">
-                        <a-entity personal-space-invader bone-visibility ></a-entity>
-                    </template>
-
-                    <template data-selector=".RightHand">
-                        <a-entity personal-space-invader bone-visibility ></a-entity>
-                    </template>
-                </a-gltf-entity>
+                                networked-audio-source
+                                networked-audio-analyser
+                                matcolor-audio-feedback="objectName: Head_Mesh"
+                                scale-audio-feedback
+                                personal-space-invader
+                                animation-mixer
+                            >
+                                <a-entity class="middle-eye"></a-entity>
+                                <a-entity
+                                    class="nametag"
+                                    nametag-transform="follow: .Head"
+                                    text="side: double; align: center; color: #ddd"
+                                    position="0 50 0"
+                                    scale="600 600 600"
+                                ></a-entity>
+                            </a-entity>
+                        </template>
+
+                        <template selector=".LeftHand">
+                            <a-entity personal-space-invader ></a-entity>
+                        </template>
+
+                        <template data-selector=".RightHand">
+                            <a-entity personal-space-invader ></a-entity>
+                        </template>
+                    </a-gltf-entity>
+                </a-entity>
             </template>
+
         </a-assets>
 
         <!-- Player Rig -->
-        <a-gltf-entity
+        <a-entity
             id="player-rig"
-            src="#bot-skinned-mesh"
-            inflate="true"
             networked="template: #remote-avatar-template; attachLocalTemplate: false;"
             spawn-controller="radius: 4;"
             wasd-to-analog2d
-            character-controller="pivot: #head"
+            character-controller="pivot: #player-camera"
+            ik-root
         >
-            <template data-selector=".Head">
-                <a-entity
-                    id="head"
-                    camera="userHeight: 1.6"
-                    camera-scale
-                    personal-space-bubble
-                    look-controls
-                    visible="false"
-                >
-                    <a-entity
-                        class="nametag"
-                        nametag-transform="follow: .Head"
-                        text="side: double; align: center; color: #ddd"
-                        position="0 2.5 0"
-                        scale="6 6 6"
-                    ></a-entity>
-                </a-entity>
-            </template>
-
-            <template data-selector=".Chest">
-                <a-entity
-                    id="body"
-                    body-controller="eyeNeckOffset: 0 -0.11 0.09; neckHeight: 0.05"
-                ></a-entity>
-            </template>
-
-            <template data-selector=".LeftHand">
-                <a-entity
-                    id="left-hand"
-                    hand-controls2="left"
-                    tracked-controls
-                    haptic-feedback
-                    teleport-controls="cameraRig: #player-rig; teleportOrigin: #head; button: action_teleport_"
-                    bone-visibility
-                >
-                    <!--<a-entity
-                        id="watch"
-                        cached-gltf-model="#watch-model"
-                        bone-mute-state-indicator
-                        position="-0.003 0.009 0.085"
-                        rotation="-79.12547150756669 -160.1417037390651 -100.1530225888679"
-                        scale="1.5 1.5 1.5"
-                    >-->
+            <a-entity
+                id="player-camera"
+                class="camera"
+                camera="userHeight: 1.6"
+                personal-space-bubble
+                look-controls
+            ></a-entity>
+            
+            <a-entity
+                id="player-left-controller"
+                class="left-controller"
+                hand-controls2="left"
+                tracked-controls
+                teleport-controls="cameraRig: #player-rig; teleportOrigin: #player-camera; button: action_teleport_"
+                haptic-feedback
+            ></a-entity>
+
+            <a-entity
+                id="player-right-controller"
+                class="right-controller"
+                hand-controls2="right"
+                tracked-controls
+                teleport-controls="cameraRig: #player-rig; teleportOrigin: #player-camera; button: action_teleport_"
+                haptic-feedback
+            ></a-entity>
+
+            <a-gltf-entity src="#bot-skinned-mesh" inflate="true" ik-controller>
+                <template data-selector=".Head">
+                    <a-entity visible="false">
+                        <a-entity class="nametag" text ></a-entity>
+                        <a-entity class="middle-eye"></a-entity>
                     </a-entity>
-                </a-entity>
-            </template>
+                </template>
 
-            <template data-selector=".RightHand">
-                <a-entity
-                    id="right-hand"
-                    hand-controls2="right"
-                    haptic-feedback
-                    teleport-controls="cameraRig: #player-rig; teleportOrigin: #head; button: action_teleport_;"
-                    bone-visibility
-                ></a-entity>
-            </template>
-        </a-gltf-entity>
+                <template data-selector=".LeftHand">
+                    <a-entity>
+                        <a-entity
+                            id="watch"
+                            cached-gltf-model="#watch-model"
+                            bone-mute-state-indicator
+                            position="-0.003 0.009 0.085"
+                            rotation="-79.12547150756669 -160.1417037390651 -100.1530225888679"
+                            scale="1.5 1.5 1.5"
+                        ></a-entity>
+                    </a-entity>
+                </template>
+            </a-gltf-entity>
+        </a-entity>
 
         <!-- Environment -->
         <a-entity