diff --git a/package.json b/package.json
index cae88c47526aef2ed515ceaa4fda82d1a2812622..d21ad9c0e5e7f7bd00ec9695b6cac65f4dda8eb9 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": "github:donmccurdy/aframe-physics-system",
+    "aframe-physics-system": "https://github.com/mozillareality/aframe-physics-system#hubs/master",
     "aframe-rounded": "^1.0.3",
     "aframe-slice9-component": "^1.0.0",
     "aframe-teleport-controls": "https://github.com/mozillareality/aframe-teleport-controls#hubs/master",
diff --git a/src/components/auto-box-collider.js b/src/components/auto-box-collider.js
new file mode 100644
index 0000000000000000000000000000000000000000..e902e347f8c68fa35046e88d4c8b9c2dbbd332c2
--- /dev/null
+++ b/src/components/auto-box-collider.js
@@ -0,0 +1,69 @@
+AFRAME.registerComponent("auto-box-collider", {
+  schema: {
+    setInitialScale: { default: false }
+  },
+
+  init() {
+    this.onLoaded = this.onLoaded.bind(this);
+    this.el.addEventListener("model-loaded", this.onLoaded);
+  },
+
+  onLoaded() {
+    this.el.removeEventListener("model-loaded", this.onLoaded);
+    if (this.data.setInitialScale) {
+      this.setInitialScale();
+    }
+
+    const initialRotation = this.el.object3D.rotation.clone();
+    this.el.setAttribute("rotation", { x: 0, y: 0, z: 0 });
+
+    const worldBox = new THREE.Box3().setFromObject(this.el.object3D);
+    const dX = Math.abs(worldBox.max.x - worldBox.min.x);
+    const dY = Math.abs(worldBox.max.y - worldBox.min.y);
+    const dZ = Math.abs(worldBox.max.z - worldBox.min.z);
+    const scale = this.el.getAttribute("scale");
+    const inverseScale = { x: 1 / scale.x, y: 1 / scale.y, z: 1 / scale.z };
+
+    const localHalfExtents = {
+      x: dX * 0.5 * inverseScale.x,
+      y: dY * 0.5 * inverseScale.y,
+      z: dZ * 0.5 * inverseScale.z
+    };
+    const center = new THREE.Vector3().addVectors(worldBox.min, worldBox.max).multiplyScalar(0.5);
+    const worldPosition = new THREE.Vector3();
+    this.el.object3D.getWorldPosition(worldPosition);
+    const localOffset = {
+      x: (center.x - worldPosition.x) * inverseScale.x,
+      y: (center.y - worldPosition.y) * inverseScale.y,
+      z: (center.z - worldPosition.z) * inverseScale.z
+    };
+
+    // Set the shape component halfExtents
+    this.el.setAttribute("shape", {
+      shape: "box",
+      halfExtents: localHalfExtents,
+      offset: localOffset
+    });
+
+    this.el.setAttribute("rotation", initialRotation);
+  },
+
+  // Adjust the scale such that the object fits within a box whose longest component axis is lengthOfLongestComponent meters.
+  // In other words, a tall model will be scaled down to be only half a meter tall, a wide model will be scaled down to be only
+  // half a meter wide, a long model will be scaled down to be only half a meter long.
+  setInitialScale() {
+    const worldBox = new THREE.Box3().setFromObject(this.el.object3D);
+    const dX = Math.abs(worldBox.max.x - worldBox.min.x);
+    const dY = Math.abs(worldBox.max.y - worldBox.min.y);
+    const dZ = Math.abs(worldBox.max.z - worldBox.min.z);
+    const max = Math.max(dX, dY, dZ);
+    const scale = this.el.getAttribute("scale");
+    const lengthOfLongestComponent = 0.5;
+    const correctiveFactor = lengthOfLongestComponent / max;
+    this.el.setAttribute("scale", {
+      x: scale.x * correctiveFactor,
+      y: scale.y * correctiveFactor,
+      z: scale.z * correctiveFactor
+    });
+  }
+});
diff --git a/src/components/gltf-model-plus.js b/src/components/gltf-model-plus.js
index 55d62b10e997a943d39c6e593cb35256c67b0e6c..686245b492555beaf311a7f2f9b94c07dd600d2a 100644
--- a/src/components/gltf-model-plus.js
+++ b/src/components/gltf-model-plus.js
@@ -203,11 +203,11 @@ function cachedLoadGLTF(src, preferredTechnique, onProgress) {
 AFRAME.registerComponent("gltf-model-plus", {
   schema: {
     src: { type: "string" },
-    inflate: { default: false },
-    preferredTechnique: { default: AFRAME.utils.device.isMobile() ? "KHR_materials_unlit" : "pbrMetallicRoughness" }
+    inflate: { default: false }
   },
 
   init() {
+    this.preferredTechnique = AFRAME.utils.device.isMobile() ? "KHR_materials_unlit" : "pbrMetallicRoughness";
     this.loadTemplates();
   },
 
@@ -245,7 +245,7 @@ AFRAME.registerComponent("gltf-model-plus", {
       }
 
       const gltfPath = THREE.LoaderUtils.extractUrlBase(src);
-      const model = await cachedLoadGLTF(src, this.data.preferredTechnique);
+      const model = await cachedLoadGLTF(src, this.preferredTechnique);
 
       // If we started loading something else already
       // TODO: there should be a way to cancel loading instead
diff --git a/src/components/spawn-in-front-of-object.js b/src/components/spawn-in-front-of-object.js
new file mode 100644
index 0000000000000000000000000000000000000000..0a5d0c0d6a63aab2e845acdae548fde881d317cb
--- /dev/null
+++ b/src/components/spawn-in-front-of-object.js
@@ -0,0 +1,14 @@
+AFRAME.registerComponent("spawn-in-front-of-object", {
+  schema: {},
+  init() {
+    this.onLoaded = this.onLoaded.bind(this);
+    this.el.addEventListener("model-loaded", this.onLoaded);
+  },
+  onLoaded() {
+    const billboardTarget = document.querySelector("#player-camera").object3D;
+    const worldPos = new THREE.Vector3().copy({ x: 0, y: 0, z: -1.5 });
+    billboardTarget.localToWorld(worldPos);
+    this.el.object3D.position.copy(worldPos);
+    billboardTarget.getWorldQuaternion(this.el.object3D.quaternion);
+  }
+});
diff --git a/src/components/sticky-object.js b/src/components/sticky-object.js
index 99f93ffafcfcfb2c8e7ff4045d4ac4e758663daa..e6f1cf764b9e32fe383bad8475fe91d7ea86d159 100644
--- a/src/components/sticky-object.js
+++ b/src/components/sticky-object.js
@@ -85,7 +85,6 @@ AFRAME.registerComponent("sticky-object-zone", {
     this.bootImpulse.copy(dir);
 
     this.el.addEventListener("collisions", e => {
-      console.log("collisions", e.detail.els, e.detail.clearedEls);
       e.detail.els.forEach(el => {
         const stickyObject = el.components["sticky-object"];
         if (!stickyObject) return;
diff --git a/src/hub.html b/src/hub.html
index 9863b8361c91da6eaf2132597ade00307b14195b..639a6f21acdcaed0857d13ffff0bee490995a940 100644
--- a/src/hub.html
+++ b/src/hub.html
@@ -18,7 +18,7 @@
         <script src="https://cdn.rawgit.com/aframevr/aframe/3e7a4b3/dist/aframe-master.js" integrity="sha384-EaMOuyBOi9ERV/lVDwQgz/yFWBDWPsIju5Co6oCZZHXuvbLBO81yPWn80q0BbBn3" crossorigin="anonymous"></script>
     <% } %>
 
-    
+
     <!-- HACK: this has to run after A-Frame but before our bundle, since A-Frame blows away the local storage setting -->
     <script src="https://cdn.rawgit.com/gfodor/ba8f88d9f34fe9cbe59a01ce3c48420d/raw/03e31f0ef7b9eac5e947bd39e440f34df0701f75/naf-janus-adapter-logging.js" integrity="sha384-4q1V8Q88oeCFriFefFo5uEUtMzbw6K116tFyC9cwbiPr6wEe7050l5HoJUxMvnzj" crossorigin="anonymous"></script>
 </head>
@@ -162,22 +162,37 @@
                     gltf-model-plus="src: #interactable-duck; inflate: true;"
                     class="interactable"
                     super-networked-interactable="counter: #counter; mass: 1;"
-                    body="type: dynamic; shape: none; mass: 1;"
+                    body="type: dynamic; shape: none; mass: 1; monitorScale: true"
                     grabbable
-                    stretchable="useWorldPosition: true;"
+                    stretchable="useWorldPosition: true; usePhysics: never"
                     hoverable
                     duck
-                    sticky-object
+                    sticky-object="autoLockOnRelease: true;"
                 ></a-entity>
             </template>
 
+            <template id="interactable-model">
+                <a-entity
+                    gltf-model-plus="inflate: false;"
+                    class="interactable"
+                    super-networked-interactable="counter: #media-counter; mass: 1;"
+                    body="type: dynamic; shape: none; mass: 1; monitorScale: true"
+                    grabbable
+                    stretchable="useWorldPosition: true; usePhysics: never"
+                    hoverable
+                   auto-box-collider 
+                    sticky-object="autoLockOnRelease: true; autoLockOnLoad: true;"
+                >
+                </a-entity>
+            </template>
+
             <template id="interactable-image">
                 <a-entity
                     class="interactable"
                     super-networked-interactable="counter: #media-counter; mass: 1;"
-                    body="type: dynamic; shape: none; mass: 1;"
+                    body="type: dynamic; shape: none; mass: 1; monitorScale: true"
                     grabbable
-                    stretchable="useWorldPosition: true;"
+                    stretchable="useWorldPosition: true; usePhysics: never"
                     hoverable
                     geometry="primitive: plane"
                     material="shader: flat; side: double;"
@@ -338,6 +353,7 @@
                     collisionEntities: [nav-mesh];
                     drawIncrementally: true;
                     incrementalDrawMs: 600;
+                    curveShootingSpeed: 5;
                     hitOpacity: 0.3;
                     missOpacity: 0.2;"
                 haptic-feedback
diff --git a/src/hub.js b/src/hub.js
index ed9f861b9b399ba60f83e88aa46f1fa325762e22..fa2b7dfc01565b564698b7b46c164e9bd430a392 100644
--- a/src/hub.js
+++ b/src/hub.js
@@ -64,6 +64,8 @@ import "./components/css-class";
 import "./components/scene-shadow";
 import "./components/avatar-replay";
 import "./components/image-plus";
+import "./components/auto-box-collider";
+import "./components/spawn-in-front-of-object";
 import "./components/pinch-to-move";
 import "./components/look-on-mobile";
 import "./components/pitch-yaw-rotator";
@@ -300,15 +302,25 @@ const onReady = async () => {
 
     document.addEventListener("paste", e => {
       const scene = AFRAME.scenes[0];
-      const imgUrl = e.clipboardData.getData("text");
-      console.log("Pasted: ", imgUrl);
-
-      const image = document.createElement("a-entity");
-      image.id = "interactable-image-" + Date.now();
-      image.setAttribute("position", { x: 0, y: 2, z: 1 });
-      image.setAttribute("image-plus", "src", imgUrl);
-      image.setAttribute("networked", { template: "#interactable-image" });
-      scene.appendChild(image);
+      const mediaUrl = e.clipboardData.getData("text");
+      console.log("Pasted: ", mediaUrl);
+      if (mediaUrl.endsWith(".gltf") || mediaUrl.endsWith(".glb")) {
+        const model = document.createElement("a-entity");
+        model.id = "interactable-model-" + Date.now();
+        model.setAttribute("position", { x: 0, y: 2, z: 1 });
+        model.setAttribute("spawn-in-front-of-object", "");
+        model.setAttribute("gltf-model-plus", "src", mediaUrl);
+        model.setAttribute("auto-box-collider", "setInitialScale", true);
+        model.setAttribute("networked", { template: "#interactable-model" });
+        scene.appendChild(model);
+      } else {
+        const image = document.createElement("a-entity");
+        image.id = "interactable-image-" + Date.now();
+        image.setAttribute("position", { x: 0, y: 2, z: 1 });
+        image.setAttribute("image-plus", "src", mediaUrl);
+        image.setAttribute("networked", { template: "#interactable-image" });
+        scene.appendChild(image);
+      }
     });
 
     if (!qsTruthy("offline")) {
diff --git a/src/network-schemas.js b/src/network-schemas.js
index a1c91e93357c21267ba40a9d25d71d2bedfc475e..2c9248d51347a8673a792bc8d9f2e83bf5ed278d 100644
--- a/src/network-schemas.js
+++ b/src/network-schemas.js
@@ -106,6 +106,11 @@ function registerNetworkSchemas() {
       "image-plus"
     ]
   });
+
+  NAF.schemas.add({
+    template: "#interactable-model",
+    components: ["position", "rotation", "scale", "gltf-model-plus"]
+  });
 }
 
 export default registerNetworkSchemas;