diff --git a/README.md b/README.md
index 96c5a4d06ab4f32d5a00948d0eb14d593cdd3974..78e4831b0ed256a6d47b2d3b90e4737f2a42123f 100644
--- a/README.md
+++ b/README.md
@@ -32,3 +32,5 @@ yarn build
 - `mobile` - Force mobile mode
 - `no_stats` - Disable performance stats
 - `vr_entry_type` - Either "gearvr" or "daydream". Used internally to force a VR entry type
+
+[![Waffle.io - Columns and their card count](https://badge.waffle.io/mozilla/socialmr.svg?columns=all)](http://waffle.io/mozilla/socialmr)
diff --git a/package.json b/package.json
index 37c85f02dd35c42738da0dfffed894adb1812ea9..93154d2d912d0d40d3ff8a067326064c206d46b5 100644
--- a/package.json
+++ b/package.json
@@ -33,7 +33,7 @@
     "mobile-detect": "^1.4.1",
     "moving-average": "^1.0.0",
     "naf-janus-adapter": "https://github.com/mozilla/naf-janus-adapter#feature/disconnect",
-    "networked-aframe": "https://github.com/networked-aframe/networked-aframe",
+    "networked-aframe": "https://github.com/mozillareality/networked-aframe#mr-social-client/master",
     "nipplejs": "^0.6.7",
     "query-string": "^5.0.1",
     "raven-js": "^3.20.1",
diff --git a/scripts/default.env b/scripts/default.env
index 82a69026a22a8eb2ad6c1da4c048095d8e3e3624..0fd18aa5be8791b61c69d2587c4611ef7f1f6b70 100644
--- a/scripts/default.env
+++ b/scripts/default.env
@@ -2,4 +2,4 @@
 # You can find more information about getting your own origin trial token here: https://github.com/GoogleChrome/OriginTrials/blob/gh-pages/developer-guide.md
 ORIGIN_TRIAL_TOKEN="AvIMoF4hyRZQVfSfksoqP+7qzwa4FSBzHRHvUyzC8rMATJVRbcOiLewBxbXtJVyV3N62gsZv7PoSNtDqqtjzYAcAAABkeyJvcmlnaW4iOiJodHRwczovL3JldGljdWx1bS5pbzo0NDMiLCJmZWF0dXJlIjoiV2ViVlIxLjFNNjIiLCJleHBpcnkiOjE1MTYxNDYyMDQsImlzU3ViZG9tYWluIjp0cnVlfQ==",
 ORIGIN_TRIAL_EXPIRES="2018-05-15",
-JANUS_SERVER="wss://dev-janus.reticulum.io"
+JANUS_SERVER="wss://prod-janus.reticulum.io"
diff --git a/src/assets/avatars/BotBobo_Avatar.glb b/src/assets/avatars/BotBobo_Avatar.glb
deleted file mode 100644
index 66862adb28f35fb3bb1ca4954d840a1f9a91bb96..0000000000000000000000000000000000000000
Binary files a/src/assets/avatars/BotBobo_Avatar.glb and /dev/null differ
diff --git a/src/assets/avatars/BotBobo_Avatar_Unlit.glb b/src/assets/avatars/BotBobo_Avatar_Unlit.glb
deleted file mode 100644
index e4a939a242fc930dc52c7d60aa145b7b86023330..0000000000000000000000000000000000000000
Binary files a/src/assets/avatars/BotBobo_Avatar_Unlit.glb and /dev/null differ
diff --git a/src/assets/avatars/BotDefault_Avatar.glb b/src/assets/avatars/BotDefault_Avatar.glb
deleted file mode 100644
index 0784e7354e0fe2e35b3df8329c5587616203b7c3..0000000000000000000000000000000000000000
Binary files a/src/assets/avatars/BotDefault_Avatar.glb and /dev/null differ
diff --git a/src/assets/avatars/BotDefault_Avatar_Unlit.glb b/src/assets/avatars/BotDefault_Avatar_Unlit.glb
deleted file mode 100644
index 850f7552c4183553779dfe0776fb92f042b7f8ca..0000000000000000000000000000000000000000
Binary files a/src/assets/avatars/BotDefault_Avatar_Unlit.glb and /dev/null differ
diff --git a/src/assets/avatars/BotDom_Avatar.glb b/src/assets/avatars/BotDom_Avatar.glb
deleted file mode 100644
index 098a042ead7b919170c6c4c6a63ba40f62a1b81b..0000000000000000000000000000000000000000
Binary files a/src/assets/avatars/BotDom_Avatar.glb and /dev/null differ
diff --git a/src/assets/avatars/BotDom_Avatar_Unlit.glb b/src/assets/avatars/BotDom_Avatar_Unlit.glb
deleted file mode 100644
index 88c830ae9f73581683050705ad75f321bdeecb37..0000000000000000000000000000000000000000
Binary files a/src/assets/avatars/BotDom_Avatar_Unlit.glb and /dev/null differ
diff --git a/src/assets/avatars/BotGreg_Avatar.glb b/src/assets/avatars/BotGreg_Avatar.glb
deleted file mode 100644
index 33ca3ee66249ed267688353ef0ab82fd97dfc8bc..0000000000000000000000000000000000000000
Binary files a/src/assets/avatars/BotGreg_Avatar.glb and /dev/null differ
diff --git a/src/assets/avatars/BotGreg_Avatar_Unlit.glb b/src/assets/avatars/BotGreg_Avatar_Unlit.glb
deleted file mode 100644
index ed0a5291790f79029b6c7e624a45253c5f6caeb3..0000000000000000000000000000000000000000
Binary files a/src/assets/avatars/BotGreg_Avatar_Unlit.glb and /dev/null differ
diff --git a/src/assets/avatars/BotGuest_Avatar.glb b/src/assets/avatars/BotGuest_Avatar.glb
deleted file mode 100644
index e0be8e9cbad1a1d6167c0c0ccf333285d0857472..0000000000000000000000000000000000000000
Binary files a/src/assets/avatars/BotGuest_Avatar.glb and /dev/null differ
diff --git a/src/assets/avatars/BotGuest_Avatar_Unlit.glb b/src/assets/avatars/BotGuest_Avatar_Unlit.glb
deleted file mode 100644
index 3bc50b18e3ffbf9f9007a6f7790a8512284949a7..0000000000000000000000000000000000000000
Binary files a/src/assets/avatars/BotGuest_Avatar_Unlit.glb and /dev/null differ
diff --git a/src/assets/avatars/BotJim_Avatar.glb b/src/assets/avatars/BotJim_Avatar.glb
deleted file mode 100644
index 34c1a8cf274d0ed62b2bc22b51d573f6bafc1728..0000000000000000000000000000000000000000
Binary files a/src/assets/avatars/BotJim_Avatar.glb and /dev/null differ
diff --git a/src/assets/avatars/BotJim_Avatar_Unlit.glb b/src/assets/avatars/BotJim_Avatar_Unlit.glb
deleted file mode 100644
index 534356f80deb17b8808bc778db2db048811baf6a..0000000000000000000000000000000000000000
Binary files a/src/assets/avatars/BotJim_Avatar_Unlit.glb and /dev/null differ
diff --git a/src/assets/avatars/BotKev_Avatar.glb b/src/assets/avatars/BotKev_Avatar.glb
deleted file mode 100644
index a20a54bcc42926a79b623e360a4ed9ddb2380695..0000000000000000000000000000000000000000
Binary files a/src/assets/avatars/BotKev_Avatar.glb and /dev/null differ
diff --git a/src/assets/avatars/BotKev_Avatar_Unlit.glb b/src/assets/avatars/BotKev_Avatar_Unlit.glb
deleted file mode 100644
index d70196b7a3f83f25445cef27cf0fbc7493805cd2..0000000000000000000000000000000000000000
Binary files a/src/assets/avatars/BotKev_Avatar_Unlit.glb and /dev/null differ
diff --git a/src/assets/avatars/BotPinky_Avatar.glb b/src/assets/avatars/BotPinky_Avatar.glb
deleted file mode 100644
index da14ccd6ada339e7d28c1dfcd636a0810d45907c..0000000000000000000000000000000000000000
Binary files a/src/assets/avatars/BotPinky_Avatar.glb and /dev/null differ
diff --git a/src/assets/avatars/BotPinky_Avatar_Unlit.glb b/src/assets/avatars/BotPinky_Avatar_Unlit.glb
deleted file mode 100644
index 4029c8c6feb3dfe137d932ff0e7eb9223c60b6d1..0000000000000000000000000000000000000000
Binary files a/src/assets/avatars/BotPinky_Avatar_Unlit.glb and /dev/null differ
diff --git a/src/assets/avatars/BotRobert_Avatar.glb b/src/assets/avatars/BotRobert_Avatar.glb
deleted file mode 100644
index 68d28e5a19616eae6e5e5c8694182efe02643c65..0000000000000000000000000000000000000000
Binary files a/src/assets/avatars/BotRobert_Avatar.glb and /dev/null differ
diff --git a/src/assets/avatars/BotRobert_Avatar_Unlit.glb b/src/assets/avatars/BotRobert_Avatar_Unlit.glb
deleted file mode 100644
index 505eae50b5604bdbe3a7076421dabd1847e561f1..0000000000000000000000000000000000000000
Binary files a/src/assets/avatars/BotRobert_Avatar_Unlit.glb and /dev/null differ
diff --git a/src/assets/avatars/BotWoody_Avatar.glb b/src/assets/avatars/BotWoody_Avatar.glb
deleted file mode 100644
index 0a205ee9311b5c73f96f5fdf562970bd362b1a21..0000000000000000000000000000000000000000
Binary files a/src/assets/avatars/BotWoody_Avatar.glb and /dev/null differ
diff --git a/src/assets/avatars/BotWoody_Avatar_Unlit.glb b/src/assets/avatars/BotWoody_Avatar_Unlit.glb
deleted file mode 100644
index bd330d47f269ccaae436fbb3a33aa85b51eee6fd..0000000000000000000000000000000000000000
Binary files a/src/assets/avatars/BotWoody_Avatar_Unlit.glb and /dev/null differ
diff --git a/src/assets/avatars/avatars.js b/src/assets/avatars/avatars.js
index 7934ecb66c1439b04e892367dc3655d1f3826abe..68d26045503229c4a8bd9d8952b0e032b49b0b29 100644
--- a/src/assets/avatars/avatars.js
+++ b/src/assets/avatars/avatars.js
@@ -1,65 +1,42 @@
 export const avatars = [
   {
     id: "botdefault",
-    models: {
-      low: `${require("./BotDefault_Avatar_Unlit.glb")}`,
-      high: `${require("./BotDefault_Avatar.glb")}`
-    }
+    model: "https://asset-bundles-dev.reticulum.io/bots/BotDefault_Avatar-9f71f8ff22.gltf"
   },
   {
     id: "botbobo",
-    models: {
-      low: `${require("./BotBobo_Avatar_Unlit.glb")}`,
-      high: `${require("./BotBobo_Avatar.glb")}`
-    }
+    model: "https://asset-bundles-dev.reticulum.io/bots/BotBobo_Avatar-f9740a010b.gltf"
   },
   {
     id: "botdom",
-    models: {
-      low: `${require("./BotDom_Avatar_Unlit.glb")}`,
-      high: `${require("./BotDom_Avatar.glb")}`
-    }
+    model: "https://asset-bundles-dev.reticulum.io/bots/BotDom_Avatar-6aa1b5d781.gltf"
   },
   {
     id: "botgreg",
-    models: {
-      low: `${require("./BotGreg_Avatar_Unlit.glb")}`,
-      high: `${require("./BotGreg_Avatar.glb")}`
-    }
+    model: "https://asset-bundles-dev.reticulum.io/bots/BotGreg_Avatar-98d39797bb.gltf"
   },
   {
     id: "botguest",
-    models: {
-      low: `${require("./BotGuest_Avatar_Unlit.glb")}`,
-      high: `${require("./BotGuest_Avatar.glb")}`
-    }
+    model: "https://asset-bundles-dev.reticulum.io/bots/BotGuest_Avatar-78cd857332.gltf"
   },
   {
     id: "botjim",
-    models: {
-      low: `${require("./BotJim_Avatar_Unlit.glb")}`,
-      high: `${require("./BotJim_Avatar.glb")}`
-    }
+    model: "https://asset-bundles-dev.reticulum.io/bots/BotJim_Avatar-d28005a687.gltf"
+  },
+  {
+    id: "botkev",
+    model: "https://asset-bundles-dev.reticulum.io/bots/BotKev_Avatar-a95787bb51.gltf"
   },
   {
     id: "botpinky",
-    models: {
-      low: `${require("./BotPinky_Avatar_Unlit.glb")}`,
-      high: `${require("./BotPinky_Avatar.glb")}`
-    }
+    model: "https://asset-bundles-dev.reticulum.io/bots/BotPinky_Avatar-b0b93f8675.gltf"
   },
   {
     id: "botrobert",
-    models: {
-      low: `${require("./BotRobert_Avatar_Unlit.glb")}`,
-      high: `${require("./BotRobert_Avatar.glb")}`
-    }
+    model: "https://asset-bundles-dev.reticulum.io/bots/BotRobert_Avatar-e9554880f3.gltf"
   },
   {
     id: "botwoody",
-    models: {
-      low: `${require("./BotWoody_Avatar_Unlit.glb")}`,
-      high: `${require("./BotWoody_Avatar.glb")}`
-    }
+    model: "https://asset-bundles-dev.reticulum.io/bots/BotWoody_Avatar-0140485a23.gltf"
   }
 ];
diff --git a/src/assets/avatars/dodec/AvatarHand_L/AvatarDodeca_Texture.png b/src/assets/avatars/dodec/AvatarHand_L/AvatarDodeca_Texture.png
deleted file mode 100644
index d0f89bc1497e8bc97c7c035abde9705e1316b613..0000000000000000000000000000000000000000
Binary files a/src/assets/avatars/dodec/AvatarHand_L/AvatarDodeca_Texture.png and /dev/null differ
diff --git a/src/assets/avatars/dodec/AvatarHand_L/Avatar_HandL.bin b/src/assets/avatars/dodec/AvatarHand_L/Avatar_HandL.bin
deleted file mode 100644
index ec35a43eff862c13c920dc70127e296629a2075f..0000000000000000000000000000000000000000
Binary files a/src/assets/avatars/dodec/AvatarHand_L/Avatar_HandL.bin and /dev/null differ
diff --git a/src/assets/avatars/dodec/AvatarHand_L/Avatar_HandL.gltf b/src/assets/avatars/dodec/AvatarHand_L/Avatar_HandL.gltf
deleted file mode 100644
index b47180f7e925c0866321a9387ba96bc161101f5e..0000000000000000000000000000000000000000
--- a/src/assets/avatars/dodec/AvatarHand_L/Avatar_HandL.gltf
+++ /dev/null
@@ -1 +0,0 @@
-{"accessors":[{"bufferView":0,"componentType":5126,"count":540,"type":"VEC3","max":[0.21502137184143066,0.31034931540489197,0.30054089426994324],"min":[-0.21345257759094238,-0.28918552398681641,-0.6024399995803833]},{"bufferView":1,"componentType":5126,"count":540,"type":"VEC3","max":[0.99698549509048462,0.97377252578735352,0.63385242223739624],"min":[-0.99751198291778564,-0.9912988543510437,-0.99289071559906006]},{"bufferView":2,"componentType":5126,"count":540,"type":"VEC4","max":[0.999259889125824,0.98216503858566284,0.98798274993896484,-1.0],"min":[-0.99856418371200562,-0.99964511394500732,-0.99477094411849976,-1.0]},{"bufferView":3,"componentType":5126,"count":540,"type":"VEC2","max":[0.49392947554588318,-0.12775179743766785],"min":[0.00923137180507183,-0.49687236547470093]},{"bufferView":4,"componentType":5123,"count":2328,"type":"SCALAR","max":[539.0],"min":[0.0]}],"asset":{"version":"2.0"},"buffers":[{"uri":"Avatar_HandL.bin","byteLength":30576}],"bufferViews":[{"buffer":0,"byteLength":6480},{"buffer":0,"byteOffset":6480,"byteLength":6480},{"buffer":0,"byteOffset":12960,"byteLength":8640},{"buffer":0,"byteOffset":21600,"byteLength":4320},{"buffer":0,"byteOffset":25920,"byteLength":4656}],"images":[{"uri":"AvatarDodeca_Texture.png","name":"AvatarDodeca_Texture"}],"materials":[{"pbrMetallicRoughness":{"baseColorTexture":{"index":0},"metallicFactor":0.0},"name":"AvatarDodeca_Texture"}],"meshes":[{"primitives":[{"attributes":{"POSITION":0,"NORMAL":1,"TANGENT":2,"TEXCOORD_0":3},"indices":4,"material":0}],"name":"Avatar_HandL"}],"nodes":[{"mesh":0,"name":"Avatar_HandL"}],"samplers":[{}],"scene":0,"scenes":[{"nodes":[0],"name":"Avatar_HandL"}],"textures":[{"sampler":0,"source":0,"name":"AvatarDodeca_Texture"}]}
\ No newline at end of file
diff --git a/src/assets/avatars/dodec/AvatarHand_R/AvatarDodeca_Texture.png b/src/assets/avatars/dodec/AvatarHand_R/AvatarDodeca_Texture.png
deleted file mode 100644
index d0f89bc1497e8bc97c7c035abde9705e1316b613..0000000000000000000000000000000000000000
Binary files a/src/assets/avatars/dodec/AvatarHand_R/AvatarDodeca_Texture.png and /dev/null differ
diff --git a/src/assets/avatars/dodec/AvatarHand_R/Avatar_HandR.bin b/src/assets/avatars/dodec/AvatarHand_R/Avatar_HandR.bin
deleted file mode 100644
index 4861e7a7c94b1bb610660e8e1f1da95c0e8fd563..0000000000000000000000000000000000000000
Binary files a/src/assets/avatars/dodec/AvatarHand_R/Avatar_HandR.bin and /dev/null differ
diff --git a/src/assets/avatars/dodec/AvatarHand_R/Avatar_HandR.gltf b/src/assets/avatars/dodec/AvatarHand_R/Avatar_HandR.gltf
deleted file mode 100644
index d7b3042b39aafbdd63400397de57b0f3c4c5a8d8..0000000000000000000000000000000000000000
--- a/src/assets/avatars/dodec/AvatarHand_R/Avatar_HandR.gltf
+++ /dev/null
@@ -1 +0,0 @@
-{"accessors":[{"bufferView":0,"componentType":5126,"count":540,"type":"VEC3","max":[0.21345257759094238,0.31034931540489197,0.30054089426994324],"min":[-0.21502137184143066,-0.28918552398681641,-0.6024399995803833]},{"bufferView":1,"componentType":5126,"count":540,"type":"VEC3","max":[0.99751198291778564,0.97377252578735352,0.63385242223739624],"min":[-0.99698549509048462,-0.9912988543510437,-0.99289071559906006]},{"bufferView":2,"componentType":5126,"count":540,"type":"VEC4","max":[0.99856418371200562,0.98216503858566284,0.98798274993896484,1.0],"min":[-0.999259889125824,-0.99964511394500732,-0.99477094411849976,1.0]},{"bufferView":3,"componentType":5126,"count":540,"type":"VEC2","max":[0.99392950534820557,-0.12775179743766785],"min":[0.50923138856887817,-0.49687236547470093]},{"bufferView":4,"componentType":5123,"count":2328,"type":"SCALAR","max":[539.0],"min":[0.0]}],"asset":{"version":"2.0"},"buffers":[{"uri":"Avatar_HandR.bin","byteLength":30576}],"bufferViews":[{"buffer":0,"byteLength":6480},{"buffer":0,"byteOffset":6480,"byteLength":6480},{"buffer":0,"byteOffset":12960,"byteLength":8640},{"buffer":0,"byteOffset":21600,"byteLength":4320},{"buffer":0,"byteOffset":25920,"byteLength":4656}],"images":[{"uri":"AvatarDodeca_Texture.png","name":"AvatarDodeca_Texture"}],"materials":[{"pbrMetallicRoughness":{"baseColorTexture":{"index":0},"metallicFactor":0.0},"name":"AvatarDodeca_Texture"}],"meshes":[{"primitives":[{"attributes":{"POSITION":0,"NORMAL":1,"TANGENT":2,"TEXCOORD_0":3},"indices":4,"material":0}],"name":"Avatar_HandR"}],"nodes":[{"mesh":0,"name":"Avatar_HandR"}],"samplers":[{}],"scene":0,"scenes":[{"nodes":[0],"name":"Avatar_HandR"}],"textures":[{"sampler":0,"source":0,"name":"AvatarDodeca_Texture"}]}
\ No newline at end of file
diff --git a/src/assets/avatars/dodec/DodecAvatarGLTF/AvatarDodeca_Texture.png b/src/assets/avatars/dodec/DodecAvatarGLTF/AvatarDodeca_Texture.png
deleted file mode 100644
index 3f7585fbd2ec32b717296daedb765cad19b9daa7..0000000000000000000000000000000000000000
Binary files a/src/assets/avatars/dodec/DodecAvatarGLTF/AvatarDodeca_Texture.png and /dev/null differ
diff --git a/src/assets/avatars/dodec/DodecAvatarGLTF/DodecAvatar_Head.bin b/src/assets/avatars/dodec/DodecAvatarGLTF/DodecAvatar_Head.bin
deleted file mode 100644
index b4284d677642861c1d1520e36ea43cffc1e6516f..0000000000000000000000000000000000000000
Binary files a/src/assets/avatars/dodec/DodecAvatarGLTF/DodecAvatar_Head.bin and /dev/null differ
diff --git a/src/assets/avatars/dodec/DodecAvatarGLTF/DodecAvatar_Head.gltf b/src/assets/avatars/dodec/DodecAvatarGLTF/DodecAvatar_Head.gltf
deleted file mode 100644
index 94949cd5b070a5bf5f87703339f62076b020be3e..0000000000000000000000000000000000000000
--- a/src/assets/avatars/dodec/DodecAvatarGLTF/DodecAvatar_Head.gltf
+++ /dev/null
@@ -1 +0,0 @@
-{"accessors":[{"bufferView":0,"componentType":5126,"count":384,"type":"VEC3","max":[0.47052082419395447,0.47052082419395447,0.47052082419395447],"min":[-0.47052082419395447,-0.47052082419395447,-0.47052082419395447]},{"bufferView":1,"componentType":5126,"count":384,"type":"VEC3","max":[0.99266362190246582,0.987006425857544,1.0],"min":[-0.99266362190246582,-0.98700630664825439,-1.0]},{"bufferView":2,"componentType":5126,"count":384,"type":"VEC4","max":[1.0,1.0,0.987902820110321,1.0],"min":[-1.0,-1.0,-0.95196151733398438,-1.0]},{"bufferView":3,"componentType":5126,"count":384,"type":"VEC2","max":[0.99672305583953857,-0.5052105188369751],"min":[0.038782119750976563,-0.99697029590606689]},{"bufferView":4,"componentType":5123,"count":1464,"type":"SCALAR","max":[383.0],"min":[0.0]}],"asset":{"version":"2.0"},"buffers":[{"uri":"DodecAvatar_Head.bin","byteLength":21360}],"bufferViews":[{"buffer":0,"byteLength":4608},{"buffer":0,"byteOffset":4608,"byteLength":4608},{"buffer":0,"byteOffset":9216,"byteLength":6144},{"buffer":0,"byteOffset":15360,"byteLength":3072},{"buffer":0,"byteOffset":18432,"byteLength":2928}],"images":[{"uri":"AvatarDodeca_Texture.png","name":"AvatarDodeca_Texture"}],"materials":[{"pbrMetallicRoughness":{"baseColorTexture":{"index":0},"metallicFactor":0.0},"name":"AvatarDodeca_Texture"}],"meshes":[{"primitives":[{"attributes":{"POSITION":0,"NORMAL":1,"TANGENT":2,"TEXCOORD_0":3},"indices":4,"material":0}],"name":"DodecAvatar_Head"}],"nodes":[{"mesh":0,"name":"DodecAvatar_Head"}],"samplers":[{}],"scene":0,"scenes":[{"nodes":[0],"name":"DodecAvatar_Head"}],"textures":[{"sampler":0,"source":0,"name":"AvatarDodeca_Texture"}]}
\ No newline at end of file
diff --git a/src/avatar-selector.js b/src/avatar-selector.js
index ea3ff71a0f1f225cb9ae9f68a813167125bc3207..9f5aabab2417584d33a1d6eb43f75503a0212631 100644
--- a/src/avatar-selector.js
+++ b/src/avatar-selector.js
@@ -10,7 +10,6 @@ import "./vendor/GLTFLoader";
 import "./components/animation-mixer";
 import "./components/audio-feedback";
 import "./components/loop-animation";
-import "./elements/a-progressive-asset";
 import "./gltf-component-mappings";
 import { avatars } from "./assets/avatars/avatars";
 
diff --git a/src/components/audio-feedback.js b/src/components/audio-feedback.js
index 29aafcdc45bd1fb8838b4824a9c75f1c2f471c9e..d4bdf5792e79cc1eb3191a0d042f59e89308956d 100644
--- a/src/components/audio-feedback.js
+++ b/src/components/audio-feedback.js
@@ -77,6 +77,9 @@ AFRAME.registerComponent("scale-audio-feedback", {
   },
 
   onAudioFrequencyChange(e) {
+    // TODO: come up with a cleaner way to handle this.
+    // bone's are "hidden" by scaling them with bone-visibility, without this we would overwrite that.
+    if (!this.el.object3D.visible) return;
     const { minScale, maxScale } = this.data;
     this.el.object3D.scale.setScalar(minScale + (maxScale - minScale) * e.detail.volume / 255);
   }
diff --git a/src/components/bone-visibility.js b/src/components/bone-visibility.js
new file mode 100644
index 0000000000000000000000000000000000000000..6f6f1a53e6f3eb464561ea91741d6d09e33a093f
--- /dev/null
+++ b/src/components/bone-visibility.js
@@ -0,0 +1,16 @@
+AFRAME.registerComponent("bone-visibility", {
+  tick() {
+    const { visible } = this.el.object3D;
+
+    if (this.lastVisible !== visible) {
+      if (visible) {
+        this.el.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.lastVisible = visible;
+    }
+  }
+});
diff --git a/src/components/gltf-model-plus.js b/src/components/gltf-model-plus.js
index 49c891ebb9260f84be62720288dfe72b04fa0c62..3864f9327c92ce63deddf8f08766a9316cf4a3da 100644
--- a/src/components/gltf-model-plus.js
+++ b/src/components/gltf-model-plus.js
@@ -166,7 +166,7 @@ function nextTick() {
   });
 }
 
-function cachedLoadGLTF(src, onProgress) {
+function cachedLoadGLTF(src, preferredTechnique, onProgress) {
   return new Promise((resolve, reject) => {
     // Load the gltf model from the cache if it exists.
     if (GLTFCache[src]) {
@@ -174,7 +174,10 @@ function cachedLoadGLTF(src, onProgress) {
       resolve(cloneGltf(GLTFCache[src]));
     } else {
       // Otherwise load the new gltf model.
-      new THREE.GLTFLoader().load(
+      const gltfLoader = new THREE.GLTFLoader();
+      gltfLoader.preferredTechnique = preferredTechnique;
+
+      gltfLoader.load(
         src,
         model => {
           if (!GLTFCache[src]) {
@@ -193,7 +196,8 @@ function cachedLoadGLTF(src, onProgress) {
 AFRAME.registerComponent("gltf-model-plus", {
   schema: {
     src: { type: "string" },
-    inflate: { default: false }
+    inflate: { default: false },
+    preferredTechnique: { default: AFRAME.utils.device.isMobile() ? "KHR_materials_unlit" : "pbrMetallicRoughness" }
   },
 
   init() {
@@ -244,7 +248,7 @@ AFRAME.registerComponent("gltf-model-plus", {
         return;
       }
 
-      const model = await cachedLoadGLTF(src);
+      const model = await cachedLoadGLTF(src, this.data.preferredTechnique);
 
       // If we started loading something else already
       // TODO: there should be a way to cancel loading instead
diff --git a/src/components/ik-controller.js b/src/components/ik-controller.js
index 4fd89465e1d4ce97c24276b5d23d89eda5a25163..23f2dbf799f59558fd261eaa615f1bd2d37a66d2 100644
--- a/src/components/ik-controller.js
+++ b/src/components/ik-controller.js
@@ -1,5 +1,4 @@
 const { Vector3, Quaternion, Matrix4, Euler } = THREE;
-
 AFRAME.registerComponent("ik-root", {
   schema: {
     camera: { type: "string", default: ".camera" },
@@ -67,16 +66,12 @@ AFRAME.registerComponent("ik-controller", {
 
     this.hands = {
       left: {
-        lastVisible: true,
         rotation: new Matrix4().makeRotationFromEuler(new Euler(-Math.PI / 2, Math.PI / 2, 0))
       },
       right: {
-        lastVisible: true,
         rotation: new Matrix4().makeRotationFromEuler(new Euler(Math.PI / 2, Math.PI / 2, 0))
       }
     };
-
-    this.headLastVisible = true;
   },
 
   update(oldData) {
@@ -180,14 +175,6 @@ AFRAME.registerComponent("ik-controller", {
 
     this.updateHand(this.hands.left, leftHand, leftController);
     this.updateHand(this.hands.right, rightHand, rightController);
-
-    if (head.object3D.visible) {
-      if (!this.headLastVisible) {
-        head.object3D.scale.set(1, 1, 1);
-      }
-    } else if (this.headLastVisible) {
-      head.object3D.scale.set(0.0000001, 0.0000001, 0.0000001);
-    }
   },
 
   updateHand(handState, hand, controller) {
@@ -195,11 +182,13 @@ AFRAME.registerComponent("ik-controller", {
     const handMatrix = handObject3D.matrix;
     const controllerObject3D = controller.object3D;
 
+    // TODO: This coupling with personal-space-invader is not ideal.
+    // There should be some intermediate thing managing multiple opinions about object visibility
+    const spaceInvader = hand.components["personal-space-invader"];
+    const handHiddenByPersonalSpace = spaceInvader && spaceInvader.invading;
+
+    handObject3D.visible = !handHiddenByPersonalSpace && controllerObject3D.visible;
     if (controllerObject3D.visible) {
-      if (!handState.lastVisible) {
-        handObject3D.scale.set(1, 1, 1);
-        handState.lastVisible = true;
-      }
       handMatrix.multiplyMatrices(this.invRootToChest, controllerObject3D.matrix);
 
       const handControls = controller.components["hand-controls2"];
@@ -212,11 +201,6 @@ AFRAME.registerComponent("ik-controller", {
 
       handObject3D.position.setFromMatrixPosition(handMatrix);
       handObject3D.rotation.setFromRotationMatrix(handMatrix);
-    } else {
-      if (handState.lastVisible) {
-        handObject3D.scale.set(0.0000001, 0.0000001, 0.0000001);
-        handState.lastVisible = false;
-      }
     }
   }
 });
diff --git a/src/components/networked-counter.js b/src/components/networked-counter.js
index 9c4fb7105578b49b48d5abe61e4c92193a0aaede..39f76ddb7fb31482e86ddf4630f5d45d14c0d4e0 100644
--- a/src/components/networked-counter.js
+++ b/src/components/networked-counter.js
@@ -32,7 +32,7 @@ AFRAME.registerComponent("networked-counter", {
     }
 
     const id = NAF.utils.getNetworkId(networkedEl);
-    if (this.queue.hasOwnProperty(id)) {
+    if (id && this.queue.hasOwnProperty(id)) {
       return;
     }
 
@@ -61,7 +61,7 @@ AFRAME.registerComponent("networked-counter", {
 
   deregister: function(networkedEl) {
     const id = NAF.utils.getNetworkId(networkedEl);
-    if (this.queue.hasOwnProperty(id)) {
+    if (id && this.queue.hasOwnProperty(id)) {
       const item = this.queue[id];
       networkedEl.removeEventListener(this.data.grab_event, item.onGrabHandler);
       networkedEl.removeEventListener(this.data.release_event, item.onReleaseHandler);
diff --git a/src/elements/a-progressive-asset.js b/src/elements/a-progressive-asset.js
deleted file mode 100644
index db51b9b74484043a1f83847cc64d4ccc4bc6554c..0000000000000000000000000000000000000000
--- a/src/elements/a-progressive-asset.js
+++ /dev/null
@@ -1,67 +0,0 @@
-/**
- * Modified version of a-asset-item that adds high-src and low-src options
- * Extracted from https://github.com/aframevr/aframe/blob/master/src/core/a-assets.js
- */
-
-AFRAME.registerElement("a-progressive-asset", {
-  prototype: Object.create(AFRAME.ANode.prototype, {
-    createdCallback: {
-      value() {
-        this.data = null;
-        this.isAssetItem = true;
-      }
-    },
-
-    attachedCallback: {
-      value() {
-        if (!this.parentNode.fileLoader) {
-          throw new Error("a-progressive-asset must be the child of an a-assets element.");
-        }
-
-        this.fileLoader = this.parentNode.fileLoader;
-
-        const self = this;
-        const fallbackSrc = this.getAttribute("src");
-        const highSrc = this.getAttribute("high-src");
-        const lowSrc = this.getAttribute("low-src");
-
-        let src = fallbackSrc;
-
-        if (window.APP.quality === "high") {
-          src = highSrc;
-        } else if (window.APP.quality === "low") {
-          src = lowSrc;
-        }
-
-        this.fileLoader.setResponseType(this.getAttribute("response-type"));
-        this.fileLoader.load(
-          src,
-          function handleOnLoad(response) {
-            self.data = response;
-            /*
-            Workaround for a Chrome bug. If another XHR is sent to the same url before the
-            previous one closes, the second request never finishes.
-            setTimeout finishes the first request and lets the logic triggered by load open
-            subsequent requests.
-            setTimeout can be removed once the fix for the bug below ships:
-            https://bugs.chromium.org/p/chromium/issues/detail?id=633696&q=component%3ABlink%3ENetwork%3EXHR%20&colspec=ID%20Pri%20M%20Stars%20ReleaseBlock%20Component%20Status%20Owner%20Summary%20OS%20Modified
-          */
-            setTimeout(function load() {
-              AFRAME.ANode.prototype.load.call(self);
-            });
-          },
-          function handleOnProgress(xhr) {
-            self.emit("progress", {
-              loadedBytes: xhr.loaded,
-              totalBytes: xhr.total,
-              xhr: xhr
-            });
-          },
-          function handleOnError(xhr) {
-            self.emit("error", { xhr: xhr });
-          }
-        );
-      }
-    }
-  })
-});
diff --git a/src/hub.html b/src/hub.html
index 5ec16e968d5db5e2a2d3a9c55b468d74dfd49482..ac4efe9f8fe4a80a6c300fb206aca272a234576a 100644
--- a/src/hub.html
+++ b/src/hub.html
@@ -21,6 +21,7 @@
         networked-scene="adapter: janus; audio: true; debug: true; connectOnLoad: false;"
         physics
         mute-mic="eventSrc: a-scene; toggleEvents: action_mute"
+        personal-space-bubble="debug: false;"
 
         app-mode-input-mappings="modes: default, hud; actionSets: default, hud;"
         >
@@ -30,69 +31,16 @@
             <img id="muted"  src="./assets/hud/muted.png" >
             <img id="avatar"  src="./assets/hud/avatar.jpg" >
 
-            <a-progressive-asset
-                id="botdefault"
-                response-type="arraybuffer"
-                src="./assets/avatars/BotDefault_Avatar_Unlit.glb"
-                high-src="./assets/avatars/BotDefault_Avatar.glb"
-                low-src="./assets/avatars/BotDefault_Avatar_Unlit.glb"
-            ></a-progressive-asset>
-            <a-progressive-asset
-                id="botbobo"
-                response-type="arraybuffer"
-                src="./assets/avatars/BotBobo_Avatar_Unlit.glb"
-                high-src="./assets/avatars/BotBobo_Avatar.glb"
-                low-src="./assets/avatars/BotBobo_Avatar_Unlit.glb"
-            ></a-progressive-asset>
-            <a-progressive-asset
-                id="botdom"
-                response-type="arraybuffer"
-                src="./assets/avatars/BotDom_Avatar_Unlit.glb"
-                high-src="./assets/avatars/BotDom_Avatar.glb"
-                low-src="./assets/avatars/BotDom_Avatar_Unlit.glb"
-            ></a-progressive-asset>
-            <a-progressive-asset
-                id="botgreg"
-                response-type="arraybuffer"
-                src="./assets/avatars/BotGreg_Avatar_Unlit.glb"
-                high-src="./assets/avatars/BotGreg_Avatar.glb"
-                low-src="./assets/avatars/BotGreg_Avatar_Unlit.glb"
-            ></a-progressive-asset>
-            <a-progressive-asset
-                id="botguest"
-                response-type="arraybuffer"
-                src="./assets/avatars/BotGuest_Avatar_Unlit.glb"
-                high-src="./assets/avatars/BotGuest_Avatar.glb"
-                low-src="./assets/avatars/BotGuest_Avatar_Unlit.glb"
-            ></a-progressive-asset>
-            <a-progressive-asset
-                id="botjim"
-                response-type="arraybuffer"
-                src="./assets/avatars/BotJim_Avatar_Unlit.glb"
-                high-src="./assets/avatars/BotJim_Avatar.glb"
-                low-src="./assets/avatars/BotJim_Avatar_Unlit.glb"
-            ></a-progressive-asset>
-            <a-progressive-asset
-                id="botpinky"
-                response-type="arraybuffer"
-                src="./assets/avatars/BotPinky_Avatar_Unlit.glb"
-                high-src="./assets/avatars/BotPinky_Avatar.glb"
-                low-src="./assets/avatars/BotPinky_Avatar_Unlit.glb"
-            ></a-progressive-asset>
-            <a-progressive-asset
-                id="botrobert"
-                response-type="arraybuffer"
-                src="./assets/avatars/BotRobert_Avatar_Unlit.glb"
-                high-src="./assets/avatars/BotRobert_Avatar.glb"
-                low-src="./assets/avatars/BotRobert_Avatar_Unlit.glb"
-            ></a-progressive-asset>
-            <a-progressive-asset
-                id="botwoody"
-                response-type="arraybuffer"
-                src="./assets/avatars/BotWoody_Avatar_Unlit.glb"
-                high-src="./assets/avatars/BotWoody_Avatar.glb"
-                low-src="./assets/avatars/BotWoody_Avatar_Unlit.glb"
-            ></a-progressive-asset>
+            <a-asset-item id="botdefault" response-type="arraybuffer" src="https://asset-bundles-dev.reticulum.io/bots/BotDefault_Avatar-9f71f8ff22.gltf"></a-asset-item>
+            <a-asset-item id="botbobo" response-type="arraybuffer" src="https://asset-bundles-dev.reticulum.io/bots/BotBobo_Avatar-f9740a010b.gltf"></a-asset-item>
+            <a-asset-item id="botdom" response-type="arraybuffer" src="https://asset-bundles-dev.reticulum.io/bots/BotDom_Avatar-6aa1b5d781.gltf"></a-asset-item>
+            <a-asset-item id="botgreg" response-type="arraybuffer" src="https://asset-bundles-dev.reticulum.io/bots/BotGreg_Avatar-98d39797bb.gltf"></a-asset-item>
+            <a-asset-item id="botguest" response-type="arraybuffer" src="https://asset-bundles-dev.reticulum.io/bots/BotGuest_Avatar-78cd857332.gltf"></a-asset-item>
+            <a-asset-item id="botjim" response-type="arraybuffer" src="https://asset-bundles-dev.reticulum.io/bots/BotJim_Avatar-d28005a687.gltf"></a-asset-item>
+            <a-asset-item id="botkev" response-type="arraybuffer" src="https://asset-bundles-dev.reticulum.io/bots/BotKev_Avatar-a95787bb51.gltf"></a-asset-item>
+            <a-asset-item id="botpinky" response-type="arraybuffer" src="https://asset-bundles-dev.reticulum.io/bots/BotPinky_Avatar-b0b93f8675.gltf"></a-asset-item>
+            <a-asset-item id="botrobert" response-type="arraybuffer" src="https://asset-bundles-dev.reticulum.io/bots/BotRobert_Avatar-e9554880f3.gltf"></a-asset-item>
+            <a-asset-item id="botwoody" response-type="arraybuffer" src="https://asset-bundles-dev.reticulum.io/bots/BotWoody_Avatar-0140485a23.gltf"></a-asset-item>
 
             <a-asset-item id="watch-model" response-type="arraybuffer" src="./assets/hud/watch.glb"></a-asset-item>
             <a-asset-item id="interactable-duck" response-type="arraybuffer" src="./assets/interactables/duck/DuckyMesh.glb"></a-asset-item>
@@ -115,7 +63,7 @@
 
                     <a-entity class="model" gltf-model-plus="inflate: true">
                         <template data-selector=".RootScene">
-                            <a-entity ik-controller animation-mixer></a-entity>
+                            <a-entity ik-controller animation-mixer space-invader-mesh="meshSelector: .Bot_Skinned"></a-entity>
                         </template>
 
                         <template data-selector=".Neck">
@@ -130,21 +78,26 @@
                              </a-entity>
                         </template>
 
+                        <template data-selector=".Chest">
+                            <a-entity personal-space-invader="radius: 0.2; useMaterial: true;" bone-visibility></a-entity>
+                        </template>
+
                         <template data-selector=".Head">
                             <a-entity
                                 networked-audio-source
                                 networked-audio-analyser
-                                personal-space-invader
+                                personal-space-invader="radius: 0.15; useMaterial: true;"
+                                bone-visibility
                             >
                             </a-entity>
                         </template>
 
                         <template data-selector=".LeftHand">
-                            <a-entity personal-space-invader ></a-entity>
+                            <a-entity personal-space-invader="radius: 0.1" bone-visibility></a-entity>
                         </template>
 
                         <template data-selector=".RightHand">
-                            <a-entity personal-space-invader ></a-entity>
+                            <a-entity personal-space-invader="radius: 0.1" bone-visibility></a-entity>
                         </template>
                     </a-entity>
                 </a-entity>
@@ -233,7 +186,7 @@
                 class="camera"
                 camera
                 position="0 1.6 0"
-                personal-space-bubble
+                personal-space-bubble="radius: 0.4"
                 look-controls
             ></a-entity>
 
@@ -281,11 +234,11 @@
                 </template>
 
                 <template data-selector=".Head">
-                    <a-entity visible="false"></a-entity>
+                    <a-entity visible="false" bone-visibility></a-entity>
                 </template>
 
                 <template data-selector=".LeftHand">
-                    <a-entity>
+                    <a-entity bone-visibility>
                         <a-entity
                             id="watch"
                             gltf-model-plus="src: #watch-model"
@@ -304,7 +257,7 @@
                 </template>
 
                 <template data-selector=".RightHand">
-                    <a-entity>
+                    <a-entity bone-visibility>
                         <a-entity
                             event-repeater="events: action_grab, action_release; eventSource: #player-right-controller"
                             static-body="shape: sphere; sphereRadius: 0.02"
diff --git a/src/hub.js b/src/hub.js
index 8a4a9dbe2b412af7ad7c09ef2313719c9e67cecd..007a48f6660a82b0e1af4faa58e4332d67a492c0 100644
--- a/src/hub.js
+++ b/src/hub.js
@@ -23,6 +23,7 @@ import "./components/wasd-to-analog2d"; //Might be a behaviour or activator in t
 import "./components/mute-mic";
 import "./components/audio-feedback";
 import "./components/bone-mute-state-indicator";
+import "./components/bone-visibility";
 import "./components/in-world-hud";
 import "./components/virtual-gamepad-controls";
 import "./components/ik-controller";
@@ -66,8 +67,6 @@ if (qs.quality) {
   window.APP.quality = isMobile ? "low" : "high";
 }
 
-import "./elements/a-progressive-asset";
-
 import "aframe-physics-system";
 import "aframe-physics-extras";
 import "super-hands";
diff --git a/src/input-mappings.js b/src/input-mappings.js
index a5338250079c08c94d79edb84457ff4e3b4e7b65..035110dd677efa748c280ae0d6b8f14fde1ae6f4 100644
--- a/src/input-mappings.js
+++ b/src/input-mappings.js
@@ -31,14 +31,19 @@ const config = {
   mappings: {
     default: {
       "vive-controls": {
-        menudown: "action_mute",
+        menudown: ["action_mute", "thumb_down"],
+        menuup: "thumb_up",
         "trackpad.pressedmove": { left: "move" },
         trackpad_dpad4_pressed_west_down: { right: "snap_rotate_left" },
         trackpad_dpad4_pressed_east_down: { right: "snap_rotate_right" },
         trackpad_dpad4_pressed_center_down: { right: "action_teleport_down" },
         trackpadup: { right: "action_teleport_up" },
-        gripdown: "action_grab",
-        gripup: "action_release"
+        gripdown: ["action_grab", "middle_ring_pinky_down", "index_down"],
+        gripup: ["action_release", "middle_ring_pinky_up", "index_up"],
+        trackpadtouchstart: "thumb_down",
+        trackpadtouchend: "thumb_up",
+        triggerdown: "index_down",
+        triggerup: "index_up"
       },
       "oculus-touch-controls": {
         joystick_dpad4_west: {
diff --git a/src/react-components/avatar-selector.js b/src/react-components/avatar-selector.js
index 0f5c0c4ca1a040b25424850aba6ca7bc6118f2b0..f6bc25d9fab0122ba74f226ed9d6a4c20e2d6c26 100644
--- a/src/react-components/avatar-selector.js
+++ b/src/react-components/avatar-selector.js
@@ -46,13 +46,7 @@ class AvatarSelector extends Component {
 
   render() {
     const avatarAssets = this.props.avatars.map(avatar => (
-      <a-progressive-asset
-        id={avatar.id}
-        key={avatar.id}
-        response-type="arraybuffer"
-        high-src={`${avatar.models.high}`}
-        low-src={`${avatar.models.low}`}
-      />
+      <a-asset-item id={avatar.id} key={avatar.id} response-type="arraybuffer" src={`${avatar.model}`} />
     ));
 
     const avatarEntities = this.props.avatars.map((avatar, i) => (
diff --git a/src/react-components/ui-root.js b/src/react-components/ui-root.js
index 83ddd1177f5be71efe796f02043bdceedb2cc6cd..946784e9e1f6e18aa6a412a86d350f39a1772a24 100644
--- a/src/react-components/ui-root.js
+++ b/src/react-components/ui-root.js
@@ -131,6 +131,10 @@ class UIRoot extends Component {
       this.enterDaydream();
     } else if (this.props.forcedVREntryType === "gearvr") {
       this.enterGearVR();
+    } else if (this.props.forcedVREntryType === "vr") {
+      this.enterVR();
+    } else if (this.props.forcedVREntryType === "2d") {
+      this.enter2D();
     }
   };
 
@@ -246,18 +250,22 @@ class UIRoot extends Component {
   };
 
   enterGearVR = async () => {
-    this.exit();
+    if (this.state.availableVREntryTypes.gearvr === VR_DEVICE_AVAILABILITY.yes) {
+      await this.performDirectEntryFlow(true);
+    } else {
+      this.exit();
 
-    // Launch via Oculus Browser
-    const location = window.location;
-    const qs = queryString.parse(location.search);
-    qs.vr_entry_type = "gearvr"; // Auto-choose 'gearvr' after landing in Oculus Browser
+      // Launch via Oculus Browser
+      const location = window.location;
+      const qs = queryString.parse(location.search);
+      qs.vr_entry_type = "gearvr"; // Auto-choose 'gearvr' after landing in Oculus Browser
 
-    const ovrwebUrl =
-      `ovrweb://${location.protocol || "http:"}//${location.host}` +
-      `${location.pathname || ""}?${queryString.stringify(qs)}#${location.hash || ""}`;
+      const ovrwebUrl =
+        `ovrweb://${location.protocol || "http:"}//${location.host}` +
+        `${location.pathname || ""}?${queryString.stringify(qs)}#${location.hash || ""}`;
 
-    window.location = ovrwebUrl;
+      window.location = ovrwebUrl;
+    }
   };
 
   enterDaydream = async () => {
@@ -356,12 +364,12 @@ class UIRoot extends Component {
       mediaStream.addTrack(this.state.audioTrack);
 
       const AudioContext = window.AudioContext || window.webkitAudioContext;
-      const audioContext = new AudioContext();
-      const source = audioContext.createMediaStreamSource(mediaStream);
-      const analyzer = audioContext.createAnalyser();
+      const micLevelAudioContext = new AudioContext();
+      const micSource = micLevelAudioContext.createMediaStreamSource(mediaStream);
+      const analyzer = micLevelAudioContext.createAnalyser();
       const levels = new Uint8Array(analyzer.fftSize);
 
-      source.connect(analyzer);
+      micSource.connect(analyzer);
 
       const micUpdateInterval = setInterval(() => {
         analyzer.getByteTimeDomainData(levels);
@@ -383,7 +391,7 @@ class UIRoot extends Component {
         this.props.store.update({ lastUsedMicDeviceId: micDeviceId });
       }
 
-      this.setState({ micUpdateInterval });
+      this.setState({ micLevelAudioContext, micUpdateInterval });
     }
 
     this.setState({ mediaStream });
@@ -471,6 +479,12 @@ class UIRoot extends Component {
     }
 
     this.stopTestTone();
+
+    if (this.state.micLevelAudioContext) {
+      this.state.micLevelAudioContext.close();
+      clearInterval(this.state.micUpdateInterval);
+    }
+
     this.setState({ entryStep: ENTRY_STEPS.finished });
   };
 
diff --git a/src/systems/personal-space-bubble.js b/src/systems/personal-space-bubble.js
index e696705e138159a67c79e9707fe97bada64a9a58..bfd648a96fb7f98129b2cd1231f3466932ab4f93 100644
--- a/src/systems/personal-space-bubble.js
+++ b/src/systems/personal-space-bubble.js
@@ -2,93 +2,188 @@ const invaderPos = new AFRAME.THREE.Vector3();
 const bubblePos = new AFRAME.THREE.Vector3();
 
 AFRAME.registerSystem("personal-space-bubble", {
+  schema: {
+    debug: { default: false }
+  },
+
   init() {
     this.invaders = [];
     this.bubbles = [];
   },
 
-  registerBubble(el) {
-    this.bubbles.push(el);
+  registerBubble(bubble) {
+    this.bubbles.push(bubble);
   },
 
-  unregisterBubble(el) {
-    const index = this.bubbles.indexOf(el);
+  unregisterBubble(bubble) {
+    const index = this.bubbles.indexOf(bubble);
 
     if (index !== -1) {
       this.bubbles.splice(index, 1);
     }
   },
 
-  registerInvader(el) {
-    NAF.utils.getNetworkedEntity(el).then(networkedEl => {
+  registerInvader(invader) {
+    NAF.utils.getNetworkedEntity(invader.el).then(networkedEl => {
       const owner = NAF.utils.getNetworkOwner(networkedEl);
 
       if (owner !== NAF.clientId) {
-        this.invaders.push(el);
+        this.invaders.push(invader);
       }
     });
   },
 
-  unregisterInvader(el) {
-    const index = this.invaders.indexOf(el);
+  unregisterInvader(invader) {
+    const index = this.invaders.indexOf(invader);
 
     if (index !== -1) {
       this.invaders.splice(index, 1);
     }
   },
 
+  update() {
+    for (let i = 0; i < this.bubbles.length; i++) {
+      this.bubbles[i].updateDebug();
+    }
+
+    for (let i = 0; i < this.invaders.length; i++) {
+      this.invaders[i].updateDebug();
+    }
+  },
+
   tick() {
     // Update matrix positions once for each space bubble and space invader
     for (let i = 0; i < this.bubbles.length; i++) {
-      this.bubbles[i].object3D.updateMatrixWorld(true);
+      this.bubbles[i].el.object3D.updateMatrixWorld(true);
     }
 
     for (let i = 0; i < this.invaders.length; i++) {
-      this.invaders[i].object3D.updateMatrixWorld(true);
+      this.invaders[i].el.object3D.updateMatrixWorld(true);
+      this.invaders[i].setInvading(false);
     }
 
     // Loop through all of the space bubbles (usually one)
     for (let i = 0; i < this.bubbles.length; i++) {
       const bubble = this.bubbles[i];
 
-      bubblePos.setFromMatrixPosition(bubble.object3D.matrixWorld);
-
-      const radius = bubble.components["personal-space-bubble"].data.radius;
-      const radiusSquared = radius * radius;
+      bubblePos.setFromMatrixPosition(bubble.el.object3D.matrixWorld);
 
       // Hide the invader if inside the bubble
       for (let j = 0; j < this.invaders.length; j++) {
         const invader = this.invaders[j];
 
-        invaderPos.setFromMatrixPosition(invader.object3D.matrixWorld);
-
-        const distanceSquared = bubblePos.distanceTo(invaderPos);
+        invaderPos.setFromMatrixPosition(invader.el.object3D.matrixWorld);
 
-        invader.object3D.visible = distanceSquared > radiusSquared;
+        const distanceSquared = bubblePos.distanceToSquared(invaderPos);
+        const radiusSum = bubble.data.radius + invader.data.radius;
+        if (distanceSquared < radiusSum * radiusSum) {
+          invader.setInvading(true);
+        }
       }
     }
   }
 });
 
+function createSphereGizmo(radius) {
+  const geometry = new THREE.SphereBufferGeometry(radius, 10, 10);
+  const wireframe = new THREE.WireframeGeometry(geometry);
+  const line = new THREE.LineSegments(wireframe);
+  line.material.opacity = 0.5;
+  line.material.transparent = true;
+  return line;
+}
+
+// TODO: we need to come up with a more generic way of doing this as this is very specific to our avatars.
+AFRAME.registerComponent("space-invader-mesh", {
+  schema: {
+    meshSelector: { type: "string" }
+  },
+  init() {
+    this.targetMesh = this.el.querySelector(this.data.meshSelector).object3DMap.skinnedmesh;
+  }
+});
+
+function findInvaderMesh(entity) {
+  while (entity && !(entity.components && entity.components["space-invader-mesh"])) {
+    entity = entity.parentNode;
+  }
+  return entity && entity.components["space-invader-mesh"].targetMesh;
+}
+
+const DEBUG_OBJ = "psb-debug";
+
 AFRAME.registerComponent("personal-space-invader", {
+  schema: {
+    radius: { type: "number", default: 0.1 },
+    useMaterial: { default: false },
+    debug: { default: false },
+    invadingOpacity: { default: 0.3 }
+  },
+
   init() {
-    this.el.sceneEl.systems["personal-space-bubble"].registerInvader(this.el);
+    const system = this.el.sceneEl.systems["personal-space-bubble"];
+    system.registerInvader(this);
+    if (this.data.useMaterial) {
+      const mesh = findInvaderMesh(this.el);
+      if (mesh) {
+        this.targetMaterial = mesh.material;
+      }
+    }
+    this.invading = false;
+  },
+
+  update() {
+    this.radiusSquared = this.data.radius * this.data.radius;
+    this.updateDebug();
+  },
+
+  updateDebug() {
+    const system = this.el.sceneEl.systems["personal-space-bubble"];
+    if (system.data.debug || this.data.debug) {
+      !this.el.object3DMap[DEBUG_OBJ] && this.el.setObject3D(DEBUG_OBJ, createSphereGizmo(this.data.radius));
+    } else if (this.el.object3DMap[DEBUG_OBJ]) {
+      this.el.removeObject3D(DEBUG_OBJ);
+    }
   },
 
   remove() {
-    this.el.sceneEl.systems["personal-space-bubble"].unregisterInvader(this.el);
+    this.el.sceneEl.systems["personal-space-bubble"].unregisterInvader(this);
+  },
+
+  setInvading(invading) {
+    if (this.targetMaterial) {
+      this.targetMaterial.opacity = invading ? this.data.invadingOpacity : 1;
+      this.targetMaterial.transparent = invading;
+    } else {
+      this.el.object3D.visible = !invading;
+    }
+    this.invading = invading;
   }
 });
 
 AFRAME.registerComponent("personal-space-bubble", {
   schema: {
-    radius: { type: "number", default: 0.8 }
+    radius: { type: "number", default: 0.8 },
+    debug: { default: false }
   },
   init() {
-    this.system.registerBubble(this.el);
+    this.system.registerBubble(this);
+  },
+
+  update() {
+    this.radiusSquared = this.data.radius * this.data.radius;
+    this.updateDebug();
+  },
+
+  updateDebug() {
+    if (this.system.data.debug || this.data.debug) {
+      !this.el.object3DMap[DEBUG_OBJ] && this.el.setObject3D(DEBUG_OBJ, createSphereGizmo(this.data.radius));
+    } else if (this.el.object3DMap[DEBUG_OBJ]) {
+      this.el.removeObject3D(DEBUG_OBJ);
+    }
   },
 
   remove() {
-    this.system.unregisterBubble(this.el);
+    this.system.unregisterBubble(this);
   }
 });
diff --git a/src/utils/vr-caps-detect.js b/src/utils/vr-caps-detect.js
index d0c96d8fbff5b277622a28cd55cb649fc4054efd..5f312c23b7a20ca33f7f2d88fc9bd192d90df609 100644
--- a/src/utils/vr-caps-detect.js
+++ b/src/utils/vr-caps-detect.js
@@ -41,6 +41,7 @@ const GENERIC_ENTRY_TYPE_DEVICE_BLACKLIST = [/cardboard/i];
 export async function getAvailableVREntryTypes() {
   const isWebVRCapableBrowser = !!navigator.getVRDisplays;
   const isSamsungBrowser = browser.name === "chrome" && navigator.userAgent.match(/SamsungBrowser/);
+  const isOculusBrowser = navigator.userAgent.match(/Oculus/);
   const isDaydreamCapableBrowser = !!(isWebVRCapableBrowser && browser.name === "chrome" && !isSamsungBrowser);
 
   let generic = VR_DEVICE_AVAILABILITY.no;
@@ -49,7 +50,11 @@ export async function getAvailableVREntryTypes() {
   // We only consider GearVR support as "maybe" and never "yes". The only browser
   // that will detect GearVR outside of VR is Samsung Internet, and we'd prefer to launch into Oculus
   // Browser for now since Samsung Internet requires an additional WebVR installation + flag, so return "maybe".
-  const gearvr = isMaybeGearVRCompatibleDevice() ? VR_DEVICE_AVAILABILITY.maybe : VR_DEVICE_AVAILABILITY.no;
+  //
+  // If we are in Oculus Browser (ie, we are literally wearing a GearVR) then return 'yes'.
+  const gearvr = isMaybeGearVRCompatibleDevice()
+    ? isOculusBrowser ? VR_DEVICE_AVAILABILITY.yes : VR_DEVICE_AVAILABILITY.maybe
+    : VR_DEVICE_AVAILABILITY.no;
 
   // For daydream detection, we first check if they are on an Android compatible device, and assume they
   // may support daydream *unless* this browser has WebVR capabilities, in which case we can do better.
diff --git a/src/vendor/GLTFLoader.js b/src/vendor/GLTFLoader.js
index 0992a18dba1a474bed8999035e6c5e1376b568b3..25e397ce98c7ebc2be929195c6863338154c01bc 100644
--- a/src/vendor/GLTFLoader.js
+++ b/src/vendor/GLTFLoader.js
@@ -1,4 +1,5 @@
 // https://github.com/mrdoob/three.js/blob/1e943ba79196737bc8505522e928595687c09425/examples/js/loaders/GLTFLoader.js
+// + MOZ_alt_materials draft extension
 
 /**
  * @author Rich Tibbett / https://github.com/richtr
@@ -14,6 +15,7 @@ THREE.GLTFLoader = ( function () {
 
 		this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
 		this.dracoLoader = null;
+		this.preferredTechnique = null;
 
 	}
 
@@ -125,6 +127,12 @@ THREE.GLTFLoader = ( function () {
 
 			if ( json.extensionsUsed ) {
 
+				if ( json.extensionsUsed.indexOf( EXTENSIONS.MOZ_ALT_MATERIALS ) >= 0 ) {
+
+					extensions[ EXTENSIONS.MOZ_ALT_MATERIALS ] = new MOZAltMaterialsExtension( this.preferredTechnique );
+
+				}
+
 				if ( json.extensionsUsed.indexOf( EXTENSIONS.KHR_LIGHTS ) >= 0 ) {
 
 					extensions[ EXTENSIONS.KHR_LIGHTS ] = new GLTFLightsExtension( json );
@@ -226,7 +234,8 @@ THREE.GLTFLoader = ( function () {
 		KHR_DRACO_MESH_COMPRESSION: 'KHR_draco_mesh_compression',
 		KHR_LIGHTS: 'KHR_lights',
 		KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS: 'KHR_materials_pbrSpecularGlossiness',
-		KHR_MATERIALS_UNLIT: 'KHR_materials_unlit'
+		KHR_MATERIALS_UNLIT: 'KHR_materials_unlit',
+		MOZ_ALT_MATERIALS: "MOZ_alt_materials"
 	};
 
 	/**
@@ -874,6 +883,32 @@ THREE.GLTFLoader = ( function () {
 
 	}
 
+	function MOZAltMaterialsExtension ( preferredTechnique ) {
+
+		this.name = EXTENSIONS.MOZ_ALT_MATERIALS;
+		this.preferredTechnique = preferredTechnique;
+
+	}
+
+	MOZAltMaterialsExtension.prototype.getAltMaterial = function ( materialDef, materials ) {
+
+		var mamExtension = materialDef.extensions && materialDef.extensions[ EXTENSIONS.MOZ_ALT_MATERIALS ];
+
+		if ( mamExtension ) {
+
+			if ( this.preferredTechnique && mamExtension[ this.preferredTechnique ] !== undefined ) {
+
+				var altMaterialIndex = mamExtension[ this.preferredTechnique ];
+				return materials[ altMaterialIndex ];
+
+			}
+
+			return materialDef;
+
+		}
+
+	};
+
 	/*********************************/
 	/********** INTERPOLATION ********/
 	/*********************************/
@@ -1855,6 +1890,13 @@ THREE.GLTFLoader = ( function () {
 		var extensions = this.extensions;
 		var materialDef = this.json.materials[ materialIndex ];
 
+		if ( materialDef.extensions && materialDef.extensions[ EXTENSIONS.MOZ_ALT_MATERIALS ] ) {
+
+			var mamExtension = extensions[ EXTENSIONS.MOZ_ALT_MATERIALS ];
+			materialDef = mamExtension.getAltMaterial( materialDef, this.json.materials );
+
+		}
+
 		var materialType;
 		var materialParams = {};
 		var materialExtensions = materialDef.extensions || {};
diff --git a/webpack.config.js b/webpack.config.js
index 4b97e17db1898080575dd3220ae34bfc3e3757c8..4f9b26d26cab4c3959a5b1796696f7a0b531c086 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -117,14 +117,7 @@ const config = {
         loader: "html-loader",
         options: {
           // <a-asset-item>'s src property is overwritten with the correct transformed asset url.
-          attrs: [
-            "img:src",
-            "a-asset-item:src",
-            "a-progressive-asset:src",
-            "a-progressive-asset:high-src",
-            "a-progressive-asset:low-src",
-            "audio:src"
-          ],
+          attrs: ["img:src", "a-asset-item:src", "audio:src"],
           // You can get transformed asset urls in an html template using ${require("pathToFile.ext")}
           interpolate: "require"
         }
diff --git a/yarn.lock b/yarn.lock
index 3700485ccf05f997515f8417ec2e63e4963816fd..41b0864f101f538ab31881607f082cfbc8b50cd7 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -5249,9 +5249,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/networked-aframe/networked-aframe":
-  version "0.6.0"
-  resolved "https://github.com/networked-aframe/networked-aframe#be5b2e949ec63fc44a1c6ab40b627cee5fda057e"
+"networked-aframe@https://github.com/mozillareality/networked-aframe#mr-social-client/master":
+  version "0.5.1"
+  resolved "https://github.com/mozillareality/networked-aframe#5d2f50ddf65140f0ae671b2b53c1c667de18dca5"
   dependencies:
     aframe-lerp-component "^1.1.0"
     easyrtc "1.1.0"