diff --git a/src/assets/avatars/Bot_Skinned.glb b/src/assets/avatars/Bot_Skinned.glb
new file mode 100644
index 0000000000000000000000000000000000000000..57359b98846076489c5291bec596cd17e04b65c0
Binary files /dev/null and b/src/assets/avatars/Bot_Skinned.glb differ
diff --git a/src/components/model-inflator.js b/src/components/model-inflator.js
new file mode 100644
index 0000000000000000000000000000000000000000..bb9f85b0f9ee26999b67a3b5fdf1b08255a3be4d
--- /dev/null
+++ b/src/components/model-inflator.js
@@ -0,0 +1,64 @@
+const inflateEntities = function(idPrefix, componentMappings, parentEl, node) {
+  // setObject3D mutates the node's parent, so we have to copy
+  const children = node.children.slice(0);
+
+  const el = document.createElement("a-entity");
+  el.id = idPrefix + node.name;
+  parentEl.appendChild(el);
+
+  // // Copy over transform to the THREE.Group and reset the actual transform of the Object3D
+  el.setAttribute("position", {
+    x: node.position.x,
+    y: node.position.y,
+    z: node.position.z
+  });
+  el.setAttribute("rotation", {
+    x: node.rotation.x * THREE.Math.RAD2DEG,
+    y: node.rotation.y * THREE.Math.RAD2DEG,
+    z: node.rotation.z * THREE.Math.RAD2DEG
+  });
+  el.setAttribute("scale", {
+    x: node.scale.x,
+    y: node.scale.y,
+    z: node.scale.z
+  });
+
+  node.position.set(0, 0, 0);
+  node.rotation.set(0, 0, 0);
+  node.scale.set(1, 1, 1);
+
+  el.setObject3D(node.type.toLowerCase(), node);
+
+  if (componentMappings && componentMappings[node.name]) {
+    const components = componentMappings[node.name];
+    for (const componentName of Object.keys(components)) {
+      el.setAttribute(componentName, components[componentName]);
+    }
+  }
+
+  children.forEach(childNode => {
+    inflateEntities(idPrefix, componentMappings, el, childNode);
+  });
+};
+
+AFRAME.registerComponent("model-inflator", {
+  schema: {
+    idPrefix: { type: "string" }
+  },
+  init: function() {
+    const componentMappings = {
+      RightHand: {
+        spin: "speed: 1;"
+      }
+    };
+
+    this.el.addEventListener("model-loaded", e => {
+      inflateEntities(
+        `${this.data.idPrefix}_`,
+        componentMappings,
+        this.el,
+        e.detail.model
+      );
+    });
+  }
+});
diff --git a/src/room.js b/src/room.js
index e2c1600e9d1fe465a72f4d6bc1642d589ab50d98..29b6a6377944cc3aa58923e963f0f1b3f8806a02 100644
--- a/src/room.js
+++ b/src/room.js
@@ -34,6 +34,7 @@ import "./components/water";
 import "./components/skybox";
 import "./components/layers";
 import "./components/spawn-controller";
+import "./components/model-inflator";
 import "./systems/personal-space-bubble";
 
 import { promptForName, getCookie, parseJwt } from "./utils";
diff --git a/templates/room.hbs b/templates/room.hbs
index 63c3f90db866b36c6042a05e10ae0a39befa8998..b98aefa14038b6cfee9c77072666c427d8e0958d 100644
--- a/templates/room.hbs
+++ b/templates/room.hbs
@@ -44,6 +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-head-mesh" response-type="arraybuffer" src="{{asset "assets/avatars/Bot_Head_Mesh.glb" }}"></a-asset-item>
             <a-asset-item id="bot-body-mesh" response-type="arraybuffer" src="{{asset "assets/avatars/Bot_Body_Mesh.glb" }}"></a-asset-item>
             <a-asset-item id="bot-left-hand-mesh" response-type="arraybuffer" src="{{asset "assets/avatars/Bot_LeftHand_Mesh.glb" }}"></a-asset-item>
@@ -174,6 +175,14 @@
             ></a-entity>
         </a-entity>
 
+         <a-entity
+            id="bot-skinned"
+            cached-gltf-model="#bot-skinned-mesh"
+            position="0 0 0"
+            spin
+            model-inflator="idPrefix: bot;"
+        ></a-entity>
+
         <!-- Environment -->
         <a-entity
             id="meeting-space"