diff --git a/package.json b/package.json
index a97713d44790787795d671f30d6f5d9a384ac8ae..0d9ed6b57e38dc448a3da828e3acb5cc24a33035 100644
--- a/package.json
+++ b/package.json
@@ -31,6 +31,7 @@
     "classnames": "^2.2.5",
     "detect-browser": "^2.1.0",
     "event-target-shim": "^3.0.1",
+    "form-urlencoded": "^2.0.4",
     "jsonschema": "^1.2.2",
     "minijanus": "^0.5.0",
     "mobile-detect": "^1.4.1",
diff --git a/scripts/default.env b/scripts/default.env
index a1186bbf1f20d84c6883a36bedea1e6ac88e4e7c..b5dbe2c7c9ec3b22eba8d36f07c2fbd700bf0cb0 100644
--- a/scripts/default.env
+++ b/scripts/default.env
@@ -3,3 +3,4 @@
 ORIGIN_TRIAL_TOKEN="ArEZ0vY0uMo3pj+oY8Up4u4Hy8QolJwKxG4/2WRhSPnTZRrviiGhzP6/y72nBdsIhdEyoundxqg//KLbs2vGnQoAAABkeyJvcmlnaW4iOiJodHRwczovL3JldGljdWx1bS5pbzo0NDMiLCJmZWF0dXJlIjoiV2ViVlIxLjFNNjIiLCJleHBpcnkiOjE1MjYzNDg2MjEsImlzU3ViZG9tYWluIjp0cnVlfQ=="
 ORIGIN_TRIAL_EXPIRES="2018-05-15"
 JANUS_SERVER="wss://prod-janus.reticulum.io"
+DEV_RETICULUM_SERVER="dev.reticulum.io"
diff --git a/src/assets/stylesheets/hub-create.scss b/src/assets/stylesheets/hub-create.scss
index 636ab5d656725522d5a407d93c8a1d92361d61ca..31e49358ec78660c6bd9b10b1ce321bc6225c876 100644
--- a/src/assets/stylesheets/hub-create.scss
+++ b/src/assets/stylesheets/hub-create.scss
@@ -192,9 +192,13 @@
 	      font-size: 1.4em;
 
 	      @media (max-width: 520px) {
-		display: none;
+		      display: none;
 	      }
-	    }
+      }
+
+      a {
+        pointer-events: all;
+      }
 	  }
 	}
 
