diff --git a/src/assets/environments/cliff_meeting_space/bundle.json.tpl b/src/assets/environments/cliff_meeting_space/bundle.json.tpl
index 9a4eca63f5628750106b5d10693cc6ee6ea4856c..cdd4383273753f504146c39f1d50088168606f0a 100644
--- a/src/assets/environments/cliff_meeting_space/bundle.json.tpl
+++ b/src/assets/environments/cliff_meeting_space/bundle.json.tpl
@@ -1,30 +1,25 @@
 {
   "name": "cliff_meeting_space",
   "version": "0.1.0",
-  "layers": [
+  "meta": {
+    "title": "Cliffside Meeting Space",
+    "description": "Enjoy a beautiful vista view while collaborating in Mixed Reality",
+    "images": [
+      { "type": "preview-thumbnail", "srcset": "https://upload.wikimedia.org/wikipedia/commons/thumb/c/c0/Big_Buck_Bunny_4K.webm/310px-seek%3D116-Big_Buck_Bunny_4K.webm.jpg" }
+    ]
+  },
+  "assets": [
     {
-      "name": "space-geometry",
-      "assets": [
-        { "name": "meeting-space", "src": "<%= require("./MeetingSpace1_mesh.glb") %>" }
-      ]
+      "name": "space-geometry", "src": "<%= require("./MeetingSpace1_mesh.glb") %>"
     },
     {
-      "name": "outdoor-geometry",
-      "assets": [
-        { "name": "outdoor-facade", "src": "<%= require("./OutdoorFacade_mesh.glb") %>" }
-      ]
+      "name": "outdoor-geometry", "src": "<%= require("./OutdoorFacade_mesh.glb") %>"
     },
     {
-      "name": "collision",
-      "assets": [
-        { "name": "floor-nav", "src": "<%= require("./FloorNav_mesh.glb") %>" }
-      ]
+      "name": "collision", "src": "<%= require("./FloorNav_mesh.glb") %>"
     },
     {
-      "name": "cliff-geometry",
-      "assets": [
-        { "name": "cliff-vista", "src": "<%= require("./CliffVista_mesh.glb") %>" }
-      ]
+      "name": "cliff-geometry", "src": "<%= require("./CliffVista_mesh.glb") %>"
     }
   ]
 }
diff --git a/src/assets/translations.data.json b/src/assets/translations.data.json
index e10a95ae40bb533716f17b93f15d0f196a6de13e..e3a8fe318eb93e0c8824fe4dbb142a675a7759c0 100644
--- a/src/assets/translations.data.json
+++ b/src/assets/translations.data.json
@@ -30,6 +30,9 @@
     "autoexit.title": "Auto-ending session in ",
     "autoexit.title_units": " seconds",
     "autoexit.subtitle": "You have started another session.",
-    "autoexit.cancel": "CANCEL"
+    "autoexit.cancel": "CANCEL",
+    "home.create_header": "Name your Hub",
+    "home.create_name.validation_warning": "Invalid name, limited to 4 to 64 characters and limited symbols.",
+    "home.create_button": "Create"
   }
 }
