diff --git a/src/components/css-class.js b/src/components/css-class.js
new file mode 100644
index 0000000000000000000000000000000000000000..1528ed4d450aea61f63b7426524229ed63609044
--- /dev/null
+++ b/src/components/css-class.js
@@ -0,0 +1,17 @@
+AFRAME.registerComponent("css-class", {
+  schema: {
+    type: "string"
+  },
+  init() {
+    this.el.classList.add(this.data);
+  },
+  update(oldData) {
+    if (this.data !== oldData) {
+      this.el.classList.remove(oldData);
+      this.el.classList.add(this.data);
+    }
+  },
+  remove() {
+    this.el.classList.remove(this.data);
+  }
+});
diff --git a/src/components/scene-shadow.js b/src/components/scene-shadow.js
new file mode 100644
index 0000000000000000000000000000000000000000..92d434480b5487cb9abe2c993a115158c87fa553
--- /dev/null
+++ b/src/components/scene-shadow.js
@@ -0,0 +1,30 @@
+// For use in environment gltf bundles to set scene shadow properties.
+AFRAME.registerComponent("scene-shadow", {
+  schema: {
+    autoUpdate: {
+      type: "boolean",
+      default: true
+    },
+    type: {
+      type: "string",
+      default: "pcf"
+    },
+    renderReverseSided: {
+      type: "boolean",
+      default: true
+    },
+    renderSingleSided: {
+      type: "boolean",
+      default: true
+    }
+  },
+  init() {
+    this.originalShadowProperties = this.el.sceneEl.getAttribute("shadow");
+  },
+  update(oldData) {
+    this.el.sceneEl.setAttribute("shadow", this.data);
+  },
+  remove() {
+    this.el.sceneEl.setAttribute("shadow", this.originalShadowProperties);
+  }
+});
diff --git a/src/components/water.js b/src/components/water.js
index b7f176131ea97f6f47fbbb31b27c59e80350b78b..dcdbace5edf334d6dcb9b05439057b405c4d9d13 100644
--- a/src/components/water.js
+++ b/src/components/water.js
@@ -148,7 +148,7 @@ AFRAME.registerComponent("water", {
     distance: { type: "number", default: 1 },
     speed: { type: "number", default: 0.1 },
     forceMobile: { type: "boolean", default: false },
-    normalMap: { type: "asset" }
+    normalMap: { type: "asset", default: "#water-normal-map" }
   },
   init() {
     const waterGeometry = new THREE.PlaneBufferGeometry(800, 800);
diff --git a/src/gltf-component-mappings.js b/src/gltf-component-mappings.js
index e2177fb25a1025ad56e9b135626b3abca64b1d97..0f7cbb703b429aef55b51e3903c0ed9182adefb6 100644
--- a/src/gltf-component-mappings.js
+++ b/src/gltf-component-mappings.js
@@ -1,6 +1,17 @@
 import "./components/gltf-model-plus";
 import { resolveURL } from "./utils/resolveURL";
 
+AFRAME.GLTFModelPlus.registerComponent("css-class", "css-class");
+AFRAME.GLTFModelPlus.registerComponent("scene-shadow", "scene-shadow");
+AFRAME.GLTFModelPlus.registerComponent("super-spawner", "super-spawner");
+AFRAME.GLTFModelPlus.registerComponent("gltf-model-plus", "gltf-model-plus");
+AFRAME.GLTFModelPlus.registerComponent("body", "body");
+AFRAME.GLTFModelPlus.registerComponent("hide-when-quality", "hide-when-quality");
+AFRAME.GLTFModelPlus.registerComponent("light", "light");
+AFRAME.GLTFModelPlus.registerComponent("skybox", "skybox");
+AFRAME.GLTFModelPlus.registerComponent("layers", "layers");
+AFRAME.GLTFModelPlus.registerComponent("xr", "xr");
+AFRAME.GLTFModelPlus.registerComponent("water", "water");
 AFRAME.GLTFModelPlus.registerComponent("scale-audio-feedback", "scale-audio-feedback");
 AFRAME.GLTFModelPlus.registerComponent("loop-animation", "loop-animation");
 AFRAME.GLTFModelPlus.registerComponent("shape", "shape");
diff --git a/src/hub.html b/src/hub.html
index 581bc3ffe008dc53cb4be3c414c119935af629e6..6632c5c928f4162635386509df8fa8df2fcf5924 100644
--- a/src/hub.html
+++ b/src/hub.html
@@ -177,15 +177,6 @@
         <!-- Interactables -->
         <a-entity id="counter" networked-counter="max: 3; ttl: 120"></a-entity>
 
-        <a-entity 
-            gltf-model-plus="src: #interactable-duck"
-            scale="2 2 2"
-            class="interactable" 
-            super-spawner="template: #interactable-template;" 
-            position="2.9 1.2 0" 
-            body="mass: 0; type: static; shape: box;"
-        ></a-entity>
-
         <a-entity
             id="cursor-controller"
             cursor-controller="
@@ -351,37 +342,12 @@
             </a-entity>
         </a-entity>
 
-        <!-- Lights -->
-        <a-entity
-            hide-when-quality="low"
-            light="type: directional; color: #F9FFCE; intensity: 0.6"
-            position="0.002 5.231 -15.3"
-        ></a-entity>
-
         <!-- Environment -->
         <a-entity 
             id="environment-root" 
             nav-mesh-helper
             static-body="shape: none;"
         ></a-entity>
-
-        <a-entity
-            id="skybox"
-            scale="8000 8000 8000"
-            skybox="azimuth:0.280; inclination:0.440"
-            light="type: ambient; color: #FFF"
-            layers="reflection:true"
-            xr="ar: false"
-        ></a-entity>
-
-        <a-entity
-            id="water"
-            water="forceMobile: true; normalMap:#water-normal-map"
-            rotation="-90 0 0"
-            position="0 -88.358 -332.424"
-            xr="ar: false"
-        ></a-entity>
-
     </a-scene>
 
     <div id="ui-root"></div>
diff --git a/src/hub.js b/src/hub.js
index 26b9e1f368908a0b94a4612193cd722d1b11f38b..f9cd5144a4e892f10202a9df10b78ceb6505897e 100644
--- a/src/hub.js
+++ b/src/hub.js
@@ -57,6 +57,8 @@ import "./components/block-button";
 import "./components/visible-while-frozen";
 import "./components/stats-plus";
 import "./components/networked-avatar";
+import "./components/css-class";
+import "./components/scene-shadow";
 
 import ReactDOM from "react-dom";
 import React from "react";