diff --git a/src/components/in-world-hud.js b/src/components/in-world-hud.js
index 6b6c28bfc2cc97f1a3a776f9c4c12edd867457a3..df42d283b956bfa2b90da54977a716ee1f072849 100644
--- a/src/components/in-world-hud.js
+++ b/src/components/in-world-hud.js
@@ -6,15 +6,17 @@ AFRAME.registerComponent("in-world-hud", {
   init() {
     this.mic = this.el.querySelector(".mic");
     this.freeze = this.el.querySelector(".freeze");
+    this.bubble = this.el.querySelector(".bubble");
 
     this.updateButtonStates = () => {
       this.mic.setAttribute("icon-button", "active", this.el.sceneEl.is("muted"));
       this.freeze.setAttribute("icon-button", "active", this.el.sceneEl.is("frozen"));
+      this.bubble.setAttribute("icon-button", "active", this.el.sceneEl.is("spacebubble"));
     };
     this.updateButtonStates();
 
     this.onStateChange = evt => {
-      if (!(evt.detail === "muted" || evt.detail === "frozen")) return;
+      if (!(evt.detail === "muted" || evt.detail === "frozen" || evt.detail === "spacebubble")) return;
       this.updateButtonStates();
     };
 
@@ -25,6 +27,10 @@ AFRAME.registerComponent("in-world-hud", {
     this.onFreezeClick = () => {
       this.el.emit("action_freeze");
     };
+
+    this.onBubbleClick = () => {
+      this.el.emit("action_space_bubble");
+    };
   },
 
   play() {
@@ -33,6 +39,7 @@ AFRAME.registerComponent("in-world-hud", {
 
     this.mic.addEventListener("click", this.onMicClick);
     this.freeze.addEventListener("click", this.onFreezeClick);
+    this.bubble.addEventListener("click", this.onBubbleClick);
   },
 
   pause() {
@@ -41,5 +48,6 @@ AFRAME.registerComponent("in-world-hud", {
 
     this.mic.removeEventListener("click", this.onMicClick);
     this.freeze.removeEventListener("click", this.onFreezeClick);
+    this.bubble.removeEventListener("click", this.onBubbleClick);
   }
 });
diff --git a/src/hub.html b/src/hub.html
index 1b793828d4a5d20f7b9ae2c82e15fd22a174e085..2be65beb8e88b9f05a9fd8e06cb1553bac03a3fa 100644
--- a/src/hub.html
+++ b/src/hub.html
@@ -188,7 +188,7 @@
                     <a-rounded height="0.13" width="0.48" color="#000000" position="-0.24 -0.065 0" radius="0.065" opacity="0.35" class="hud bg"></a-rounded>
                     <a-image icon-button="image: #mute-off; hoverImage: #mute-off-hover; activeImage: #mute-on; activeHoverImage: #mute-on-hover" scale="0.1 0.1 0.1" position="-0.17 0 0.001" class="hud mic" material="alphaTest:0.1;"></a-image>
                     <a-image icon-button="image: #freeze-off; hoverImage: #freeze-off-hover; activeImage: #freeze-on; activeHoverImage: #freeze-on-hover" scale="0.2 0.2 0.2" position="0 0 0.001" class="hud freeze"></a-image>
-                    <a-image icon-button="image: #bubble-off; hoverImage: #bubble-off-hover; activeImage: #bubble-on; activeHoverImage: #bubble-on-hover" scale="0.1 0.1 0.1" position="0.17 0 0.001" class="hud mic" material="alphaTest:0.1;"></a-image>
+                    <a-image icon-button="image: #bubble-off; hoverImage: #bubble-off-hover; activeImage: #bubble-on; activeHoverImage: #bubble-on-hover" scale="0.1 0.1 0.1" position="0.17 0 0.001" class="hud bubble" material="alphaTest:0.1;"></a-image>
                 </a-entity>
             </a-entity>
 
diff --git a/src/react-components/2d-hud.js b/src/react-components/2d-hud.js
index d02d6deb206275df0a2389d31a6b9354c035de4f..eff928870432936ca41843202bbdfc073adaf032 100644
--- a/src/react-components/2d-hud.js
+++ b/src/react-components/2d-hud.js
@@ -4,7 +4,7 @@ import cx from "classnames";
 
 import styles from "../assets/stylesheets/2d-hud.scss";
 
-const TwoDHUD = ({ muted, frozen, onToggleMute, onToggleFreeze }) => (
+const TwoDHUD = ({ muted, frozen, spacebubble, onToggleMute, onToggleFreeze, onToggleSpaceBubble }) => (
   <div className={styles.container}>
     <div className={cx("ui-interactive", styles.panel, styles.left)}>
       <div className={cx(styles.iconButton, styles.mute, { [styles.active]: muted })} onClick={onToggleMute} />
@@ -14,7 +14,10 @@ const TwoDHUD = ({ muted, frozen, onToggleMute, onToggleFreeze }) => (
       onClick={onToggleFreeze}
     />
     <div className={cx("ui-interactive", styles.panel, styles.right)}>
-      <div className={cx(styles.iconButton, styles.bubble, { [styles.active]: muted })} onClick={onToggleMute} />
+      <div
+        className={cx(styles.iconButton, styles.bubble, { [styles.active]: spacebubble })}
+        onClick={onToggleSpaceBubble}
+      />
     </div>
   </div>
 );
@@ -22,8 +25,10 @@ const TwoDHUD = ({ muted, frozen, onToggleMute, onToggleFreeze }) => (
 TwoDHUD.propTypes = {
   muted: PropTypes.bool,
   frozen: PropTypes.bool,
+  spacebubble: PropTypes.bool,
   onToggleMute: PropTypes.func,
-  onToggleFreeze: PropTypes.func
+  onToggleFreeze: PropTypes.func,
+  onToggleSpaceBubble: PropTypes.func
 };
 
 export default TwoDHUD;
diff --git a/src/react-components/ui-root.js b/src/react-components/ui-root.js
index 7ff3d25a675e484879619f00a26427b90592e26e..5bc31eaeed4f0262f1a34e2a373ee44b7696d822 100644
--- a/src/react-components/ui-root.js
+++ b/src/react-components/ui-root.js
@@ -92,6 +92,7 @@ class UIRoot extends Component {
 
     muted: false,
     frozen: false,
+    spacebubble: true,
 
     exited: false,
 
@@ -128,9 +129,9 @@ class UIRoot extends Component {
     this.setState({ sceneLoaded: true });
   };
 
-  // TODO: mute state should probably actually just live in react land
+  // TODO: we need to come up with a cleaner way to handle the shared state between aframe and react than emmitting events and setting state on the scene
   onAframeStateChanged = e => {
-    if (!(e.detail === "muted" || e.detail === "frozen")) return;
+    if (!(e.detail === "muted" || e.detail === "frozen" || e.detail === "spacebubble")) return;
     this.setState({
       [e.detail]: this.props.scene.is(e.detail)
     });
@@ -144,6 +145,10 @@ class UIRoot extends Component {
     this.props.scene.emit("action_freeze");
   };
 
+  toggleSpaceBubble = () => {
+    this.props.scene.emit("action_space_bubble");
+  };
+
   handleForcedVREntryType = () => {
     if (!this.props.forcedVREntryType) return;
 
@@ -774,8 +779,10 @@ class UIRoot extends Component {
             <TwoDHUD
               muted={this.state.muted}
               frozen={this.state.frozen}
+              spacebubble={this.state.spacebubble}
               onToggleMute={this.toggleMute}
               onToggleFreeze={this.toggleFreeze}
+              onToggleSpaceBubble={this.toggleSpaceBubble}
             />
           ) : null}
         </div>
diff --git a/src/systems/personal-space-bubble.js b/src/systems/personal-space-bubble.js
index bfd648a96fb7f98129b2cd1231f3466932ab4f93..d89a59cae4ff907f777f98d6327ef704cc1e38e3 100644
--- a/src/systems/personal-space-bubble.js
+++ b/src/systems/personal-space-bubble.js
@@ -3,12 +3,17 @@ const bubblePos = new AFRAME.THREE.Vector3();
 
 AFRAME.registerSystem("personal-space-bubble", {
   schema: {
-    debug: { default: false }
+    debug: { default: false },
+    enabled: { default: true }
   },
 
   init() {
     this.invaders = [];
     this.bubbles = [];
+
+    this.el.addEventListener("action_space_bubble", () => {
+      this.el.setAttribute("personal-space-bubble", { enabled: !this.data.enabled });
+    });
   },
 
   registerBubble(bubble) {
@@ -48,10 +53,21 @@ AFRAME.registerSystem("personal-space-bubble", {
 
     for (let i = 0; i < this.invaders.length; i++) {
       this.invaders[i].updateDebug();
+      if (!this.data.enabled) {
+        this.invaders[i].setInvading(false);
+      }
+    }
+
+    if (this.data.enabled) {
+      this.el.addState("spacebubble");
+    } else {
+      this.el.removeState("spacebubble");
     }
   },
 
   tick() {
+    if (!this.data.enabled) return;
+
     // Update matrix positions once for each space bubble and space invader
     for (let i = 0; i < this.bubbles.length; i++) {
       this.bubbles[i].el.object3D.updateMatrixWorld(true);