diff --git a/src/components/gltf-bundle.js b/src/components/gltf-bundle.js
index 57ed05a6cdc2f354f55f2733e6136740ac2c9fd4..55646540a198c3a379d829102562d5b86d381771 100644
--- a/src/components/gltf-bundle.js
+++ b/src/components/gltf-bundle.js
@@ -14,15 +14,9 @@ AFRAME.registerComponent("gltf-bundle", {
   _addGltfEntitiesForBundleJson: function(bundleJson) {
     const loaded = [];
 
-    for (let i = 0; i < bundleJson.layers.length; i++) {
-      const layer = bundleJson.layers[i];
-
-      // TODO choose a proper asset based upon quality settings, etc. For now just take the first.
-      if (layer.assets.length > 1) {
-        throw `Unable to inflate bundle ${this.data.src} because multiple assets defined for layer ${layer.name}`;
-      }
-
-      const src = layer.assets[0].src;
+    for (let i = 0; i < bundleJson.assets.length; i++) {
+      const asset = bundleJson.assets[i];
+      const src = asset.src;
       const gltfEl = document.createElement("a-gltf-entity");
       gltfEl.setAttribute("src", src);
       gltfEl.setAttribute("position", "0 0 0");
diff --git a/src/react-components/home-root.js b/src/react-components/home-root.js
index 90c314494258df7e505d18e36a93f8c36c761b12..80d540f2e4c1fd4e9f3967e1579dfea8402af540 100644
--- a/src/react-components/home-root.js
+++ b/src/react-components/home-root.js
@@ -2,9 +2,11 @@ import React, { Component } from "react";
 import PropTypes from "prop-types";
 import classNames from "classnames";
 import queryString from "query-string";
-import { IntlProvider, FormattedMessage, addLocaleData } from "react-intl";
+import { IntlProvider, injectIntl, FormattedMessage, addLocaleData } from "react-intl";
 import en from "react-intl/locale-data/en";
 
+import HubCreatePanel from "./hub-create-panel.js";
+
 const navigatorLang = (navigator.languages && navigator.languages[0]) || navigator.language || navigator.userLanguage;
 
 const lang = navigatorLang.toLowerCase().split(/[_-]+/)[0];
@@ -14,15 +16,46 @@ addLocaleData([...en]);
 
 const messages = localeData[lang] || localeData.en;
 
+const ENVIRONMENT_URLS = [
+  `${document.location.protocol}//${document.location.host}/assets/environments/cliff_meeting_space/bundle.json`
+];
+
 class HomeRoot extends Component {
-  static propTypes = {};
+  static propTypes = {
+    intl: PropTypes.object
+  };
+
+  state = {
+    environments: []
+  };
+
+  componentDidMount() {
+    this.loadEnvironments();
+  }
+
+  loadEnvironments = () => {
+    const environments = [];
 
-  state = {};
+    const environmentLoads = ENVIRONMENT_URLS.map(
+      src =>
+        new Promise(async resolve => {
+          const res = await fetch(src);
+          const data = await res.json();
+          data.bundle_url = src;
+          environments.push(data);
+          resolve();
+        })
+    );
 
-  componentDidMount() {}
+    Promise.all(environmentLoads).then(() => this.setState({ environments }));
+  };
 
   render() {
-    return <div>Hello</div>;
+    return (
+      <IntlProvider locale={lang} messages={messages}>
+        <div>{this.state.environments.length > 0 && <HubCreatePanel environments={this.state.environments} />}</div>
+      </IntlProvider>
+    );
   }
 }
 
diff --git a/src/react-components/hub-create-panel.js b/src/react-components/hub-create-panel.js
new file mode 100644
index 0000000000000000000000000000000000000000..1964285726770885c31d04e304e7de26141adee4
--- /dev/null
+++ b/src/react-components/hub-create-panel.js
@@ -0,0 +1,85 @@
+import React, { Component } from "react";
+import PropTypes from "prop-types";
+import { injectIntl, FormattedMessage } from "react-intl";
+import { SCHEMA } from "../storage/store";
+import { generateHubName } from "../utils/name-generation";
+
+class HubCreatePanel extends Component {
+  static propTypes = {
+    intl: PropTypes.object,
+    environments: PropTypes.array
+  };
+
+  state = {
+    name: generateHubName(),
+    environmentBundleUrl: ""
+  };
+
+  componentDidMount() {
+    this.setState({ environmentBundleUrl: this.props.environments[0].bundle_url });
+  }
+
+  createHub = async e => {
+    e.preventDefault();
+
+    const payload = {
+      hub: { name: this.state.name, default_environment_gltf_bundle_url: this.state.environmentBundleUrl }
+    };
+
+    const res = await fetch("/api/v1/hubs", {
+      body: JSON.stringify(payload),
+      headers: { "content-type": "application/json" },
+      method: "POST"
+    });
+
+    const hub = await res.json();
+    document.location = hub.url;
+  };
+
+  render() {
+    const { formatMessage } = this.props.intl;
+
+    if (this.props.environments.length == 0) {
+      return <div />;
+    }
+
+    const environmentChoices = this.props.environments.map(e => (
+      <option key={e.bundle_url} value={e.bundle_url}>
+        {e.meta.title}
+      </option>
+    ));
+
+    return (
+      <form onSubmit={this.createHub}>
+        <div className="create-panel">
+          <div className="create-panel__header">
+            <FormattedMessage id="home.create_header" />
+          </div>
+          <div className="create-panel__form">
+            <input
+              className="create-panel__form__name"
+              value={this.state.name}
+              onChange={e => this.setState({ name: e.target.value })}
+              onFocus={e => e.target.select()}
+              required
+              pattern={"^[A-Za-z0-9-'\":!@#$%^&*(),.?~ ]{4,64}$"}
+              title={formatMessage({ id: "home.create_name.validation_warning" })}
+            />
+            <select value={this.state.environmentBundleUrl}>{environmentChoices}</select>
+            <button
+              onClick={e => {
+                e.preventDefault();
+                this.setState({ name: generateHubName() });
+              }}
+            >
+              Rotate
+            </button>
+            <input type="submit" value={formatMessage({ id: "home.create_button" })} />
+          </div>
+        </div>
+      </form>
+    );
+  }
+}
+
+export default injectIntl(HubCreatePanel);
diff --git a/src/utils/name-generation.js b/src/utils/name-generation.js
new file mode 100644
index 0000000000000000000000000000000000000000..cdedd157240ef4a34acd534d8c5e7d36b0ca55fc
--- /dev/null
+++ b/src/utils/name-generation.js
@@ -0,0 +1,672 @@
+const adjectives = [
+  "able",
+  "absolute",
+  "acceptable",
+  "acclaimed",
+  "accomplished",
+  "accurate",
+  "aching",
+  "acrobatic",
+  "adorable",
+  "adventurous",
+  "basic",
+  "belated",
+  "beloved",
+  "calm",
+  "candid",
+  "capital",
+  "carefree",
+  "caring",
+  "cautious",
+  "celebrated",
+  "charming",
+  "daring",
+  "darling",
+  "dearest",
+  "each",
+  "eager",
+  "early",
+  "earnest",
+  "easy",
+  "easygoing",
+  "ecstatic",
+  "edible",
+  "fabulous",
+  "fair",
+  "faithful",
+  "familiar",
+  "famous",
+  "fancy",
+  "fantastic",
+  "far",
+  "generous",
+  "gentle",
+  "genuine",
+  "giant",
+  "handmade",
+  "handsome",
+  "handy",
+  "happy",
+  "icy",
+  "ideal",
+  "identical",
+  "keen",
+  "lasting",
+  "lavish",
+  "magnificent",
+  "majestic",
+  "mammoth",
+  "marvelous",
+  "natural",
+  "obedient",
+  "palatable",
+  "parched",
+  "passionate",
+  "pastel",
+  "peaceful",
+  "perfect",
+  "perfumed",
+  "quaint",
+  "qualified",
+  "radiant",
+  "rapid",
+  "rare",
+  "safe",
+  "sandy",
+  "satisfied",
+  "scaly",
+  "scarce",
+  "scared",
+  "scary",
+  "scented",
+  "scientific",
+  "secret",
+  "sentimental",
+  "talkative",
+  "tangible",
+  "tart",
+  "tasty",
+  "tattered",
+  "teeming",
+  "ultimate",
+  "uncommon",
+  "unconscious",
+  "understated",
+  "warm",
+  "active",
+  "adept",
+  "admirable",
+  "admired",
+  "adorable",
+  "adored",
+  "advanced",
+  "affectionate",
+  "beneficial",
+  "best",
+  "better",
+  "big",
+  "cheerful",
+  "cheery",
+  "chief",
+  "chilly",
+  "classic",
+  "clean",
+  "clear",
+  "clever",
+  "decent",
+  "decisive",
+  "deep",
+  "defiant",
+  "definitive",
+  "delectable",
+  "delicious",
+  "elaborate",
+  "elastic",
+  "elated",
+  "elegant",
+  "elementary",
+  "elliptical",
+  "fast",
+  "favorable",
+  "favorite",
+  "fearless",
+  "gifted",
+  "glamorous",
+  "gleaming",
+  "glittering",
+  "harmonious",
+  "imaginative",
+  "immense",
+  "jealous",
+  "kind",
+  "leafy",
+  "legal",
+  "mature",
+  "mean",
+  "meaty",
+  "nautical",
+  "neat",
+  "necessary",
+  "needy",
+  "oddball",
+  "offbeat",
+  "periodic",
+  "perky",
+  "personal",
+  "pertinent",
+  "petty",
+  "quarterly",
+  "ready",
+  "real",
+  "realistic",
+  "reasonable",
+  "regal",
+  "serene",
+  "shabby",
+  "sharp",
+  "shiny",
+  "showy",
+  "shy",
+  "silky",
+  "tempting",
+  "tense",
+  "terrific",
+  "testy",
+  "thankful",
+  "uniform",
+  "unique",
+  "vast",
+  "weary",
+  "wee",
+  "welcome",
+  "agile",
+  "alarmed",
+  "alert",
+  "alive",
+  "bleak",
+  "blissful",
+  "blushing",
+  "coarse",
+  "colorful",
+  "colossal",
+  "comfortable",
+  "compassionate",
+  "complete",
+  "delightful",
+  "dense",
+  "dependable",
+  "dependent",
+  "descriptive",
+  "detailed",
+  "determined",
+  "devoted",
+  "different",
+  "eminent",
+  "emotional",
+  "enchanted",
+  "enchanting",
+  "energetic",
+  "enormous",
+  "fine",
+  "finished",
+  "firm",
+  "firsthand",
+  "fixed",
+  "flashy",
+  "flawless",
+  "glorious",
+  "glossy",
+  "golden",
+  "good",
+  "gorgeous",
+  "graceful",
+  "healthy",
+  "heartfelt",
+  "hearty",
+  "helpful",
+  "impartial",
+  "impressive",
+  "jolly",
+  "jovial",
+  "lighthearted",
+  "likable",
+  "lined",
+  "mellow",
+  "melodic",
+  "memorable",
+  "mild",
+  "new",
+  "opulent",
+  "playful",
+  "pleasant",
+  "pleasing",
+  "plump",
+  "plush",
+  "polished",
+  "polite",
+  "reliable",
+  "relieved",
+  "remarkable",
+  "remote",
+  "respectful",
+  "responsible",
+  "simple",
+  "simplistic",
+  "sizzling",
+  "sleepy",
+  "slight",
+  "slim",
+  "smart",
+  "smooth",
+  "snappy",
+  "snoopy",
+  "thirsty",
+  "this",
+  "thorough",
+  "those",
+  "thoughtful",
+  "united",
+  "vibrant",
+  "vicious",
+  "wellmade",
+  "whimsical",
+  "whirlwind",
+  "zesty",
+  "amazing",
+  "ambitious",
+  "ample",
+  "amused",
+  "amusing",
+  "ancient",
+  "angelic",
+  "antique",
+  "bold",
+  "bossy",
+  "both",
+  "bouncy",
+  "bountiful",
+  "complex",
+  "conscious",
+  "considerate",
+  "constant",
+  "content",
+  "conventional",
+  "cooked",
+  "cool",
+  "cooperative",
+  "diligent",
+  "dimwitted",
+  "direct",
+  "discrete",
+  "envious",
+  "essential",
+  "ethical",
+  "euphoric",
+  "flippant",
+  "fluffy",
+  "fluid",
+  "flustered",
+  "focused",
+  "fond",
+  "gracious",
+  "grand",
+  "grandiose",
+  "granular",
+  "grateful",
+  "grave",
+  "great",
+  "hidden",
+  "high",
+  "hilarious",
+  "homely",
+  "incomparable",
+  "incredible",
+  "infamous",
+  "joyful",
+  "lively",
+  "loathsome",
+  "lonely",
+  "long",
+  "milky",
+  "mindless",
+  "miniature",
+  "minor",
+  "misty",
+  "next",
+  "nice",
+  "nifty",
+  "nimble",
+  "orderly",
+  "organic",
+  "ornate",
+  "popular",
+  "posh",
+  "positive",
+  "potable",
+  "powerful",
+  "powerless",
+  "precious",
+  "present",
+  "prestigious",
+  "quick",
+  "rewarding",
+  "rich",
+  "right",
+  "sociable",
+  "soft",
+  "solid",
+  "some",
+  "sophisticated",
+  "soulful",
+  "sparkling",
+  "spectacular",
+  "speedy",
+  "spicy",
+  "spiffy",
+  "spirited",
+  "spiteful",
+  "splendid",
+  "spotless",
+  "spry",
+  "thrifty",
+  "tidy",
+  "tight",
+  "timely",
+  "tinted",
+  "unruly",
+  "untimely",
+  "violet",
+  "wicked",
+  "wide",
+  "wild",
+  "willing",
+  "winding",
+  "windy",
+  "zigzag",
+  "apprehensive",
+  "appropriate",
+  "artistic",
+  "assured",
+  "astonishing",
+  "bright",
+  "brilliant",
+  "bronze",
+  "coordinated",
+  "courageous",
+  "courteous",
+  "crafty",
+  "crazy",
+  "creamy",
+  "creative",
+  "crisp",
+  "distant",
+  "distinct",
+  "downright",
+  "evergreen",
+  "everlasting",
+  "every",
+  "evil",
+  "excellent",
+  "excitable",
+  "exemplary",
+  "exhausted",
+  "forthright",
+  "fortunate",
+  "fragrant",
+  "frank",
+  "free",
+  "frequent",
+  "fresh",
+  "friendly",
+  "frightened",
+  "frigid",
+  "gripping",
+  "grounded",
+  "honest",
+  "honorable",
+  "honored",
+  "hopeful",
+  "hospitable",
+  "hot",
+  "huge",
+  "infatuated",
+  "infinite",
+  "informal",
+  "insistent",
+  "instructive",
+  "juicy",
+  "jumbo",
+  "knowing",
+  "knowledgeable",
+  "longterm",
+  "loud",
+  "lovable",
+  "loving",
+  "modern",
+  "modest",
+  "moist",
+  "monumental",
+  "normal",
+  "notable",
+  "outgoing",
+  "precious",
+  "pretty",
+  "prickly",
+  "primary",
+  "pristine",
+  "private",
+  "prize",
+  "productive",
+  "profitable",
+  "quiet",
+  "quintessential",
+  "roasted",
+  "robust",
+  "square",
+  "squiggly",
+  "stable",
+  "staid",
+  "starry",
+  "steel",
+  "stimulating",
+  "striking",
+  "striped",
+  "strong",
+  "studious",
+  "stunning",
+  "tough",
+  "trained",
+  "treasured",
+  "tremendous",
+  "triangular",
+  "tricky",
+  "unused",
+  "unusual",
+  "upbeat",
+  "virtual",
+  "witty",
+  "wonderful",
+  "wooden",
+  "worldly",
+  "youthful",
+  "attached",
+  "attentive",
+  "attractive",
+  "austere",
+  "authentic",
+  "automatic",
+  "aware",
+  "awesome",
+  "bubbly",
+  "bustling",
+  "busy",
+  "buttery",
+  "cuddly",
+  "cultured",
+  "curly",
+  "curvy",
+  "cute",
+  "cylindrical",
+  "downright",
+  "dramatic",
+  "excited",
+  "exciting",
+  "exotic",
+  "experienced",
+  "expert",
+  "frosty",
+  "fruitful",
+  "full",
+  "fumbling",
+  "funny",
+  "fussy",
+  "growing",
+  "grown",
+  "gummy",
+  "humble",
+  "humongous",
+  "hungry",
+  "intelligent",
+  "interesting",
+  "known",
+  "kooky",
+  "kosher",
+  "loyal",
+  "lucky",
+  "luminous",
+  "lustrous",
+  "luxurious",
+  "multicolored",
+  "mysterious",
+  "noteworthy",
+  "numb",
+  "nutritious",
+  "outstanding",
+  "overjoyed",
+  "proper",
+  "proud",
+  "prudent",
+  "punctual",
+  "puny",
+  "pure",
+  "puzzled",
+  "puzzling",
+  "quirky",
+  "stupendous",
+  "sturdy",
+  "stylish",
+  "subdued",
+  "subtle",
+  "sunny",
+  "super",
+  "superb",
+  "supportive",
+  "surprised",
+  "sweet",
+  "swift",
+  "sympathetic",
+  "trivial",
+  "trusting",
+  "trustworthy",
+  "trusty",
+  "truthful",
+  "twin",
+  "usable",
+  "used",
+  "useful",
+  "utilized",
+  "vital",
+  "vivid",
+  "worried",
+  "worthwhile",
+  "worthy",
+  "writhing",
+  "wry",
+  "yummy",
+  "chocolate",
+  "crimson",
+  "cyan",
+  "fuchsia",
+  "gold",
+  "honeydew",
+  "lime",
+  "linen",
+  "magenta",
+  "olive",
+  "peru",
+  "salmon",
+  "seashell",
+  "sienna",
+  "snow",
+  "thistle",
+  "tomato",
+  "transparent",
+  "turquoise",
+  "violet"
+];
+
+const nouns = [
+  "space",
+  "land",
+  "world",
+  "universe",
+  "plane",
+  "room",
+  "nation",
+  "plaza",
+  "gathering",
+  "meetup",
+  "get together",
+  "conclave",
+  "party",
+  "powwow",
+  "domain",
+  "dominion",
+  "realm",
+  "square",
+  "commons",
+  "park",
+  "cosmos",
+  "sphere",
+  "terrain",
+  "spot",
+  "zone",
+  "area",
+  "tract",
+  "turf",
+  "place",
+  "territory",
+  "volume",
+  "camp",
+  "picnic",
+  "outing",
+  "vacation",
+  "adventure",
+  "exploration",
+  "outing",
+  "walkabout",
+  "safari",
+  "venture",
+  "roundtable",
+  "barbecue",
+  "celebration",
+  "festivity",
+  "gala",
+  "shindig",
+  "social",
+  "convention",
+  "assembly",
+  "congregation",
+  "rendezvous",
+  "huddle",
+  "meet"
+];
+
+export function generateHubName() {
+  const randItem = set => {
+    const s = set[Math.floor(Math.random() * set.length)];
+    return s.charAt(0).toUpperCase() + s.slice(1);
+  };
+
+  return `${randItem(adjectives)} ${randItem(adjectives)} ${randItem(nouns)}`;
+}