diff --git a/src/components/icon-button.js b/src/components/icon-button.js
new file mode 100644
index 0000000000000000000000000000000000000000..eab80803f4d85ad425a6d5a0c71fc8f6f9ddc333
--- /dev/null
+++ b/src/components/icon-button.js
@@ -0,0 +1,57 @@
+AFRAME.registerComponent("icon-button", {
+  schema: {
+    image: { type: "string" },
+    hoverImage: { type: "string" },
+    activeImage: { type: "string" },
+    activeHoverImage: { type: "string" },
+    active: { type: "boolean" },
+    haptic: { type: "selector" }
+  },
+
+  init() {
+    this.onHover = () => {
+      this.hovering = true;
+      this.updateButtonState();
+      this.emitHapticPulse();
+    };
+    this.onHoverOut = () => {
+      this.hovering = false;
+      this.updateButtonState();
+    };
+    this.onClick = () => {
+      this.emitHapticPulse();
+    };
+  },
+
+  emitHapticPulse() {
+    if (this.data.haptic) {
+      this.data.haptic.emit("haptic_pulse", { intensity: "low" });
+    }
+  },
+
+  play() {
+    this.updateButtonState();
+    this.el.addEventListener("mouseover", this.onHover);
+    this.el.addEventListener("mouseout", this.onHoverOut);
+    this.el.addEventListener("click", this.onClick);
+  },
+
+  pause() {
+    this.el.removeEventListener("mouseover", this.onHover);
+    this.el.removeEventListener("mouseout", this.onHoverOut);
+    this.el.removeEventListener("click", this.onClick);
+  },
+
+  update() {
+    this.updateButtonState();
+  },
+
+  updateButtonState() {
+    const hovering = this.hovering;
+    const active = this.data.active;
+
+    const image = active ? (hovering ? "activeHoverImage" : "activeImage") : hovering ? "hoverImage" : "image";
+
+    this.el.setAttribute("src", this.data[image]);
+  }
+});
diff --git a/src/components/in-world-hud.js b/src/components/in-world-hud.js
index 9e307e498256da344e5e2e3533baf82b09f93b6f..6b6c28bfc2cc97f1a3a776f9c4c12edd867457a3 100644
--- a/src/components/in-world-hud.js
+++ b/src/components/in-world-hud.js
@@ -5,99 +5,41 @@ AFRAME.registerComponent("in-world-hud", {
   },
   init() {
     this.mic = this.el.querySelector(".mic");
+    this.freeze = this.el.querySelector(".freeze");
 
-    const muted = this.el.sceneEl.is("muted");
-    this.mic.setAttribute("src", muted ? "#muted" : "#unmuted");
-
-    this.showCorrectMuteState = () => {
-      const muted = this.el.sceneEl.is("muted");
-      this.mic.setAttribute("src", muted ? "#muted" : "#unmuted");
+    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.updateButtonStates();
 
     this.onStateChange = evt => {
-      if (evt.detail !== "muted") return;
-      this.showCorrectMuteState();
-    };
-
-    this.onMicHover = () => {
-      this.hoveredOnMic = true;
-      this.data.haptic.emit("haptic_pulse", { intensity: "low" });
-      this.mic.setAttribute("material", "color", "#1DD");
-    };
-
-    this.onMicHoverExit = () => {
-      this.hoveredOnMic = false;
-      this.mic.setAttribute("material", "color", "#FFF");
-      this.showCorrectMuteState();
+      if (!(evt.detail === "muted" || evt.detail === "frozen")) return;
+      this.updateButtonStates();
     };
 
-    this.onMicDown = () => {
-      this.data.haptic.emit("haptic_pulse", { intensity: "medium" });
-      this.el.sceneEl.removeEventListener("micAudio", this.onAudioFrequencyChange);
-      this.mic.setAttribute("material", "color", this.el.sceneEl.is("muted") ? "#0FA" : "#F33");
+    this.onMicClick = () => {
       this.el.emit("action_mute");
-      window.setTimeout(() => {
-        this.mic.setAttribute("material", "color", "#FFF");
-        this.el.sceneEl.addEventListener("micAudio", this.onAudioFrequencyChange);
-      }, 150);
-    };
-
-    this.onClick = () => {
-      if (this.hoveredOnMic) {
-        this.onMicDown();
-      }
     };
 
-    this.onAudioFrequencyChange = e => {
-      if (this.hoveredOnMic) return;
-      const red = 1.0 - e.detail.volume / 10.0;
-      this.mic.object3DMap.mesh.material.color = { r: red, g: 9, b: 9 };
+    this.onFreezeClick = () => {
+      this.el.emit("action_freeze");
     };
-
-    this.el.sceneEl.addEventListener("mediaStream", evt => {
-      this.ms = evt.detail.ms;
-      const ctx = THREE.AudioContext.getContext();
-      const source = ctx.createMediaStreamSource(this.ms);
-      this.analyser = ctx.createAnalyser();
-      this.levels = new Uint8Array(this.analyser.frequencyBinCount);
-      source.connect(this.analyser);
-    });
   },
 
   play() {
-    this.mic.addEventListener("raycaster-intersected", this.onMicHover);
-    this.mic.addEventListener("raycaster-intersected-cleared", this.onMicHoverExit);
-
     this.el.sceneEl.addEventListener("stateadded", this.onStateChange);
     this.el.sceneEl.addEventListener("stateremoved", this.onStateChange);
 
-    this.el.addEventListener("click", this.onClick);
-
-    this.el.sceneEl.addEventListener("micAudio", this.onAudioFrequencyChange);
+    this.mic.addEventListener("click", this.onMicClick);
+    this.freeze.addEventListener("click", this.onFreezeClick);
   },
 
   pause() {
     this.el.sceneEl.removeEventListener("stateadded", this.onStateChange);
     this.el.sceneEl.removeEventListener("stateremoved", this.onStateChange);
 
-    this.el.removeEventListener("click", this.onClick);
-
-    this.el.sceneEl.removeEventListener("micAudio", this.onAudioFrequencyChange);
-  },
-
-  tick: function() {
-    if (!this.analyser) return;
-
-    this.analyser.getByteFrequencyData(this.levels);
-
-    let sum = 0;
-    for (let i = 0; i < this.levels.length; i++) {
-      sum += this.levels[i];
-    }
-    this.volume = sum / this.levels.length;
-    this.el.emit("micAudio", {
-      volume: this.volume,
-      levels: this.levels
-    });
+    this.mic.removeEventListener("click", this.onMicClick);
+    this.freeze.removeEventListener("click", this.onFreezeClick);
   }
 });
diff --git a/src/hub.html b/src/hub.html
index 973461f270e5b2f412421d8f3ba5ed06b4dc3328..8f5491682717686a3eb09374018d020830983b0a 100644
--- a/src/hub.html
+++ b/src/hub.html
@@ -29,9 +29,18 @@
         >
 
         <a-assets>
-            <img id="unmuted"  src="./assets/hud/unmuted.png" >
-            <img id="muted"  src="./assets/hud/muted.png" >
-            <img id="avatar"  src="./assets/hud/avatar.png" >
+            <img id="mute_off"  src="./assets/hud/mute_off.png" >
+            <img id="mute_off-hover"  src="./assets/hud/mute_off-hover.png" >
+            <img id="mute_on"  src="./assets/hud/mute_on.png" >
+            <img id="mute_on-hover"  src="./assets/hud/mute_on-hover.png" >
+            <img id="bubble_off"  src="./assets/hud/bubble_off.png" >
+            <img id="bubble_off-hover"  src="./assets/hud/bubble_off-hover.png" >
+            <img id="bubble_on"  src="./assets/hud/bubble_on.png" >
+            <img id="bubble_on-hover"  src="./assets/hud/bubble_on-hover.png" >
+            <img id="freeze_off"  src="./assets/hud/freeze_off.png" >
+            <img id="freeze_off-hover"  src="./assets/hud/freeze_off-hover.png" >
+            <img id="freeze_on"  src="./assets/hud/freeze_on.png" >
+            <img id="freeze_on-hover"  src="./assets/hud/freeze_on-hover.png" >
 
             <a-asset-item id="botdefault" response-type="arraybuffer" src="https://asset-bundles-prod.reticulum.io/bots/BotDefault_Avatar-9f71f8ff22.gltf"></a-asset-item>
             <a-asset-item id="botbobo" response-type="arraybuffer" src="https://asset-bundles-prod.reticulum.io/bots/BotBobo_Avatar-f9740a010b.gltf"></a-asset-item>
@@ -176,10 +185,10 @@
                 vr-mode-toggle-playing__hud-controller
             >
                 <a-entity in-world-hud="haptic:#player-right-controller;raycaster:#player-right-controller;" rotation="30 0 0">
-                    <a-rounded height="0.13" width="0.6" color="#000000" position="-0.3 -0.065 0" radius="0.065" opacity="0.35" class="hud bg"></a-rounded>
-                    <a-image src="#unmuted" scale="0.1 0.1 0.1" position="-0.2 0 0.001" class="hud mic" material="alphaTest:0.1;"></a-image>
-                    <a-image src="#avatar" scale="0.2 0.2 0.2" position="0 0 0.001" class="hud avatar"></a-image>
-                    <a-image src="#unmuted" scale="0.1 0.1 0.1" position="0.2 0 0.001" class="hud mic" material="alphaTest:0.1;"></a-image>
+                    <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-entity>
             </a-entity>
 
diff --git a/src/hub.js b/src/hub.js
index 34d2812823ff6b2aa3926dbf5f20afa6c47da577..699c124090ab680333c04b122da5fce65d7b541e 100644
--- a/src/hub.js
+++ b/src/hub.js
@@ -50,6 +50,7 @@ import "./components/gltf-model-plus";
 import "./components/gltf-bundle";
 import "./components/hud-controller";
 import "./components/freeze-controller";
+import "./components/icon-button";
 
 import ReactDOM from "react-dom";
 import React from "react";