diff --git a/src/room.js b/src/room.js
index a687dca43f54086f2462b926338cb4fe55366298..014069ae03729397260ca0d152cdf3a107d067d3 100644
--- a/src/room.js
+++ b/src/room.js
@@ -40,6 +40,7 @@ import "./components/spawn-controller";
 import "./components/bone-visibility";
 
 import "./systems/personal-space-bubble";
+import "./systems/app-mode";
 
 import "./elements/a-gltf-entity";
 
diff --git a/src/systems/app-mode.js b/src/systems/app-mode.js
new file mode 100644
index 0000000000000000000000000000000000000000..10f26cf0f1563760a143e26577cb67c8e94815fc
--- /dev/null
+++ b/src/systems/app-mode.js
@@ -0,0 +1,75 @@
+/* global AFRAME, console, setTimeout, clearTimeout */
+
+const AppModes = Object.freeze({ DEFAULT: "default", HUD: "hud" });
+AFRAME.registerSystem("app-mode", {
+  init() {
+    console.log("init app mode system");
+    this.setMode(AppModes.DEFAULT);
+  },
+
+  setMode(newMode) {
+    if (Object.values(AppModes).includes(newMode) && newMode !== this.mode) {
+      this.mode = newMode;
+      this.el.emit("appmode-change", { mode: this.mode });
+    }
+  }
+});
+
+AFRAME.registerComponent("mode-responder-toggle", {
+  multiple: true,
+  schema: {
+    mode: { type: "string" },
+    invert: { type: "boolean", default: false }
+  },
+
+  init() {
+    const AppModeSystem = this.el.sceneEl.systems["app-mode"];
+    this.el.sceneEl.addEventListener("appmode-change", e => {
+      this.updateComponentState(e.detail.mode === this.data.mode);
+    });
+    this.updateComponentState(AppModeSystem.mode === this.data.mode);
+  },
+
+  updateComponentState(isModeActive) {
+    const componentName = this.id;
+    this.el.components[componentName][isModeActive !== this.data.invert ? "play" : "pause"]();
+  }
+});
+
+AFRAME.registerComponent("mode-responder-hudstate", {
+  init() {
+    this.el.sceneEl.addEventListener("appmode-change", e => {
+      switch (e.detail.mode) {
+        case AppModes.HUD:
+          this.el.setAttribute("material", "color", "green");
+          this.el.setAttribute("scale", "2 1 1");
+          break;
+        case AppModes.DEFAULT:
+          this.el.setAttribute("material", "color", "white");
+          this.el.setAttribute("scale", "0.3 0.3 1");
+          break;
+      }
+    });
+  }
+});
+
+AFRAME.registerComponent("hud-detector", {
+  dependencies: ["raycaster"],
+
+  init() {
+    const AppModeSystem = this.el.sceneEl.systems["app-mode"];
+
+    let hoverTimeout;
+    this.el.addEventListener("raycaster-intersection", e => {
+      console.log("raycast hit", e.detail.els, e.detail.intersections);
+      hoverTimeout = setTimeout(() => {
+        AppModeSystem.setMode(AppModes.HUD);
+      }, 500);
+    });
+    this.el.addEventListener("raycaster-intersection-cleared", e => {
+      console.log("raycast clear", e.detail.clearedEls);
+      AppModeSystem.setMode(AppModes.DEFAULT);
+      clearTimeout(hoverTimeout);
+    });
+  }
+});
diff --git a/templates/room.hbs b/templates/room.hbs
index d56e30738aa48e8a0ffff9cbeb070f009f4cef91..1803a9c4d4048f8f505e488f880f786c1d51a98b 100644
--- a/templates/room.hbs
+++ b/templates/room.hbs
@@ -115,8 +115,19 @@
             spawn-controller="radius: 4;"
             wasd-to-analog2d
             character-controller="pivot: #player-camera"
+            mode-responder-toggle__character-controller="mode: hud; invert: true;"
             ik-root
         >
+            <a-plane
+                class="hud"
+                mode-responder-hudstate
+                position="0 0.7 -0.7"
+                rotation="-28.9 0 0"
+                scale="0.3 0.3 1"
+                material="side:double"
+            >
+            </a-plane>
+
             <a-entity
                 id="player-camera"
                 class="camera"
@@ -124,6 +135,8 @@
                 position="0 1.6 0"
                 personal-space-bubble
                 look-controls
+                raycaster="objects: .hud; interval: 100; showLine: true; direction: 0 0 -1; origin: 0 -0.05 0;"
+                hud-detector
             ></a-entity>
             
             <a-entity