diff --git a/package.json b/package.json
index 744b8d81544a7a5fafee47af0f431b6753e7c665..afa65f97961f7d2a4918aa3c9fd4fbae8e7d8fd1 100644
--- a/package.json
+++ b/package.json
@@ -26,7 +26,7 @@
     "aframe-input-mapping-component": "https://github.com/mozillareality/aframe-input-mapping-component#hubs/master",
     "aframe-motion-capture-components": "https://github.com/mozillareality/aframe-motion-capture-components#1ca616fa67b627e447b23b35a09da175d8387668",
     "aframe-physics-extras": "^0.1.3",
-    "aframe-physics-system": "https://github.com/mozillareality/aframe-physics-system#hubs/master",
+    "aframe-physics-system": "https://github.com/mozillareality/aframe-physics-system#static-dynamic-mass-change",
     "aframe-rounded": "^1.0.3",
     "aframe-slice9-component": "^1.0.0",
     "aframe-teleport-controls": "https://github.com/mozillareality/aframe-teleport-controls#hubs/master",
@@ -44,7 +44,7 @@
     "moment-timezone": "^0.5.14",
     "moving-average": "^1.0.0",
     "naf-janus-adapter": "^0.9.0",
-    "networked-aframe": "https://github.com/mozillareality/networked-aframe#mr-social-client/master",
+    "networked-aframe": "https://github.com/mozillareality/networked-aframe#bug/aframe-initialization-race-condition",
     "nipplejs": "https://github.com/mozillareality/nipplejs#mr-social-client/master",
     "phoenix": "^1.3.0",
     "query-string": "^5.0.1",
diff --git a/src/components/auto-box-collider.js b/src/components/auto-box-collider.js
index 633fd0d446de630aff616e27f8a211f394c59f4d..f611c768aca67c3f241860f2e5d38c238e0b80a0 100644
--- a/src/components/auto-box-collider.js
+++ b/src/components/auto-box-collider.js
@@ -1,52 +1,9 @@
+import { calculateBoxShape } from "../utils/auto-box-collider";
 AFRAME.registerComponent("auto-box-collider", {
   schema: {
     resize: { default: false },
     resizeLength: { default: 0.5 }
   },
 
-  init() {
-    this.onLoaded = this.onLoaded.bind(this);
-    this.el.addEventListener("model-loaded", this.onLoaded);
-  },
-
-  remove() {
-    this.el.removeEventListener("model-loaded", this.onLoaded);
-  },
-
-  onLoaded() {
-    const rotation = this.el.object3D.rotation.clone();
-    this.el.object3D.rotation.set(0, 0, 0);
-    const { min, max } = new THREE.Box3().setFromObject(this.el.object3DMap.mesh);
-    const halfExtents = new THREE.Vector3()
-      .addVectors(min.clone().negate(), max)
-      .multiplyScalar(0.5 / this.el.object3D.scale.x);
-    const center = new THREE.Vector3().addVectors(min, max).multiplyScalar(0.5);
-    this.el.object3D.worldToLocal(center);
-    this.el.object3DMap.mesh.position.sub(center);
-
-    if (this.data.resize) {
-      this.resize(min, max);
-    }
-    this.el.setAttribute("shape", {
-      shape: "box",
-      halfExtents: halfExtents
-    });
-    this.el.object3D.rotation.copy(rotation);
-    this.el.removeAttribute("auto-box-collider");
-  },
-
-  // Adjust the scale such that the object fits within a box of a specified size.
-  resize(min, max) {
-    const dX = Math.abs(max.x - min.x);
-    const dY = Math.abs(max.y - min.y);
-    const dZ = Math.abs(max.z - min.z);
-    const lengthOfLongestComponent = Math.max(dX, dY, dZ);
-    const correctiveFactor = this.data.resizeLength / lengthOfLongestComponent;
-    const scale = this.el.object3D.scale;
-    this.el.setAttribute("scale", {
-      x: scale.x * correctiveFactor,
-      y: scale.y * correctiveFactor,
-      z: scale.z * correctiveFactor
-    });
-  }
+  init() {}
 });
