diff --git a/src/components/cursor-controller.js b/src/components/cursor-controller.js
index d4a1cd9cc6fdf38d13ee7e60843bc4144bca0aad..909f969d0ca9276c5a54922ef273beb96d87c3e7 100644
--- a/src/components/cursor-controller.js
+++ b/src/components/cursor-controller.js
@@ -126,7 +126,6 @@ AFRAME.registerComponent("cursor-controller", {
         .applyQuaternion(this.controllerQuaternion)
         .normalize();
     }
-
     this.el.setAttribute("raycaster", { origin: this.origin, direction: this.direction });
 
     let intersection = null;
@@ -293,7 +292,7 @@ AFRAME.registerComponent("cursor-controller", {
   _updateController: function() {
     this.hasPointingDevice = this.controllerQueue.length > 0 && this.inVR;
 
-    this.setCursorVisibility(this.hasPointingDevice || this.isMobile);
+    this.setCursorVisibility(this.hasPointingDevice || this.isMobile || (!this.isMobile && !this.inVR));
 
     if (this.hasPointingDevice) {
       const controllerData = this.controllerQueue[0];
diff --git a/src/hub.js b/src/hub.js
index e92f94f05b9bba9811343b9e835dd8650d0e8e16..c1efb954d2b6b38c48a8d7c3c48306506553bb3d 100644
--- a/src/hub.js
+++ b/src/hub.js
@@ -124,7 +124,8 @@ import { generateDefaultProfile, generateRandomName } from "./utils/identity.js"
 import { getAvailableVREntryTypes, VR_DEVICE_AVAILABILITY } from "./utils/vr-caps-detect.js";
 import ConcurrentLoadDetector from "./utils/concurrent-load-detector.js";
 import TouchEventsHandler from "./utils/touch-events-handler.js";
-import { MouseEventsHandler, GearVRMouseEventsHandler } from "./utils/mouse-events-handler.js";
+import MouseEventsHandler from "./utils/mouse-events-handler.js";
+import GearVRMouseEventsHandler from "./utils/gearvr-mouse-events-handler.js";
 import PrimaryActionHandler from "./utils/primary-action-handler.js";
 
 function qsTruthy(param) {
@@ -230,6 +231,7 @@ const onReady = async () => {
 
   const enterScene = async (mediaStream, enterInVR, hubId) => {
     const scene = document.querySelector("a-scene");
+    scene.style.cursor = "none";
     scene.renderer.sortObjects = true;
     const playerRig = document.querySelector("#player-rig");
     document.querySelector("canvas").classList.remove("blurred");
@@ -237,6 +239,40 @@ const onReady = async () => {
 
     if (enterInVR) {
       scene.enterVR();
+      if (isMobile) {
+        // Set up GearVR event handling
+        // TODO: Only use this when using gearvr
+        window.APP.gearvrMouseEventsHandler = new GearVRMouseEventsHandler();
+        const teleportEl = document.querySelector("#gaze-teleport");
+        if (teleportEl && teleportEl.components && teleportEl.components["teleport-controls"]) {
+          const teleportControls = teleportEl.components["teleport-controls"];
+          window.APP.gearvrMouseEventsHandler.registerGazeTeleporter(teleportControls);
+        } else {
+          const registerTeleporter = e => {
+            if (e.detail.name !== "teleport-controls") return;
+            teleportEl.removeEventListener("componentinitialized", registerTeleporter);
+            const teleportControls = teleportEl.components["teleport-controls"];
+            window.APP.gearvrMouseEventsHandler.registerGazeTeleporter(teleportControls);
+          };
+          teleportEl.addEventListener("componentinitialized", registerTeleporter);
+        }
+
+        const cursorEl = document.querySelector("#cursor-controller");
+        if (cursorEl && cursorEl.components && cursorEl.components["cursor-controller"]) {
+          const cursor = cursorEl.components["cursor-controller"];
+          window.APP.gearvrMouseEventsHandler.registerCursor(cursor);
+        } else {
+          const registerCursor = e => {
+            if (e.detail.name !== "cursor-controller") return;
+            cursorEl.removeEventListener("componentinitialized", registerCursor);
+            const cursor = cursorEl.components["cursor-controller"];
+            window.APP.gearvrMouseEventsHandler.registerCursor(cursor);
+          };
+          cursorEl.addEventListener("componentinitialized", registerCursor);
+        }
+      }
+
+      // Set up event handling for anything emitting "action_primary_down/up" and "action_grab/release"
       window.APP.primaryActionHandler = new PrimaryActionHandler(scene);
 
       const cursorEl = document.querySelector("#cursor-controller");
@@ -246,27 +282,34 @@ const onReady = async () => {
       } else {
         const registerCursor = e => {
           if (e.detail.name !== "cursor-controller") return;
+          cursorEl.removeEventListener("componentinitialized", registerCursor);
           const cursor = cursorEl.components["cursor-controller"];
           window.APP.primaryActionHandler.registerCursor(cursor);
         };
         cursorEl.addEventListener("componentinitialized", registerCursor);
       }
     } else {
-      window.APP.touchEventsHandler = new TouchEventsHandler();
-      window.APP.mouseEventsHandler = new MouseEventsHandler();
-      window.APP.gearvrMouseEventsHandler = new GearVRMouseEventsHandler(); // TODO: Use when gearvr is detected
+      if (isMobile) {
+        window.APP.touchEventsHandler = new TouchEventsHandler();
+      } else {
+        window.APP.mouseEventsHandler = new MouseEventsHandler();
+      }
 
       const camera = document.querySelector("#player-camera");
       const registerCameraController = e => {
         if (e.detail.name !== "camera-controller") return;
         camera.removeEventListener("componentinitialized", registerCameraController);
 
-        window.APP.touchEventsHandler.registerCameraController(camera.components["camera-controller"]);
-        scene.components["look-on-mobile"].registerCameraController(camera.components["camera-controller"]);
-        scene.setAttribute("look-on-mobile", "enabled", true);
+        if (window.APP.touchEventsHandler) {
+          window.APP.touchEventsHandler.registerCameraController(camera.components["camera-controller"]);
+          scene.components["look-on-mobile"].registerCameraController(camera.components["camera-controller"]);
+          scene.setAttribute("look-on-mobile", "enabled", true);
+        }
 
-        window.APP.mouseEventsHandler.registerCameraController(camera.components["camera-controller"]);
-        window.APP.mouseEventsHandler.setInverseMouseLook(qsTruthy("invertMouseLook"));
+        if (window.APP.mouseEventsHandler) {
+          window.APP.mouseEventsHandler.registerCameraController(camera.components["camera-controller"]);
+          window.APP.mouseEventsHandler.setInverseMouseLook(qsTruthy("invertMouseLook"));
+        }
       };
       camera.addEventListener("componentinitialized", registerCameraController);
       camera.setAttribute("camera-controller", "foo", "bar");
@@ -274,16 +317,25 @@ const onReady = async () => {
       const cursorEl = document.querySelector("#cursor-controller");
       if (cursorEl && cursorEl.components && cursorEl.components["cursor-controller"]) {
         const cursor = cursorEl.components["cursor-controller"];
-        window.APP.touchEventsHandler.registerPinchEmitter(cursorEl);
-        window.APP.touchEventsHandler.registerCursor(cursor);
-        window.APP.mouseEventsHandler.registerCursor(cursor);
+        if (window.APP.touchEventsHandler) {
+          window.APP.touchEventsHandler.registerPinchEmitter(cursorEl);
+          window.APP.touchEventsHandler.registerCursor(cursor);
+        }
+        if (window.APP.mouseEventsHandler) {
+          window.APP.mouseEventsHandler.registerCursor(cursor);
+        }
       } else {
         const registerCursor = e => {
           if (e.detail.name !== "cursor-controller") return;
+          cursorEl.removeEventListener("componentinitialized", registerCursor);
           const cursor = cursorEl.components["cursor-controller"];
-          window.APP.touchEventsHandler.registerPinchEmitter(cursorEl);
-          window.APP.touchEventsHandler.registerCursor(cursor);
-          window.APP.mouseEventsHandler.registerCursor(cursor);
+          if (window.APP.touchEventsHandler) {
+            window.APP.touchEventsHandler.registerPinchEmitter(cursorEl);
+            window.APP.touchEventsHandler.registerCursor(cursor);
+          }
+          if (window.APP.mouseEventsHandler) {
+            window.APP.mouseEventsHandler.registerCursor(cursor);
+          }
         };
         cursorEl.addEventListener("componentinitialized", registerCursor);
       }
diff --git a/src/utils/gearvr-mouse-events-handler.js b/src/utils/gearvr-mouse-events-handler.js
new file mode 100644
index 0000000000000000000000000000000000000000..6f00064696ffc33a1209618aa0eddbe57ab5d19a
--- /dev/null
+++ b/src/utils/gearvr-mouse-events-handler.js
@@ -0,0 +1,62 @@
+export default class GearVRMouseEventsHandler {
+  constructor() {
+    this.cursor = null;
+    this.gazeTeleporter = null;
+    this.isMouseDownHandledByCursor = false;
+    this.isMouseDownHandledByGazeTeleporter = false;
+
+    this.registerCursor = this.registerCursor.bind(this);
+    this.registerGazeTeleporter = this.registerGazeTeleporter.bind(this);
+    this.isReady = this.isReady.bind(this);
+    this.addEventListeners = this.addEventListeners.bind(this);
+    this.onMouseDown = this.onMouseDown.bind(this);
+    this.onMouseUp = this.onMouseUp.bind(this);
+  }
+
+  registerCursor(cursor) {
+    this.cursor = cursor;
+    if (this.isReady()) {
+      this.addEventListeners();
+    }
+  }
+
+  registerGazeTeleporter(gazeTeleporter) {
+    this.gazeTeleporter = gazeTeleporter;
+    if (this.isReady()) {
+      this.addEventListeners();
+    }
+  }
+
+  isReady() {
+    return this.cursor && this.gazeTeleporter;
+  }
+
+  addEventListeners() {
+    document.addEventListener("mousedown", this.onMouseDown);
+    document.addEventListener("mouseup", this.onMouseUp);
+  }
+
+  onMouseDown() {
+    this.isMouseDownHandledByCursor = this.cursor.startInteraction();
+    if (this.isMouseDownHandledByCursor) {
+      return;
+    }
+
+    const button = this.gazeTeleporter.data.button;
+    this.gazeTeleporter.el.emit(button + "down");
+    this.isMouseDownHandledByGazeTeleporter = true;
+  }
+
+  onMouseUp() {
+    if (this.isMouseDownHandledByCursor) {
+      this.cursor.endInteraction();
+      this.isMouseDownHandledByCursor = false;
+    }
+
+    if (this.isMouseDownHandledByGazeTeleporter) {
+      const button = this.gazeTeleporter.data.button;
+      this.gazeTeleporter.el.emit(button + "up");
+      this.isMouseDownHandledByGazeTeleporter = false;
+    }
+  }
+}
diff --git a/src/utils/mouse-events-handler.js b/src/utils/mouse-events-handler.js
index 52147c86c60972034bd4195412c4c22ff2ba61c9..4463d76fb2b08c7e818de2b8146ada13a1b98030 100644
--- a/src/utils/mouse-events-handler.js
+++ b/src/utils/mouse-events-handler.js
@@ -2,7 +2,7 @@
 const HORIZONTAL_LOOK_SPEED = 0.1;
 const VERTICAL_LOOK_SPEED = 0.06;
 
-export class MouseEventsHandler {
+export default class MouseEventsHandler {
   constructor() {
     this.cursor = null;
     this.cameraController = null;
@@ -113,65 +113,3 @@ export class MouseEventsHandler {
     this.cameraController.look(deltaPitch, deltaYaw);
   }
 }
-
-//TODO: Finish gearvr mouse events handler
-export class GearVRMouseEventsHandler {
-  constructor() {
-    this.cursor = null;
-    this.gazeTeleporter = null;
-    this.isMouseDownHandledByCursor = false;
-    this.isMouseDownHandledByGazeTeleporter = false;
-
-    this.registerCursor = this.registerCursor.bind(this);
-    this.registerGazeTeleporter = this.registerGazeTeleporter.bind(this);
-    this.isReady = this.isReady.bind(this);
-    this.addEventListeners = this.addEventListeners.bind(this);
-    this.onMouseDown = this.onMouseDown.bind(this);
-    this.onMouseUp = this.onMouseUp.bind(this);
-  }
-
-  registerCursor(cursor) {
-    this.cursor = cursor;
-    if (this.isReady()) {
-      this.addEventListeners();
-    }
-  }
-
-  registerGazeTeleporter(gazeTeleporter) {
-    this.gazeTeleporter = gazeTeleporter;
-    if (this.isReady()) {
-      this.addEventListeners();
-    }
-  }
-
-  isReady() {
-    return this.cursor && this.gazeTeleporter;
-  }
-
-  addEventListeners() {
-    document.addEventListener("mousedown", this.onMouseDown);
-    document.addEventListener("mouseup", this.onMouseUp);
-  }
-
-  onMouseDown() {
-    this.isMouseDownHandledByCursor = this.cursor.startInteraction();
-    if (this.isMouseDownHandledByCursor) {
-      return;
-    }
-
-    this.gazeTeleporter.startTeleport();
-    this.isMouseDownHandledByGazeTeleporter = true;
-  }
-
-  onMouseUp() {
-    if (this.isMouseDownHandledByCursor) {
-      this.cursor.endInteraction();
-      this.isMouseDownHandledByCursor = false;
-    }
-
-    if (this.isMouseDownHandledByGazeTeleporter) {
-      this.gazeTeleporter.endTeleport();
-      this.isMouseDownHandledByGazeTeleporter = false;
-    }
-  }
-}
diff --git a/src/utils/touch-events-handler.js b/src/utils/touch-events-handler.js
index 2f9f4cb757fef86b3aa88cfdf293d5858ee423fd..8effcc59152317471520c197605a2160e61d1042 100644
--- a/src/utils/touch-events-handler.js
+++ b/src/utils/touch-events-handler.js
@@ -2,7 +2,6 @@ const VIRTUAL_JOYSTICK_HEIGHT = 0.8;
 const HORIZONTAL_LOOK_SPEED = 0.35;
 const VERTICAL_LOOK_SPEED = 0.18;
 
-//TODO: Oculus Touch controls emit touch events (wat), so we have to filter those out.
 export default class TouchEventsHandler {
   constructor() {
     this.cursor = null;
@@ -63,7 +62,6 @@ export default class TouchEventsHandler {
   }
 
   handleTouchStart(e) {
-    console.log(e);
     this.cursor.setCursorVisibility(false);
     Array.prototype.forEach.call(e.changedTouches, this.singleTouchStart);
   }