diff --git a/src/assets/stylesheets/index.scss b/src/assets/stylesheets/index.scss
index 77615e9d341f4ca5688ebe6f0e209163f2c884cf..38833657208e9a58a19ef8b10add9e8a5ceaa25d 100644
--- a/src/assets/stylesheets/index.scss
+++ b/src/assets/stylesheets/index.scss
@@ -34,6 +34,10 @@ body {
   display: flex;
   flex-direction: column;
   z-index: 2;
+
+  &--noninteractive {
+    pointer-events: none;
+  }
 }
 
 .background-video {
@@ -49,7 +53,7 @@ body {
 .header-content {
   padding: 1.5em 2.5em 1.5em 2.5em;
   background-color: rgba(0, 0, 0, 0.85);
-  min-height: 90px;
+  height: 90px;
   display: flex;
   border-bottom: 2px solid #242424;
 
@@ -217,3 +221,111 @@ body {
     }
   }
 }
+
+.overlay {
+  width: 100%;
+  height: 100%;
+  top: 0;
+  left: 0;
+  position: absolute;
+  pointer-events: none;
+  color: white;
+  z-index: 2;
+}
+
+.mailing-list-form {
+  display: flex;
+  height: 100%;
+  flex-direction: column;
+  justify-content: center;
+  text-align: center;
+  margin: 0;
+
+  &__first {
+    width: 100%;
+  }
+
+  &__email_field {
+    @extend %rounded-border;
+    @extend %default-font;
+    color: $light-text;
+    font-size: 1.2em;
+    background-color: transparent;
+    line-height: 2.0em;
+    padding-left: 1.25em;
+    padding-right: 1.25em;
+    margin: 0.5em 0;
+    width: 100%;
+  }
+
+  &__submit {
+    @extend %bottom-button;
+    border: 0;
+    margin-top: 16px;
+  }
+
+  &__privacy {
+    margin-top: 10px;
+    font-size: 0.7em;
+  }
+}
+
+.dialog {
+  display: grid;
+  grid-template-columns: 1fr 20px minmax(200px,500px) 20px 1fr;
+  grid-template-rows: 1fr 20px 275px 20px 1fr;
+  width: 100%;
+  height: 100%;
+  background-color: rgba(0,0,0,.6);
+
+  &__box {
+    grid-column: 3;
+    grid-row: 3;
+    position: relative;
+    pointer-events: auto;
+
+    &__contents {
+      background-color: rgba(0,0,0,0.8);
+      border-radius: 8px;
+      width: 100%;
+      height: 100%;
+      display: flex;
+      flex-direction: column;
+      align-items: center;
+      justify-content: center;
+      text-align: center;
+      position: relative;
+
+      &__title {
+	@extend %top-title;
+	margin-top: 20px;
+      }
+
+      &__body {
+	margin: 40px;
+	font-size: 1.1em;
+	margin-top: 20px;
+	color: $grey-text;
+	display: flex;
+	flex-direction: column;
+
+	a { color: white }
+      }
+
+      &__close {
+	position: absolute;
+	left: 12px;
+	top: 6px;
+	color: white;
+	font-size: 1.4em;
+
+	background: none;
+	cursor: pointer;
+	border: none;
+      }
+    }
+  }
+}
+
+
+
diff --git a/src/assets/translations.data.json b/src/assets/translations.data.json
index bf513559ad159824e95b16df5984e11899611d25..e2d73a361c9c11b16943c1fe6390b014e29706fc 100644
--- a/src/assets/translations.data.json
+++ b/src/assets/translations.data.json
@@ -1,6 +1,5 @@
 {
-  "en":
-  {
+  "en": {
     "entry.screen-prefix": "Enter on ",
     "entry.desktop-screen": "Screen",
     "entry.mobile-screen": "Phone",
@@ -34,6 +33,7 @@
     "audio.granted-subtitle": "You can still mute yourself in-game",
     "audio.granted-next": "NEXT",
     "exit.subtitle.exited": "Your session has ended.",
+    "exit.subtitle.exited": "Your session has ended. Refresh your browser to start a new one.",
     "exit.subtitle.closed": "This room is no longer available.",
     "exit.subtitle.full": "This room is full, please try again later.",
     "exit.subtitle.connect_error": "Unable to connect to this room, please try again later.",
@@ -48,12 +48,15 @@
     "home.webvr_disclaimer_post": " experiment by ",
     "home.webvr_disclaimer_mr_team": "Mozilla Mixed Reality",
     "home.view_source": "View Source",
-    "home.join_on_slack": "Join us on Slack",
+    "home.join_us": "Join the Conversation",
     "home.report_issue": "Report an Issue",
     "home.get_updates": "Get Updates",
     "home.hero_title": "A new way to get together online.",
     "home.hero_subtitle": "Laugh, play, get stuff done, or just hang out.",
-    "home.made_with_love": "made with ❤️ by ",
-    "home.environment_author_by": " by "
+    "home.made_with_love": "made with 🦆 by ",
+    "home.environment_author_by": " by ",
+    "home.dialog.close": "CLOSE",
+    "mailing_list.privacy_label": "I'm okay with Mozilla handling my info as explained in",
+    "mailing_list.privacy_link": "this Privacy Notice"
   }
 }
diff --git a/src/components/stats-plus.css b/src/components/stats-plus.css
new file mode 100644
index 0000000000000000000000000000000000000000..fc4417bf506b5a5916c286b72aeaf86189790732
--- /dev/null
+++ b/src/components/stats-plus.css
@@ -0,0 +1,26 @@
+:global(.rs-header) {
+  display: flex;
+  justify-content: space-between;
+  border-bottom: 1px rgba(255,255,255,0.1) solid;
+  margin-bottom: 8px;
+}
+
+:global(.rs-collapse-btn) {
+  cursor: pointer;
+  font-size: 12px;
+}
+
+:global(.rs-fps-counter) {
+  cursor: pointer;
+  position: absolute;
+  bottom: 0;
+  left: 0;
+  padding: 8px;
+  color: #aaa;
+  font-size: 10px;
+}
+
+:global(.rs-mobile) {
+  bottom: auto;
+  top: 0;
+}
\ No newline at end of file
diff --git a/src/components/stats-plus.js b/src/components/stats-plus.js
new file mode 100644
index 0000000000000000000000000000000000000000..c64100eeafa0e069a10ff2b005181a2dc446435e
--- /dev/null
+++ b/src/components/stats-plus.js
@@ -0,0 +1,130 @@
+import "./stats-plus.css";
+// Adapted from https://github.com/aframevr/aframe/blob/master/src/components/scene/stats.js
+
+function createStats(scene) {
+  const threeStats = new window.threeStats(scene.renderer);
+  const aframeStats = new window.aframeStats(scene);
+  const plugins = scene.isMobile ? [] : [threeStats, aframeStats];
+  return new window.rStats({
+    css: [], // Our stylesheet is injected from AFrame.
+    values: {
+      fps: { caption: "fps", below: 30 }
+    },
+    groups: [{ caption: "Framerate", values: ["fps", "raf"] }],
+    plugins: plugins
+  });
+}
+
+const HIDDEN_CLASS = "a-hidden";
+
+AFRAME.registerComponent("stats-plus", {
+  // Whether or not the stats panel is expanded.
+  // Shows FPS counter when collapsed.
+  schema: { default: false },
+  init() {
+    this.onExpand = this.onExpand.bind(this);
+    this.onCollapse = this.onCollapse.bind(this);
+    this.onEnterVr = this.onEnterVr.bind(this);
+    this.onExitVr = this.onExitVr.bind(this);
+
+    const scene = this.el.sceneEl;
+    this.stats = createStats(scene);
+    this.statsEl = document.querySelector(".rs-base");
+
+    // Add header to stats panel so we can collapse it
+    const statsHeaderEl = document.createElement("div");
+    statsHeaderEl.classList.add("rs-header");
+
+    const statsTitleEl = document.createElement("h1");
+    statsTitleEl.innerHTML = "Stats";
+    statsHeaderEl.appendChild(statsTitleEl);
+
+    const collapseEl = document.createElement("div");
+    collapseEl.classList.add("rs-collapse-btn");
+    collapseEl.innerHTML = "X";
+    collapseEl.addEventListener("click", this.onCollapse);
+    statsHeaderEl.appendChild(collapseEl);
+
+    this.statsEl.insertBefore(statsHeaderEl, this.statsEl.firstChild);
+
+    // Add fps counter to the page
+    this.fpsEl = document.createElement("div");
+    this.fpsEl.addEventListener("click", this.onExpand);
+    this.fpsEl.classList.add("rs-fps-counter");
+    document.body.appendChild(this.fpsEl);
+    this.lastFpsUpdate = performance.now();
+    this.frameCount = 0;
+
+    if (scene.isMobile) {
+      this.statsEl.classList.add("rs-mobile");
+      this.fpsEl.classList.add("rs-mobile");
+    }
+
+    scene.addEventListener("enter-vr", this.onEnterVr);
+    scene.addEventListener("exit-vr", this.onExitVr);
+  },
+  update(oldData) {
+    if (oldData !== this.data) {
+      if (this.data) {
+        this.statsEl.classList.remove(HIDDEN_CLASS);
+        this.fpsEl.classList.add(HIDDEN_CLASS);
+      } else {
+        this.statsEl.classList.add(HIDDEN_CLASS);
+        this.fpsEl.classList.remove(HIDDEN_CLASS);
+      }
+    }
+  },
+  tick() {
+    if (this.data) {
+      // Update rStats
+      const stats = this.stats;
+      stats("rAF").tick();
+      stats("FPS").frame();
+      stats().update();
+    } else {
+      // Update the fps counter
+      const now = performance.now();
+      this.frameCount++;
+
+      // Update the fps counter text once a second
+      if (now >= this.lastFpsUpdate + 1000) {
+        const fps = this.frameCount / ((now - this.lastFpsUpdate) / 1000);
+        this.fpsEl.innerHTML = Math.round(fps) + " FPS";
+        this.lastFpsUpdate = now;
+        this.frameCount = 0;
+      }
+    }
+  },
+  onEnterVr() {
+    // Hide all stats elements when entering VR on mobile
+    if (this.el.sceneEl.isMobile) {
+      this.statsEl.classList.add(HIDDEN_CLASS);
+      this.fpsEl.classList.add(HIDDEN_CLASS);
+    }
+  },
+  onExitVr() {
+    // Revert to previous state whe exiting VR on mobile
+    if (this.el.sceneEl.isMobile) {
+      if (this.data) {
+        this.statsEl.classList.remove(HIDDEN_CLASS);
+      } else {
+        this.fpsEl.classList.remove(HIDDEN_CLASS);
+      }
+    }
+  },
+  onExpand() {
+    this.el.setAttribute(this.name, true);
+  },
+  onCollapse() {
+    this.el.setAttribute(this.name, false);
+  },
+  remove() {
+    this.el.sceneEl.removeListener("enter-vr", this.hide);
+    this.el.sceneEl.removeListener("exit-vr", this.show);
+
+    if (this.statsEl) {
+      this.statsEl.parentNode.removeChild(this.statsEl);
+      this.fpsEl.parentNode.removeChild(this.fpsEl);
+    }
+  }
+});
diff --git a/src/hub.js b/src/hub.js
index e9e8372004750e203848299409fc3b69936711df..bdf37034aa1ee7647b088e616ebb8718ba6f14ba 100644
--- a/src/hub.js
+++ b/src/hub.js
@@ -49,6 +49,7 @@ import "./components/hand-poses";
 import "./components/gltf-model-plus";
 import "./components/gltf-bundle";
 import "./components/hud-controller";
+import "./components/stats-plus";
 
 import ReactDOM from "react-dom";
 import React from "react";
@@ -57,6 +58,7 @@ import HubChannel from "./utils/hub-channel";
 
 import "./systems/personal-space-bubble";
 import "./systems/app-mode";
+import "./systems/exit-on-blur";
 
 import "./gltf-component-mappings";
 
@@ -174,6 +176,9 @@ const onReady = async () => {
   };
 
   const exitScene = () => {
+    if (NAF.connection.adapter && NAF.connection.adapter.localMediaStream) {
+      NAF.connection.adapter.localMediaStream.getTracks().forEach(t => t.stop());
+    }
     hubChannel.disconnect();
     const scene = document.querySelector("a-scene");
     scene.renderer.animate(null); // Stop animation loop, TODO A-Frame should do this
@@ -199,9 +204,7 @@ const onReady = async () => {
       serverURL: process.env.JANUS_SERVER
     });
 
-    if (!qsTruthy("no_stats")) {
-      scene.setAttribute("stats", true);
-    }
+    scene.setAttribute("stats-plus", false);
 
     if (isMobile || qsTruthy("mobile")) {
       playerRig.setAttribute("virtual-gamepad-controls", {});
@@ -308,8 +311,10 @@ const onReady = async () => {
   console.log(`Hub ID: ${hubId}`);
 
   const socketProtocol = document.location.protocol === "https:" ? "wss:" : "ws:";
-  const socketPort = qs.phx_port || document.location.port;
-  const socketHost = qs.phx_host || document.location.hostname;
+  const socketPort = qs.phx_port || (process.env.NODE_ENV === "production" ? document.location.port : 443);
+  const socketHost =
+    qs.phx_host ||
+    (process.env.NODE_ENV === "production" ? document.location.hostname : process.env.DEV_RETICULUM_SERVER);
   const socketUrl = `${socketProtocol}//${socketHost}${socketPort ? `:${socketPort}` : ""}/socket`;
   console.log(`Phoenix Channel URL: ${socketUrl}`);
 
diff --git a/src/react-components/home-root.js b/src/react-components/home-root.js
index af896b643db9e3cf38ac5403aa58e143f28c58ce..36f1a7fbab31bf43653416432b0c54b25fd176a4 100644
--- a/src/react-components/home-root.js
+++ b/src/react-components/home-root.js
@@ -3,6 +3,8 @@ import PropTypes from "prop-types";
 import { IntlProvider, FormattedMessage, addLocaleData } from "react-intl";
 import en from "react-intl/locale-data/en";
 import homeVideo from "../assets/video/home.webm";
+import classNames from "classnames";
+import formurlencoded from "form-urlencoded";
 
 import HubCreatePanel from "./hub-create-panel.js";
 
@@ -17,9 +19,10 @@ const messages = localeData[lang] || localeData.en;
 
 const ENVIRONMENT_URLS = [
   "https://asset-bundles-prod.reticulum.io/rooms/meetingroom/MeetingRoom.bundle.json",
-  "https://asset-bundles-prod.reticulum.io/rooms/theater/TheaterMeshes.bundle.json",
-  "https://asset-bundles-prod.reticulum.io/rooms/atrium/AtriumMeshes.bundle.json",
-  "https://asset-bundles-prod.reticulum.io/rooms/courtyard/CourtyardMeshes.bundle.json"
+  "https://asset-bundles-prod.reticulum.io/rooms/theater/Theater.bundle.json",
+  "https://asset-bundles-prod.reticulum.io/rooms/atrium/Atrium.bundle.json",
+  "https://asset-bundles-prod.reticulum.io/rooms/courtyard/Courtyard.bundle.json",
+  "https://asset-bundles-prod.reticulum.io/rooms/MedievalFantasyBook/MedievalFantasyBook.bundle.json"
 ];
 
 class HomeRoot extends Component {
@@ -28,7 +31,10 @@ class HomeRoot extends Component {
   };
 
   state = {
-    environments: []
+    environments: [],
+    dialogType: null,
+    mailingListEmail: "",
+    mailingListPrivacy: false
   };
 
   componentDidMount() {
@@ -36,6 +42,40 @@ class HomeRoot extends Component {
     document.querySelector("#background-video").playbackRate = 0.5;
   }
 
+  showDialog = dialogType => {
+    return e => {
+      e.preventDefault();
+      e.stopPropagation();
+      this.setState({ dialogType });
+    };
+  };
+
+  closeDialog = () => {
+    this.setState({ dialogType: null });
+  };
+
+  signUpForMailingList = async e => {
+    e.preventDefault();
+    e.stopPropagation();
+    if (!this.state.mailingListPrivacy) return;
+
+    const url = "https://www.mozilla.org/en-US/newsletter/";
+
+    const payload = {
+      email: this.state.mailingListEmail,
+      newsletters: "mixed-reality",
+      privacy: true,
+      fmt: "H",
+      source_url: document.location.href
+    };
+
+    await fetch(url, {
+      body: formurlencoded(payload),
+      method: "POST",
+      headers: { "content-type": "application/x-www-form-urlencoded" }
+    }).then(() => this.setState({ dialogType: "email_submitted" }));
+  };
+
   loadEnvironments = () => {
     const environments = [];
 
@@ -52,10 +92,98 @@ class HomeRoot extends Component {
   };
 
   render() {
+    let dialogTitle = null;
+    let dialogBody = null;
+
+    switch (this.state.dialogType) {
+      // TODO i18n, FormattedMessage doesn't play nicely with links
+      case "slack":
+        dialogTitle = "Get in Touch";
+        dialogBody = (
+          <span>
+            Want to join the conversation?
+            <p />
+            Join us on the{" "}
+            <a href="https://webvr-slack.herokuapp.com/" target="_blank" rel="noopener noreferrer">
+              WebVR Slack
+            </a>{" "}
+            in the #social channel.<br />VR meetups every Friday at noon PST!
+            <p /> Or, tweet at{" "}
+            <a href="https://twitter.com/mozillareality" target="_blank" rel="noopener noreferrer">
+              @mozillareality
+            </a>{" "}
+            on Twitter.
+          </span>
+        );
+        break;
+      case "email_submitted":
+        dialogTitle = "";
+        dialogBody = "Great! Please check your e-mail to confirm your subscription.";
+        break;
+      case "updates":
+        dialogTitle = "";
+        dialogBody = (
+          <span>
+            Sign up to get release notes about new features.
+            <p />
+            <form onSubmit={this.signUpForMailingList}>
+              <div className="mailing-list-form">
+                <input
+                  type="email"
+                  value={this.state.mailingListEmail}
+                  onChange={e => this.setState({ mailingListEmail: e.target.value })}
+                  className="mailing-list-form__email_field"
+                  required
+                  placeholder="Your email here"
+                />
+                <label className="mailing-list-form__privacy">
+                  <input
+                    className="mailing-list-form__privacy_checkbox"
+                    type="checkbox"
+                    required
+                    value={this.state.mailingListPrivacy}
+                    onChange={e => this.setState({ mailingListPrivacy: e.target.checked })}
+                  />
+                  <span className="mailing-list-form__privacy_label">
+                    <FormattedMessage id="mailing_list.privacy_label" />{" "}
+                    <a target="_blank" rel="noopener noreferrer" href="https://www.mozilla.org/en-US/privacy/">
+                      <FormattedMessage id="mailing_list.privacy_link" />
+                    </a>
+                  </span>
+                </label>
+                <input className="mailing-list-form__submit" type="submit" value="Sign Up Now" />
+              </div>
+            </form>
+          </span>
+        );
+        break;
+      case "report":
+        dialogTitle = "Report an Issue";
+        dialogBody = (
+          <span>
+            Need to report a problem?
+            <p />
+            You can file a{" "}
+            <a href="https://github.com/mozilla/mr-social-client/issues" target="_blank" rel="noopener noreferrer">
+              Github Issue
+            </a>{" "}
+            or e-mail us for support at <a href="mailto:hubs@mozilla.com">hubs@mozilla.com</a>.
+            <p />
+            You can also find us in #social on the{" "}
+            <a href="http://webvr-slack.herokuapp.com/" target="_blank" rel="noopener noreferrer">
+              WebVR Slack
+            </a>.
+          </span>
+        );
+        break;
+    }
+
+    const mainContentClassNames = classNames({ "main-content": true, "main-content--noninteractive": !!dialogTitle });
+
     return (
       <IntlProvider locale={lang} messages={messages}>
         <div className="home">
-          <div className="main-content">
+          <div className={mainContentClassNames}>
             <div className="header-content">
               <div className="header-content__title">
                 <img className="header-content__title__name" src="../assets/images/logo.svg" />
@@ -110,13 +238,28 @@ class HomeRoot extends Component {
             <div className="footer-content">
               <div className="footer-content__links">
                 <div className="footer-content__links__top">
-                  <a className="footer-content__links__link" rel="noopener noreferrer" target="_blank" href="#">
-                    <FormattedMessage id="home.join_on_slack" />
+                  <a
+                    className="footer-content__links__link"
+                    rel="noopener noreferrer"
+                    href="#"
+                    onClick={this.showDialog("slack")}
+                  >
+                    <FormattedMessage id="home.join_us" />
                   </a>
-                  <a className="footer-content__links__link" rel="noopener noreferrer" target="_blank" href="#">
+                  <a
+                    className="footer-content__links__link"
+                    rel="noopener noreferrer"
+                    href="#"
+                    onClick={this.showDialog("updates")}
+                  >
                     <FormattedMessage id="home.get_updates" />
                   </a>
-                  <a className="footer-content__links__link" rel="noopener noreferrer" target="_blank" href="#">
+                  <a
+                    className="footer-content__links__link"
+                    rel="noopener noreferrer"
+                    href="#"
+                    onClick={this.showDialog("report")}
+                  >
                     <FormattedMessage id="home.report_issue" />
                   </a>
                 </div>
@@ -130,6 +273,22 @@ class HomeRoot extends Component {
           <video playsInline autoPlay muted loop className="background-video" id="background-video">
             <source src={homeVideo} type="video/webm" />
           </video>
+          {this.state.dialogType && (
+            <div className="overlay">
+              <div className="dialog">
+                <div className="dialog__box">
+                  <div className="dialog__box__contents">
+                    <button className="dialog__box__contents__close" onClick={this.closeDialog}>
+                      <span>🗙</span>
+                    </button>
+                    <div className="dialog__box__contents__title">{dialogTitle}</div>
+                    <div className="dialog__box__contents__body">{dialogBody}</div>
+                    <div className="dialog__box__contents__button-container" />
+                  </div>
+                </div>
+              </div>
+            </div>
+          )}
         </div>
       </IntlProvider>
     );
diff --git a/src/react-components/hub-create-panel.js b/src/react-components/hub-create-panel.js
index 32fd19b4edfcb343b0cada0d4f0abf0052344d8e..6e689653bcc4129547ec780cacbe220cb9f954da 100644
--- a/src/react-components/hub-create-panel.js
+++ b/src/react-components/hub-create-panel.js
@@ -38,14 +38,25 @@ class HubCreatePanel extends Component {
       hub: { name: this.state.name, default_environment_gltf_bundle_url: environment.bundle_url }
     };
 
-    const res = await fetch("/api/v1/hubs", {
+    let createUrl = "/api/v1/hubs";
+
+    if (process.env.NODE_ENV === "development") {
+      createUrl = `https://${process.env.DEV_RETICULUM_SERVER}${createUrl}`;
+    }
+
+    const res = await fetch(createUrl, {
       body: JSON.stringify(payload),
       headers: { "content-type": "application/json" },
       method: "POST"
     });
 
     const hub = await res.json();
-    document.location = hub.url;
+
+    if (process.env.NODE_ENV === "production") {
+      document.location = hub.url;
+    } else {
+      document.location = `/hub.html?hub_id=${hub.hub_id}`;
+    }
   };
 
   isHubNameValid = () => {
@@ -149,12 +160,22 @@ class HubCreatePanel extends Component {
                         {environmentTitle}
                       </span>
                       {environmentAuthor &&
-                        environmentAuthor.name && (
+                        environmentAuthor.name &&
+                        (environmentAuthor.url ? (
+                          <a
+                            href={environmentAuthor.url}
+                            target="_blank"
+                            className="create-panel__form__environment__picker__labels__header__author"
+                          >
+                            <FormattedMessage id="home.environment_author_by" />
+                            <span>{environmentAuthor.name}</span>
+                          </a>
+                        ) : (
                           <span className="create-panel__form__environment__picker__labels__header__author">
                             <FormattedMessage id="home.environment_author_by" />
                             <span>{environmentAuthor.name}</span>
                           </span>
-                        )}
+                        ))}
                     </div>
                     <div className="create-panel__form__environment__picker__labels__footer">
                       <FormattedMessage id="home.environment_picker_footer" />
diff --git a/src/react-components/ui-root.js b/src/react-components/ui-root.js
index 90d08830d616ecc1a8d0f7b0ebd18eb4241b2e0e..eef65a7448aefe0647ba46d411710906220e82bd 100644
--- a/src/react-components/ui-root.js
+++ b/src/react-components/ui-root.js
@@ -108,6 +108,12 @@ class UIRoot extends Component {
     this.props.scene.addEventListener("loaded", this.onSceneLoaded);
     this.props.scene.addEventListener("stateadded", this.onAframeStateChanged);
     this.props.scene.addEventListener("stateremoved", this.onAframeStateChanged);
+    this.props.scene.addEventListener("exit", this.exit);
+  }
+
+  componentWillUnmount() {
+    this.props.scene.removeEventListener("loaded", this.onSceneLoaded);
+    this.props.scene.removeEventListener("exit", this.exit);
   }
 
   componentDidUpdate(prevProps) {
diff --git a/src/systems/exit-on-blur.js b/src/systems/exit-on-blur.js
new file mode 100644
index 0000000000000000000000000000000000000000..e6263a101f5a2fd1f71cd4b122a12431104e2457
--- /dev/null
+++ b/src/systems/exit-on-blur.js
@@ -0,0 +1,30 @@
+AFRAME.registerSystem("exit-on-blur", {
+  init() {
+    this.onBlur = this.onBlur.bind(this);
+    this.onFocus = this.onFocus.bind(this);
+
+    window.addEventListener("blur", this.onBlur);
+    window.addEventListener("focus", this.onFocus);
+
+    this.exitTimeout = null;
+  },
+
+  onBlur() {
+    if (this.el.isMobile) {
+      this.exitTimeout = setTimeout(() => {
+        this.el.dispatchEvent(new CustomEvent("exit"));
+      }, 10 * 1000);
+    }
+  },
+
+  onFocus() {
+    if (this.el.isMobile) {
+      clearTimeout(this.exitTimeout);
+    }
+  },
+
+  remove() {
+    clearTimeout(this.exitTimeout);
+    window.removeEventListener("blur", this.onBlur);
+  }
+});
diff --git a/src/telemetry.js b/src/telemetry.js
index 6eed4d002b96c507dcdaea78b6a5ab58f9791441..9fa4ae9ce79009d203fb4992cd6d643460633794 100644
--- a/src/telemetry.js
+++ b/src/telemetry.js
@@ -2,6 +2,6 @@ import Raven from "raven-js";
 
 export default function registerTelemetry() {
   if (process.env.NODE_ENV === "production") {
-    Raven.config("https://f571beaf5cee4e3085e0bf436f3eb158@sentry.io/256771").install();
+    Raven.config("https://013d6a364fed43cdb0539a61d520597a@sentry.prod.mozaws.net/370").install();
   }
 }
diff --git a/webpack.config.js b/webpack.config.js
index 4f9b26d26cab4c3959a5b1796696f7a0b531c086..55bae82132e8d8611ba8b01ffcddf1ac49109c4b 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -87,7 +87,7 @@ const config = {
   mode: "development",
   devtool: process.env.NODE_ENV === "production" ? "source-map" : "inline-source-map",
   devServer: {
-    open: true,
+    open: false,
     https: createHTTPSConfig(),
     host: "0.0.0.0",
     useLocalIp: true,
@@ -216,7 +216,8 @@ const config = {
     new webpack.DefinePlugin({
       "process.env": JSON.stringify({
         NODE_ENV: process.env.NODE_ENV,
-        JANUS_SERVER: process.env.JANUS_SERVER
+        JANUS_SERVER: process.env.JANUS_SERVER,
+        DEV_RETICULUM_SERVER: process.env.DEV_RETICULUM_SERVER
       })
     })
   ]
diff --git a/yarn.lock b/yarn.lock
index 8820642a27070f29460b409b87626ac173c298f5..6ae1ce2d8e2b6fb237724d975e27a2145a189ca7 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -118,7 +118,7 @@ accepts@1.3.3:
     mime-types "~2.1.11"
     negotiator "0.6.1"
 
-accepts@~1.3.4, accepts@~1.3.5:
+accepts@~1.3.4:
   version "1.3.5"
   resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.5.tgz#eb777df6011723a3b14e8a72c0805c8e86746bd2"
   dependencies:
@@ -2007,18 +2007,14 @@ colormin@^1.0.5:
     css-color-names "0.0.4"
     has "^1.0.1"
 
-colors@*:
-  version "1.2.1"
-  resolved "https://registry.yarnpkg.com/colors/-/colors-1.2.1.tgz#f4a3d302976aaf042356ba1ade3b1a2c62d9d794"
+colors@*, colors@^1.1.2, colors@~1.1.2:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/colors/-/colors-1.1.2.tgz#168a4701756b6a7f51a12ce0c97bfa28c084ed63"
 
 colors@1.0.3:
   version "1.0.3"
   resolved "https://registry.yarnpkg.com/colors/-/colors-1.0.3.tgz#0433f44d809680fdeb60ed260f1b0c262e82a40b"
 
-colors@^1.1.2, colors@~1.1.2:
-  version "1.1.2"
-  resolved "https://registry.yarnpkg.com/colors/-/colors-1.1.2.tgz#168a4701756b6a7f51a12ce0c97bfa28c084ed63"
-
 combine-source-map@~0.7.1:
   version "0.7.2"
   resolved "https://registry.yarnpkg.com/combine-source-map/-/combine-source-map-0.7.2.tgz#0870312856b307a87cc4ac486f3a9a62aeccc09e"
@@ -2836,17 +2832,7 @@ error@^7.0.2:
     string-template "~0.2.1"
     xtend "~4.0.0"
 
-es-abstract@^1.5.1:
-  version "1.11.0"
-  resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.11.0.tgz#cce87d518f0496893b1a30cd8461835535480681"
-  dependencies:
-    es-to-primitive "^1.1.1"
-    function-bind "^1.1.1"
-    has "^1.0.1"
-    is-callable "^1.1.3"
-    is-regex "^1.0.4"
-
-es-abstract@^1.7.0:
+es-abstract@^1.5.1, es-abstract@^1.7.0:
   version "1.10.0"
   resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.10.0.tgz#1ecb36c197842a00d8ee4c2dfd8646bb97d60864"
   dependencies:
@@ -3076,42 +3062,7 @@ expand-tilde@^2.0.0, expand-tilde@^2.0.2:
   dependencies:
     homedir-polyfill "^1.0.1"
 
-express@^4.10.7:
-  version "4.16.3"
-  resolved "https://registry.yarnpkg.com/express/-/express-4.16.3.tgz#6af8a502350db3246ecc4becf6b5a34d22f7ed53"
-  dependencies:
-    accepts "~1.3.5"
-    array-flatten "1.1.1"
-    body-parser "1.18.2"
-    content-disposition "0.5.2"
-    content-type "~1.0.4"
-    cookie "0.3.1"
-    cookie-signature "1.0.6"
-    debug "2.6.9"
-    depd "~1.1.2"
-    encodeurl "~1.0.2"
-    escape-html "~1.0.3"
-    etag "~1.8.1"
-    finalhandler "1.1.1"
-    fresh "0.5.2"
-    merge-descriptors "1.0.1"
-    methods "~1.1.2"
-    on-finished "~2.3.0"
-    parseurl "~1.3.2"
-    path-to-regexp "0.1.7"
-    proxy-addr "~2.0.3"
-    qs "6.5.1"
-    range-parser "~1.2.0"
-    safe-buffer "5.1.1"
-    send "0.16.2"
-    serve-static "1.13.2"
-    setprototypeof "1.1.0"
-    statuses "~1.4.0"
-    type-is "~1.6.16"
-    utils-merge "1.0.1"
-    vary "~1.1.2"
-
-express@^4.16.2:
+express@^4.10.7, express@^4.16.2:
   version "4.16.2"
   resolved "https://registry.yarnpkg.com/express/-/express-4.16.2.tgz#e35c6dfe2d64b7dca0a5cd4f21781be3299e076c"
   dependencies:
@@ -3317,18 +3268,6 @@ finalhandler@1.1.0:
     statuses "~1.3.1"
     unpipe "~1.0.0"
 
-finalhandler@1.1.1:
-  version "1.1.1"
-  resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.1.tgz#eebf4ed840079c83f4249038c9d703008301b105"
-  dependencies:
-    debug "2.6.9"
-    encodeurl "~1.0.2"
-    escape-html "~1.0.3"
-    on-finished "~2.3.0"
-    parseurl "~1.3.2"
-    statuses "~1.4.0"
-    unpipe "~1.0.0"
-
 find-cache-dir@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-1.0.0.tgz#9288e3e9e3cc3748717d39eade17cf71fc30ee6f"
@@ -3416,6 +3355,10 @@ form-data@~2.1.1:
     combined-stream "^1.0.5"
     mime-types "^2.1.12"
 
+form-urlencoded@^2.0.4:
+  version "2.0.4"
+  resolved "https://registry.yarnpkg.com/form-urlencoded/-/form-urlencoded-2.0.4.tgz#dbcd590a49ae35d5e9516bbba8567242d0291fe5"
+
 forwarded@~0.1.2:
   version "0.1.2"
   resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84"
@@ -6391,7 +6334,7 @@ prop-types@^15.5.4, prop-types@^15.6.0:
     loose-envify "^1.3.1"
     object-assign "^4.1.1"
 
-proxy-addr@~2.0.2, proxy-addr@~2.0.3:
+proxy-addr@~2.0.2:
   version "2.0.3"
   resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.3.tgz#355f262505a621646b3130a728eb647e22055341"
   dependencies:
@@ -7159,7 +7102,7 @@ serve-static@1.13.1:
     parseurl "~1.3.2"
     send "0.16.1"
 
-serve-static@1.13.2, serve-static@^1.10.0, serve-static@^1.8.0:
+serve-static@^1.10.0, serve-static@^1.8.0:
   version "1.13.2"
   resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.13.2.tgz#095e8472fd5b46237db50ce486a43f4b86c6cec1"
   dependencies:
@@ -7971,7 +7914,7 @@ type-check@~0.3.2:
   dependencies:
     prelude-ls "~1.1.2"
 
-type-is@~1.6.15, type-is@~1.6.16:
+type-is@~1.6.15:
   version "1.6.16"
   resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.16.tgz#f89ce341541c672b25ee7ae3c73dee3b2be50194"
   dependencies: