diff --git a/package.json b/package.json
index 03ae8e86c0439d1748ed66115b4289d50ab7d7a2..f9dd871bfc08810ed727d4ccefd7b483ba50f0b3 100644
--- a/package.json
+++ b/package.json
@@ -19,6 +19,7 @@
     "aframe-input-mapping-component": "https://github.com/johnshaughnessy/aframe-input-mapping-component#23e2855",
     "aframe-teleport-controls": "https://github.com/netpro2k/aframe-teleport-controls#feature/teleport-origin",
     "aframe-xr": "github:brianpeiris/aframe-xr#3162aed",
+    "detect-browser": "^2.1.0",
     "material-design-lite": "^1.3.0",
     "minijanus": "^0.4.0",
     "naf-janus-adapter": "^0.3.0",
diff --git a/src/react-components/ui-root.js b/src/react-components/ui-root.js
index a71e4e886abb9813d0b29cea868887ca60d17474..0e46ace485799f2f602907e17b2f439d57bac2f8 100644
--- a/src/react-components/ui-root.js
+++ b/src/react-components/ui-root.js
@@ -1,5 +1,6 @@
 import React, { Component } from 'react';
 import PropTypes from 'prop-types';
+import { getPreEntryMobileVRDeviceCaps } from "../utils/vr-caps-detect.js"
 
 class UIRoot extends Component {
   static propTypes = {
@@ -7,8 +8,7 @@ class UIRoot extends Component {
   };
 
   componentDidMount = () => {
-    if (navigator.getVRDisplays()) {
-    }
+    getPreEntryMobileVRDeviceCaps().then(x => console.log(x))
   }
 
   render() {
diff --git a/src/utils/vr-caps-detect.js b/src/utils/vr-caps-detect.js
new file mode 100644
index 0000000000000000000000000000000000000000..4013e1bbc12a82439c6a55a45c12d158f98c4cca
--- /dev/null
+++ b/src/utils/vr-caps-detect.js
@@ -0,0 +1,80 @@
+const { detect } = require("detect-browser");
+const browser = detect();
+
+// Precision on device detection is fuzzy -- we can sometimes know if a device is definitely
+// available, or definitely *not* available, and assume it may be available otherwise.
+const VR_DEVICE_AVAILABILITY = {
+  yes: "yes", // Implies VR can be launched into on this device immediately, in this browser
+  no: "no", // Implies this VR device is definitely not supported regardless of browser
+  maybe: "maybe" // Implies this device may support this VR device, but may not be installed or in the right browser
+};
+
+function hasPhysicalScreenDimensions(w, h) {
+  const dpr = window.devicePixelRatio;
+  const width = screen.width * dpr;
+  const height = screen.height * dpr;
+
+  // Compensate for rounding error due to fractional pixel densities
+  return Math.abs(w - width) < 3 && Math.abs(h - height) < 3;
+}
+
+function matchesScreenSizesAndUserAgentRegex(sizes, regex) {
+  return !!(sizes.find(s => hasPhysicalScreenDimensions(...s)) && navigator.userAgent.match(regex));
+}
+
+function isGearVRCompatibleDevice() {
+  // Modern Samsung Galaxy devices have model numbers in the user agent of the form SX-XXXX
+  return matchesScreenSizesAndUserAgentRegex([[1440, 2560], [1440, 2960]], /\WS.-.....?\W/);
+}
+
+function isGooglePixelPhone() {
+  return matchesScreenSizesAndUserAgentRegex([[1080, 1920], [1440, 2560]], /\WPixel\W/);
+}
+
+function isKnownDaydreamCompatibleDevice() {
+  // Samsung S6, S7, and Note 5 do not support Daydream, but other GearVR compatiable devices
+  // do. Instead of doing fine-grained model detection we will just assume they are all compatible.
+  if (isGearVRCompatibleDevice()) return true;
+  if (isGooglePixelPhone()) return true;
+
+  // Note this is non-exhaustive -- this function may return false for compatible devices.
+  return false;
+}
+
+// Captures the potential ways a user can launch into mobile VR *before* a headset is attached to the phone.
+// Tries to determine VR platform compatibility regardless of the current browser.
+//
+// For each mobile VR platform, returns "yes" if that platform can be launched into directly from this browser
+// on this device, returns "no" if that platform cannot be supported on any browser, and "maybe" if the
+// device potentially could support that platform if a different browser was running.
+export async function getPreEntryMobileVRDeviceCaps() {
+  const isWebVREnabled = !!navigator.getVRDisplays;
+  const isWebVREnabledChrome = !!(isWebVREnabled && browser.name === "chrome");
+  const isAndroidCompatible =
+    navigator.userAgent.match(/\Wandroid\W/i) && !navigator.userAgent.match(/\Wwindows phone\W/i);
+
+  // We only consider GearVR hardware support as "maybe" and never return "yes" for GearVR. The only browser
+  // that will detect GearVR outside of VR is Samsung Internet, and we'd prefer to launch into Oculus
+  // Browser for now since Samsung Internet requires an additional WebVR installation + flag.
+  const gearvr = isGearVRCompatibleDevice() ? VR_DEVICE_AVAILABILITY.maybe : VR_DEVICE_AVAILABILITY.no;
+
+  // For daydream detection, we first check if they are on an Android compatible device, and assume they
+  // may support daydream *unless* this browser has WebVR capabilities, in which case we can do better.
+  let daydream = isAndroidCompatible ? VR_DEVICE_AVAILABILITY.maybe : VR_DEVICE_AVAILABILITY.no;
+
+  if (isWebVREnabled) {
+    // For daydream detection, if this is a WebVR browser we can increase confidence in daydream compatibility.
+    const displays = await navigator.getVRDisplays();
+    const hasDaydreamWebVRDevice = displays.find(d => d.displayName.match(/\Wdaydream\W/i));
+
+    if (hasDaydreamWebVRDevice) {
+      // If we detected daydream via WebVR
+      daydream = VR_DEVICE_AVAILABILITY.yes;
+    } else if (isWebVREnabledChrome || !isKnownDaydreamCompatibleDevice()) {
+      // If we didn't detect daydream in chrome (which is known to detect it) and we are on a known compatible device
+      daydream = VR_DEVICE_AVAILABILITY.no;
+    }
+  }
+
+  return { gearvr, daydream };
+}
diff --git a/yarn.lock b/yarn.lock
index 6ef0aefe1bdce4a08646a63e22a0957c5040c677..b27d10b90e3d20263df87e3177517fec3730c9b1 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2136,6 +2136,10 @@ destroy@~1.0.4:
   version "1.0.4"
   resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80"
 
+detect-browser@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/detect-browser/-/detect-browser-2.1.0.tgz#df35462901dfd92b8f37c2fa457d6e1f57b5e8eb"
+
 detect-conflict@^1.0.0:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/detect-conflict/-/detect-conflict-1.0.1.tgz#088657a66a961c05019db7c4230883b1c6b4176e"