diff --git a/src/components/gltf-model-plus.js b/src/components/gltf-model-plus.js
index b449ea9ae2c0248c380eae5c3027b07f4fafc3b7..26cd1fc81348cd8ef1427bb5a7b8a54b3671ad4b 100644
--- a/src/components/gltf-model-plus.js
+++ b/src/components/gltf-model-plus.js
@@ -191,7 +191,15 @@ function cachedLoadGLTF(src, basePath, preferredTechnique, onProgress) {
       gltfLoader.load(src, resolve, onProgress, reject);
     });
   }
-  return GLTFCache[src].then(cloneGltf);
+  return GLTFCache[src]
+    .then(gltf => {
+      return new Promise((resolve, reject) => {
+        window.setTimeout(() => {
+          resolve(gltf);
+        }, 2000);
+      });
+    })
+    .then(cloneGltf);
 }
 
 /**
diff --git a/src/components/image-plus.js b/src/components/image-plus.js
index 2a0a2c5d02e174e19d98705b7652eb56a030a4ce..08cf851b88a514c892c931be3770d30f5ca020d9 100644
--- a/src/components/image-plus.js
+++ b/src/components/image-plus.js
@@ -67,8 +67,6 @@ errorImage.onload = () => {
 };
 
 AFRAME.registerComponent("image-plus", {
-  dependencies: ["geometry"],
-
   schema: {
     src: { type: "string" },
     contentType: { type: "string" }
@@ -85,27 +83,13 @@ AFRAME.registerComponent("image-plus", {
         height = geo.height * ratio;
       }
     } else if (geo && geo.height) {
+      //TODO
       width = geo.width / ratio;
     } else {
       width = Math.min(1.0, 1.0 / ratio);
       height = Math.min(1.0, ratio);
     }
-    this.el.setAttribute("geometry", { width, height });
-    this.el.setAttribute("shape", {
-      shape: "box",
-      halfExtents: {
-        x: width / 2,
-        y: height / 2,
-        z: 0.05
-      }
-    });
-  },
-
-  init() {
-    const material = new THREE.MeshBasicMaterial();
-    material.side = THREE.DoubleSide;
-    material.transparent = true;
-    this.el.getObject3D("mesh").material = material;
+    return { width, height };
   },
 
   remove() {
@@ -230,7 +214,7 @@ AFRAME.registerComponent("image-plus", {
         } else if (contentType.startsWith("video")) {
           texture = await this.loadVideo(url);
         } else {
-          throw new Error(`Unknown centent type: ${contentType}`);
+          throw new Error(`Unknown content type: ${contentType}`);
         }
 
         textureCache.set(url, { count: 1, texture });
@@ -246,9 +230,27 @@ AFRAME.registerComponent("image-plus", {
       texture = errorTexture;
     }
 
-    const material = this.el.getObject3D("mesh").material;
+    const { width, height } = this._fit(
+      texture.image.videoWidth || texture.image.width,
+      texture.image.videoHeight || texture.image.height
+    );
+    const material = new THREE.MeshBasicMaterial();
+    material.side = THREE.DoubleSide;
+    material.transparent = true;
     material.map = texture;
     material.needsUpdate = true;
-    this._fit(texture.image.videoWidth || texture.image.width, texture.image.videoHeight || texture.image.height);
+
+    const geometry = new THREE.PlaneGeometry(width, height, 1, 1);
+    this.mesh = new THREE.Mesh(geometry, material);
+    this.el.setObject3D("mesh", this.mesh);
+    this.el.setAttribute("shape", {
+      shape: "box",
+      halfExtents: { x: width / 2, y: height / 2, z: 0.05 }
+    });
+    if (this.el.components.body && this.el.components.body.body) {
+      this.el.components.body.syncToPhysics(); // not sure if necessary?
+      this.el.components.body.updateCannonScale();
+    }
+    this.el.emit("image-loaded");
   }
 });
diff --git a/src/components/sticky-object.js b/src/components/sticky-object.js
index d415779078f6a0acb2d6975170cde39cbd80cf47..22120af4a9802f769fb827cb27c4564cca7f9ba3 100644
--- a/src/components/sticky-object.js
+++ b/src/components/sticky-object.js
@@ -1,6 +1,6 @@
 /* global THREE, CANNON, AFRAME */
 AFRAME.registerComponent("sticky-object", {
-  dependencies: ["body", "super-networked-interactable"],
+  dependencies: ["body"],
 
   schema: {
     autoLockOnLoad: { default: false },
@@ -27,14 +27,10 @@ AFRAME.registerComponent("sticky-object", {
   },
 
   setLocked(locked) {
-    if (!NAF.utils.isMine(this.el)) return;
+    if (this.el.components.networked && !NAF.utils.isMine(this.el)) return;
 
-    const mass = this.el.components["super-networked-interactable"].data.mass;
     this.locked = locked;
-    this.el.body.type = locked ? window.CANNON.Body.STATIC : window.CANNON.Body.DYNAMIC;
-    this.el.setAttribute("body", {
-      mass: locked ? 0 : mass
-    });
+    this.el.setAttribute("body", { type: locked ? "static" : "dynamic" });
   },
 
   _onBodyLoaded() {
diff --git a/src/components/super-networked-interactable.js b/src/components/super-networked-interactable.js
index 3b84bb7ac3b99421024113d527aa8a1ea54da725..844dc1df1706f2a51f55afd531e41898724fcb77 100644
--- a/src/components/super-networked-interactable.js
+++ b/src/components/super-networked-interactable.js
@@ -5,7 +5,6 @@
  */
 AFRAME.registerComponent("super-networked-interactable", {
   schema: {
-    mass: { default: 1 },
     hapticsMassVelocityFactor: { default: 0.1 },
     counter: { type: "selector" }
   },
@@ -18,7 +17,7 @@ AFRAME.registerComponent("super-networked-interactable", {
     NAF.utils.getNetworkedEntity(this.el).then(networkedEl => {
       this.networkedEl = networkedEl;
       if (!NAF.utils.isMine(networkedEl)) {
-        this.el.setAttribute("body", { type: "static", mass: 0 });
+        this.el.setAttribute("body", { type: "static" });
       } else {
         this.counter.register(networkedEl);
       }
@@ -51,7 +50,7 @@ AFRAME.registerComponent("super-networked-interactable", {
     this.hand = e.detail.hand;
     if (this.networkedEl && !NAF.utils.isMine(this.networkedEl)) {
       if (NAF.utils.takeOwnership(this.networkedEl)) {
-        this.el.setAttribute("body", { type: "dynamic", mass: this.data.mass });
+        this.el.setAttribute("body", { type: "dynamic" });
         this.counter.register(this.networkedEl);
       } else {
         this.el.emit("grab-end", { hand: this.hand });
@@ -61,7 +60,7 @@ AFRAME.registerComponent("super-networked-interactable", {
   },
 
   _onOwnershipLost: function() {
-    this.el.setAttribute("body", { type: "static", mass: 0 });
+    this.el.setAttribute("body", { type: "static" });
     this.el.emit("grab-end", { hand: this.hand });
     this.hand = null;
     this.counter.deregister(this.el);
diff --git a/src/hub.html b/src/hub.html
index 572ce73d400715cb1d9629d53bd426adaeb26471..eadce7db74b9b93747193e08c75e54effc4b7b38 100644
--- a/src/hub.html
+++ b/src/hub.html
@@ -33,7 +33,7 @@
     <a-scene
         renderer="antialias: true"
         networked-scene="adapter: janus; audio: true; debug: true; connectOnLoad: false;"
-        physics="gravity: -6;"
+        physics="gravity: -6; debug: true;"
         mute-mic="eventSrc: a-scene; toggleEvents: action_mute"
         freeze-controller="toggleEvent: action_freeze"
         personal-space-bubble="debug: false;"
@@ -86,7 +86,6 @@
             <img id="water-normal-map" crossorigin="anonymous" src="./assets/waternormals.jpg">
 
             <!-- Templates -->
-
             <template id="video-template">
                 <a-entity class="video" geometry="primitive: plane;" material="side: double; shader: flat;" networked-video-player></a-entity>
             </template>
@@ -163,15 +162,13 @@
                 <a-entity
                     gltf-model-plus="inflate: false;"
                     class="interactable"
-                    super-networked-interactable="counter: #media-counter; mass: 1;"
+                    super-networked-interactable="counter: #media-counter;"
                     body="type: dynamic; shape: none; mass: 1;"
                     grabbable
                     stretchable="useWorldPosition: true; usePhysics: never"
                     hoverable
                     sticky-object="autoLockOnRelease: true; autoLockOnLoad: true;"
-                    auto-box-collider
                     position-at-box-shape-border="target:.delete-button"
-                    auto-scale-cannon-physics-body
                     destroy-at-extreme-distances
                 >
                     <a-entity class="delete-button" visible-while-frozen scale="0.08 0.08 0.08">
@@ -184,13 +181,12 @@
             <template id="interactable-image">
                 <a-entity
                     class="interactable"
-                    super-networked-interactable="counter: #media-counter; mass: 1;"
+                    super-networked-interactable="counter: #media-counter;"
                     body="type: dynamic; shape: none; mass: 1;"
                     auto-scale-cannon-physics-body
                     grabbable
                     stretchable="useWorldPosition: true; usePhysics: never"
                     hoverable
-                    geometry="primitive: plane"
                     image-plus
                     sticky-object="autoLockOnLoad: true; autoLockOnRelease: true;"
                     position-at-box-shape-border="target:.delete-button;dirs:forward,back"
diff --git a/src/hub.js b/src/hub.js
index 123af93bfec23e1d828d51419b1d963e17e30bbd..11b1bac1f48ef01e75b755961e3a71a4243543a6 100644
--- a/src/hub.js
+++ b/src/hub.js
@@ -325,7 +325,7 @@ const onReady = async () => {
         e.preventDefault();
         const imgUrl = e.dataTransfer.getData("url");
         if (imgUrl) {
-          console.log("Droped: ", imgUrl);
+          console.log("Dropped: ", imgUrl);
           addMedia(imgUrl);
         }
       });
diff --git a/src/utils/auto-box-collider.js b/src/utils/auto-box-collider.js
new file mode 100644
index 0000000000000000000000000000000000000000..391607882f4dd6233d4292d414f0d60c2369d622
--- /dev/null
+++ b/src/utils/auto-box-collider.js
@@ -0,0 +1,79 @@
+export function getBox(entity) {
+  const box = new THREE.Box3();
+  const mesh = entity.getObject3D("mesh");
+  const rotation = entity.object3D.rotation.clone();
+  entity.object3D.rotation.set(0, 0, 0);
+  entity.object3D.updateMatrixWorld(true);
+
+  box.expandByObject = expandByObject;
+  box.setFromObject(mesh);
+  entity.object3D.rotation.copy(rotation);
+  return box;
+}
+
+export function getCenterAndHalfExtents(entity, box, center, halfExtents) {
+  const { min, max } = box;
+  center.addVectors(min, max).multiplyScalar(0.5);
+  entity.object3D.worldToLocal(center);
+  halfExtents
+    .copy(min)
+    .negate()
+    .add(max)
+    .multiplyScalar(0.5 / entity.object3D.scale.x);
+}
+
+export function getScaleCoefficient(length, box) {
+  const { max, min } = box;
+  const dX = Math.abs(max.x - min.x);
+  const dY = Math.abs(max.y - min.y);
+  const dZ = Math.abs(max.z - min.z);
+  const lengthOfLongestComponent = Math.max(dX, dY, dZ);
+  return length / lengthOfLongestComponent;
+}
+
+const expandByObject = (function() {
+  // Computes the world-axis-aligned bounding box of an object (including its children),
+  // accounting for both the object's, and children's, world transforms
+
+  var scope, i, l;
+
+  var v1 = new THREE.Vector3();
+
+  function traverse(node) {
+    var geometry = node.geometry;
+
+    if (geometry !== undefined) {
+      if (geometry.isGeometry) {
+        var vertices = geometry.vertices;
+
+        for (i = 0, l = vertices.length; i < l; i++) {
+          v1.copy(vertices[i]);
+          v1.applyMatrix4(node.matrixWorld);
+
+          scope.expandByPoint(v1);
+        }
+      } else if (geometry.isBufferGeometry) {
+        var attribute = geometry.attributes.position;
+
+        if (attribute !== undefined) {
+          for (i = 0, l = attribute.count; i < l; i++) {
+            v1.fromBufferAttribute(attribute, i).applyMatrix4(node.matrixWorld);
+            if (isNaN(v1.x) || isNaN(v1.y) || isNaN(v1.z)) {
+              continue;
+            }
+
+            scope.expandByPoint(v1);
+          }
+        }
+      }
+    }
+  }
+
+  return function expandByObject(object) {
+    scope = this;
+
+    object.traverse(traverse);
+
+    return this;
+  };
+})();
diff --git a/src/utils/media-utils.js b/src/utils/media-utils.js
index 211ebd9d5194ce8b0e881b4b5d72c412aa1057d1..cd91971d8ab52d50fdf4fe0d93656bfbe9b99f9e 100644
--- a/src/utils/media-utils.js
+++ b/src/utils/media-utils.js
@@ -1,3 +1,4 @@
+import { getBox, getCenterAndHalfExtents, getScaleCoefficient } from "./auto-box-collider";
 const whitelistedHosts = [/^.*\.reticulum\.io$/, /^.*hubs\.mozilla\.com$/, /^hubs\.local$/];
 const isHostWhitelisted = hostname => !!whitelistedHosts.filter(r => r.test(hostname)).length;
 let resolveMediaUrl = "/api/v1/media";
@@ -21,53 +22,83 @@ const fetchContentType = async url => fetch(url, { method: "HEAD" }).then(r => r
 
 let interactableId = 0;
 const offset = { x: 0, y: 0, z: -1.5 };
-export const spawnNetworkedImage = (src, contentType) => {
-  const scene = AFRAME.scenes[0];
-  const image = document.createElement("a-entity");
-  image.id = "interactable-image-" + interactableId++;
-  image.setAttribute("networked", { template: "#interactable-image" });
-  image.setAttribute("offset-relative-to", {
-    target: "#player-camera",
-    offset: offset,
-    selfDestruct: true
+export const spawnNetworkedImage = (entity, src, contentType) => {
+  entity.id = "interactable-image-" + interactableId++;
+  entity.setAttribute("networked", { template: "#interactable-image" });
+  entity.addEventListener("image-loaded", function onBodyLoaded() {
+    entity.removeEventListener("image-loaded", onBodyLoaded);
   });
-  image.setAttribute("image-plus", { src, contentType });
-  scene.appendChild(image);
-  return image;
+  entity.setAttribute("image-plus", { src, contentType });
 };
 
-export const spawnNetworkedInteractable = (src, basePath) => {
+export const spawnNetworkedInteractable = (entity, src, basePath) => {
+  entity.id = "interactable-model-" + interactableId++;
+  entity.setAttribute("networked", { template: "#interactable-model" });
+  entity.addEventListener("model-loaded", function onBodyLoaded() {
+    entity.removeEventListener("model-loaded", onBodyLoaded);
+    setShapeAndScale(entity);
+  });
+  entity.setAttribute("gltf-model-plus", { src: src, basePath: basePath });
+};
+
+export const addMedia = async url => {
   const scene = AFRAME.scenes[0];
-  const model = document.createElement("a-entity");
-  model.id = "interactable-model-" + interactableId++;
-  model.setAttribute("networked", { template: "#interactable-model" });
-  model.setAttribute("offset-relative-to", {
-    // on: "model-loaded",
+
+  const entity = document.createElement("a-entity");
+  entity.setObject3D("mesh", new THREE.Mesh(new THREE.BoxGeometry(), new THREE.MeshBasicMaterial()));
+  entity.classList.add("interactable");
+  entity.setAttribute("body", { type: "dynamic", shape: "none", mass: "1" });
+  entity.setAttribute("grabbable", "");
+  entity.setAttribute("hoverable", "");
+  entity.setAttribute("stretchable", { useWorldPosition: true, usePhysics: "never" });
+  entity.setAttribute("sticky-object", { autoLockOnRelease: true, autoLockOnLoad: true });
+  entity.setAttribute("destroy-at-extreme-distances", "");
+  entity.setAttribute("offset-relative-to", {
     target: "#player-camera",
     offset: offset,
     selfDestruct: true
   });
-  model.setAttribute("gltf-model-plus", { src, basePath });
-  model.setAttribute("auto-box-collider", { resize: true });
-  scene.appendChild(model);
-  return model;
-};
+  setShapeAndScale(entity);
+  scene.appendChild(entity);
 
-export const addMedia = async url => {
   try {
     const { raw, origin, meta } = await resolveFarsparkUrl(url);
     console.log("resolved", url, raw, origin, meta);
 
     const contentType = (meta && meta.expected_content_type) || (await fetchContentType(raw));
     if (contentType.startsWith("image/") || contentType.startsWith("video/")) {
-      return spawnNetworkedImage(raw, contentType);
+      return spawnNetworkedImage(entity, raw, contentType);
     } else if (contentType.startsWith("model/gltf") || url.endsWith(".gltf") || url.endsWith(".glb")) {
-      return spawnNetworkedInteractable(raw, THREE.LoaderUtils.extractUrlBase(origin));
+      return spawnNetworkedInteractable(entity, raw, THREE.LoaderUtils.extractUrlBase(origin));
     } else {
       throw new Error(`Unsupported content type: ${contentType}`);
     }
   } catch (e) {
     console.error("Error adding media", e);
-    spawnNetworkedImage("error");
+    return spawnNetworkedImage(entity, "error");
   }
 };
+
+function setShapeAndScale(entity) {
+  const box = getBox(entity);
+  const center = new THREE.Vector3();
+  const halfExtents = new THREE.Vector3();
+  getCenterAndHalfExtents(entity, box, center, halfExtents);
+  entity.getObject3D("mesh").position.sub(center);
+  const scaleCoefficient = getScaleCoefficient(0.5, box);
+  entity.setAttribute("shape", {
+    shape: "box",
+    halfExtents: halfExtents
+  });
+  const scale = entity.object3D.scale;
+  entity.setAttribute("scale", {
+    x: scale.x * scaleCoefficient,
+    y: scale.y * scaleCoefficient,
+    z: scale.z * scaleCoefficient
+  });
+  if (entity.components.body && entity.components.body.body) {
+    // TODO: Do this in shape component update
+    entity.components.body.syncToPhysics();
+    entity.components.body.updateCannonScale();
+  }
+}
diff --git a/yarn.lock b/yarn.lock
index 0dc13a86aa4c0e7a671857cc211ee5934b5b12f0..d3a49e002bcc8cc8dbdf1e0be3726d69d40ef2ec 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -182,9 +182,9 @@ aframe-physics-extras@^0.1.3:
   version "0.1.3"
   resolved "https://registry.yarnpkg.com/aframe-physics-extras/-/aframe-physics-extras-0.1.3.tgz#803e2164fb96c0a80f2d1a81458f3277f262b130"
 
-"aframe-physics-system@https://github.com/mozillareality/aframe-physics-system#hubs/master":
+"aframe-physics-system@https://github.com/mozillareality/aframe-physics-system#static-dynamic-mass-change":
   version "3.1.2"
-  resolved "https://github.com/mozillareality/aframe-physics-system#50f5deb1134eb0d43c0435d287eef7037818d3cc"
+  resolved "https://github.com/mozillareality/aframe-physics-system#1fe4e3d8d325de7a30441cdad001459789efff05"
   dependencies:
     browserify "^14.3.0"
     budo "^10.0.3"
@@ -5521,9 +5521,9 @@ neo-async@^2.5.0:
   version "2.5.0"
   resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.5.0.tgz#76b1c823130cca26acfbaccc8fbaf0a2fa33b18f"
 
-"networked-aframe@https://github.com/mozillareality/networked-aframe#mr-social-client/master":
+"networked-aframe@https://github.com/mozillareality/networked-aframe#bug/aframe-initialization-race-condition":
   version "0.6.1"
-  resolved "https://github.com/mozillareality/networked-aframe#06236f794f83cfebdc4ea9f3a9e8a5804f5bdcf9"
+  resolved "https://github.com/mozillareality/networked-aframe#1d93d17f95e877cf1efcec460d7f8a4bf59c3784"
   dependencies:
     buffered-interpolation "^0.2.4"
     easyrtc "1.1.0"