diff --git a/package.json b/package.json
index 82d9dc575e093de0ebc3c88eafc24197bda403e2..d3585914f7bfab8ae13fe2d52bb92f522666e578 100644
--- a/package.json
+++ b/package.json
@@ -87,9 +87,9 @@
     "react-redux": "5.0.7",
     "react-responsive": "5.0.0",
     "react-style-proptype": "3.2.1",
-    "react-tabs": "2.2.2",
+    "react-tabs": "2.3.0",
     "react-test-renderer": "16.2.0",
-    "react-tooltip": "3.6.1",
+    "react-tooltip": "3.8.0",
     "react-virtualized": "9.20.1",
     "redux": "3.7.2",
     "redux-mock-store": "^1.2.3",
@@ -98,11 +98,11 @@
     "scratch-audio": "0.1.0-prerelease.20180625202813",
     "scratch-blocks": "0.1.0-prerelease.1535662135",
     "scratch-l10n": "3.0.20180830210150",
-    "scratch-paint": "0.2.0-prerelease.20180831175055",
+    "scratch-paint": "0.2.0-prerelease.20180905220723",
     "scratch-render": "0.1.0-prerelease.20180824141819",
     "scratch-storage": "1.0.2",
     "scratch-svg-renderer": "0.2.0-prerelease.20180817005452",
-    "scratch-vm": "0.2.0-prerelease.20180830155110",
+    "scratch-vm": "0.2.0-prerelease.20180906133035",
     "selenium-webdriver": "3.6.0",
     "startaudiocontext": "1.2.1",
     "style-loader": "^0.23.0",
diff --git a/src/components/browser-modal/browser-modal.jsx b/src/components/browser-modal/browser-modal.jsx
index 0a271ea7a7838ba2a3500e02503c5bf8b41e24f7..9afac0e3891f6026874bcb0528c32b1661a1fbde 100644
--- a/src/components/browser-modal/browser-modal.jsx
+++ b/src/components/browser-modal/browser-modal.jsx
@@ -84,4 +84,8 @@ BrowserModal.propTypes = {
     onBack: PropTypes.func.isRequired
 };
 
-export default injectIntl(BrowserModal);
+const WrappedBrowserModal = injectIntl(BrowserModal);
+
+WrappedBrowserModal.setAppElement = ReactModal.setAppElement;
+
+export default WrappedBrowserModal;
diff --git a/src/components/gui/gui.jsx b/src/components/gui/gui.jsx
index 96fea86537fb95b886a43268dac70d77334d2e2f..71c623135093df1f503ff64c93a57183eb33e761 100644
--- a/src/components/gui/gui.jsx
+++ b/src/components/gui/gui.jsx
@@ -62,7 +62,6 @@ const GUIComponent = props => {
         costumeLibraryVisible,
         costumesTabVisible,
         enableCommunity,
-        hideIntro,
         importInfoVisible,
         intl,
         isPlayerOnly,
@@ -116,7 +115,7 @@ const GUIComponent = props => {
                 {...componentProps}
             >
                 {previewInfoVisible ? (
-                    <PreviewModal hideIntro={hideIntro} />
+                    <PreviewModal />
                 ) : null}
                 {loading ? (
                     <Loader />
@@ -282,7 +281,6 @@ GUIComponent.propTypes = {
     costumeLibraryVisible: PropTypes.bool,
     costumesTabVisible: PropTypes.bool,
     enableCommunity: PropTypes.bool,
-    hideIntro: PropTypes.bool,
     importInfoVisible: PropTypes.bool,
     intl: intlShape.isRequired,
     isPlayerOnly: PropTypes.bool,
diff --git a/src/containers/gui.jsx b/src/containers/gui.jsx
index 6e5d96070b77135abfeb28b64101928a4c00b4c6..f08e6bab746dd171f040e48e916663bbf438538b 100644
--- a/src/containers/gui.jsx
+++ b/src/containers/gui.jsx
@@ -72,10 +72,15 @@ class GUI extends React.Component {
                 `Failed to load project from server [id=${window.location.hash}]: ${this.state.errorMessage}`);
         }
         const {
+            /* eslint-disable no-unused-vars */
+            assetHost,
+            hideIntro,
+            projectData,
+            projectHost,
+            /* eslint-enable no-unused-vars */
             children,
             fetchingProject,
             loadingStateVisible,
-            projectData, // eslint-disable-line no-unused-vars
             vm,
             ...componentProps
         } = this.props;
@@ -94,6 +99,7 @@ class GUI extends React.Component {
 GUI.propTypes = {
     children: PropTypes.node,
     fetchingProject: PropTypes.bool,
+    hideIntro: PropTypes.bool,
     importInfoVisible: PropTypes.bool,
     loadingStateVisible: PropTypes.bool,
     onSeeCommunity: PropTypes.func,
@@ -102,7 +108,7 @@ GUI.propTypes = {
     vm: PropTypes.instanceOf(VM)
 };
 
-const mapStateToProps = state => ({
+const mapStateToProps = (state, ownProps) => ({
     activeTabIndex: state.scratchGui.editorTab.activeTabIndex,
     backdropLibraryVisible: state.scratchGui.modals.backdropLibrary,
     blocksTabVisible: state.scratchGui.editorTab.activeTabIndex === BLOCKS_TAB_INDEX,
@@ -113,7 +119,7 @@ const mapStateToProps = state => ({
     isPlayerOnly: state.scratchGui.mode.isPlayerOnly,
     isRtl: state.locales.isRtl,
     loadingStateVisible: state.scratchGui.modals.loadingProject,
-    previewInfoVisible: state.scratchGui.modals.previewInfo,
+    previewInfoVisible: state.scratchGui.modals.previewInfo && !ownProps.hideIntro,
     targetIsStage: (
         state.scratchGui.targets.stage &&
         state.scratchGui.targets.stage.id === state.scratchGui.targets.editingTarget
diff --git a/src/containers/preview-modal.jsx b/src/containers/preview-modal.jsx
index 7145cfa6acbb3170a85a4bc1c9d44673731fc64e..47b3cc3115ef6cfda1a38c31a4906566fc984498 100644
--- a/src/containers/preview-modal.jsx
+++ b/src/containers/preview-modal.jsx
@@ -6,8 +6,6 @@ import {connect} from 'react-redux';
 import tabletFullScreen from '../lib/tablet-full-screen';
 
 import PreviewModalComponent from '../components/preview-modal/preview-modal.jsx';
-import BrowserModalComponent from '../components/browser-modal/browser-modal.jsx';
-import supportedBrowser from '../lib/supported-browser';
 
 import {
     closePreviewInfo,
@@ -27,25 +25,6 @@ class PreviewModal extends React.Component {
             previewing: false
         };
     }
-
-    /**
-     * Conditionally returns an intro modal depending on the hideIntro prop
-     * @returns { React.Component | null } null if hideIntro is true, the intro modal component otherwise
-     */
-    introIfShown () {
-        if (this.props.hideIntro) {
-            return null; // If hideIntro is true, the intro modal should not appear
-        }
-
-        // otherwise, show the intro modal
-        return (<PreviewModalComponent
-            isRtl={this.props.isRtl}
-            previewing={this.state.previewing}
-            onCancel={this.handleCancel}
-            onTryIt={this.handleTryIt}
-            onViewProject={this.handleViewProject}
-        />);
-    }
     handleTryIt () {
         this.setState({previewing: true});
         // try to run in fullscreen mode on tablets.
@@ -59,18 +38,19 @@ class PreviewModal extends React.Component {
         this.props.onViewProject();
     }
     render () {
-        return (supportedBrowser() ?
-            this.introIfShown() :
-            <BrowserModalComponent
+        return (
+            <PreviewModalComponent
                 isRtl={this.props.isRtl}
-                onBack={this.handleCancel}
+                previewing={this.state.previewing}
+                onCancel={this.handleCancel}
+                onTryIt={this.handleTryIt}
+                onViewProject={this.handleViewProject}
             />
         );
     }
 }
 
 PreviewModal.propTypes = {
-    hideIntro: PropTypes.bool,
     isRtl: PropTypes.bool,
     onTryIt: PropTypes.func,
     onViewProject: PropTypes.func
diff --git a/src/lib/app-state-hoc.jsx b/src/lib/app-state-hoc.jsx
index 2f70b44d251f52d5ccec5fb08ae6eb786bf79794..fe7ac023eab97c5df5aea8e5829a6769d28392b5 100644
--- a/src/lib/app-state-hoc.jsx
+++ b/src/lib/app-state-hoc.jsx
@@ -4,7 +4,6 @@ import {Provider} from 'react-redux';
 import {createStore, combineReducers, compose} from 'redux';
 import ConnectedIntlProvider from './connected-intl-provider.jsx';
 
-import guiReducer, {guiInitialState, guiMiddleware, initFullScreen, initPlayer} from '../reducers/gui';
 import localesReducer, {initLocale, localesInitialState} from '../reducers/locales';
 
 import {setPlayer, setFullScreen} from '../reducers/mode.js';
@@ -12,49 +11,76 @@ import {setPlayer, setFullScreen} from '../reducers/mode.js';
 import locales from 'scratch-l10n';
 import {detectLocale} from './detect-locale';
 
-import {ScratchPaintReducer} from 'scratch-paint';
-
 const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
-const enhancer = composeEnhancers(guiMiddleware);
 
 /*
  * Higher Order Component to provide redux state. If an `intl` prop is provided
  * it will override the internal `intl` redux state
  * @param {React.Component} WrappedComponent - component to provide state for
+ * @param {boolean} localesOnly - only provide the locale state, not everything
+ *                      required by the GUI. Used to exclude excess state when
+                        only rendering modals, not the GUI.
  * @returns {React.Component} component with redux and intl state provided
  */
-const AppStateHOC = function (WrappedComponent) {
+const AppStateHOC = function (WrappedComponent, localesOnly) {
     class AppStateWrapper extends React.Component {
         constructor (props) {
             super(props);
-            let initializedGui = guiInitialState;
-            if (props.isFullScreen) {
-                initializedGui = initFullScreen(initializedGui);
-            }
-            if (props.isPlayerOnly) {
-                initializedGui = initPlayer(initializedGui);
-            }
+            let initialState = {};
+            let reducers = {};
+            let enhancer;
 
             let initializedLocales = localesInitialState;
             const locale = detectLocale(Object.keys(locales));
             if (locale !== 'en') {
                 initializedLocales = initLocale(initializedLocales, locale);
             }
+            if (localesOnly) {
+                // Used for instantiating minimal state for the unsupported
+                // browser modal
+                reducers = {locales: localesReducer};
+                initialState = {locales: initializedLocales};
+                enhancer = composeEnhancers();
+            } else {
+                // You are right, this is gross. But it's necessary to avoid
+                // importing unneeded code that will crash unsupported browsers.
+                const guiRedux = require('../reducers/gui');
+                const guiReducer = guiRedux.default;
+                const {
+                    guiInitialState,
+                    guiMiddleware,
+                    initFullScreen,
+                    initPlayer
+                } = guiRedux;
+                const {ScratchPaintReducer} = require('scratch-paint');
 
-            const reducer = combineReducers({
-                locales: localesReducer,
-                scratchGui: guiReducer,
-                scratchPaint: ScratchPaintReducer
-            });
-            this.store = createStore(
-                reducer,
-                {
+                let initializedGui = guiInitialState;
+                if (props.isFullScreen) {
+                    initializedGui = initFullScreen(initializedGui);
+                }
+                if (props.isPlayerOnly) {
+                    initializedGui = initPlayer(initializedGui);
+                }
+                reducers = {
+                    locales: localesReducer,
+                    scratchGui: guiReducer,
+                    scratchPaint: ScratchPaintReducer
+                };
+                initialState = {
                     locales: initializedLocales,
                     scratchGui: initializedGui
-                },
-                enhancer);
+                };
+                enhancer = composeEnhancers(guiMiddleware);
+            }
+            const reducer = combineReducers(reducers);
+            this.store = createStore(
+                reducer,
+                initialState,
+                enhancer
+            );
         }
         componentDidUpdate (prevProps) {
+            if (localesOnly) return;
             if (prevProps.isPlayerOnly !== this.props.isPlayerOnly) {
                 this.store.dispatch(setPlayer(this.props.isPlayerOnly));
             }
diff --git a/src/playground/index.jsx b/src/playground/index.jsx
index da2be7a27831b6179acb436d6b441f66e86c9dcf..ba0ada4da78fbc561cbd7eca365e8a73aa2b62c8 100644
--- a/src/playground/index.jsx
+++ b/src/playground/index.jsx
@@ -7,17 +7,12 @@ import React from 'react';
 import ReactDOM from 'react-dom';
 
 import analytics from '../lib/analytics';
-import GUI from '../containers/gui.jsx';
-import HashParserHOC from '../lib/hash-parser-hoc.jsx';
 import AppStateHOC from '../lib/app-state-hoc.jsx';
+import BrowserModalComponent from '../components/browser-modal/browser-modal.jsx';
+import supportedBrowser from '../lib/supported-browser';
 
 import styles from './index.css';
 
-if (process.env.NODE_ENV === 'production' && typeof window === 'object') {
-    // Warn before navigating away
-    window.onbeforeunload = () => true;
-}
-
 // Register "base" page view
 analytics.pageview('/');
 
@@ -25,16 +20,15 @@ const appTarget = document.createElement('div');
 appTarget.className = styles.app;
 document.body.appendChild(appTarget);
 
-GUI.setAppElement(appTarget);
-const WrappedGui = HashParserHOC(AppStateHOC(GUI));
-
-// TODO a hack for testing the backpack, allow backpack host to be set by url param
-const backpackHostMatches = window.location.href.match(/[?&]backpack_host=([^&]*)&?/);
-const backpackHost = backpackHostMatches ? backpackHostMatches[1] : null;
-
-const backpackOptions = {
-    visible: true,
-    host: backpackHost
-};
-
-ReactDOM.render(<WrappedGui backpackOptions={backpackOptions} />, appTarget);
+if (supportedBrowser()) {
+    // require needed here to avoid importing unsupported browser-crashing code
+    // at the top level
+    require('./render-gui.jsx').default(appTarget);
+
+} else {
+    BrowserModalComponent.setAppElement(appTarget);
+    const WrappedBrowserModalComponent = AppStateHOC(BrowserModalComponent, true /* localesOnly */);
+    const handleBack = () => {};
+    // eslint-disable-next-line react/jsx-no-bind
+    ReactDOM.render(<WrappedBrowserModalComponent onBack={handleBack} />, appTarget);
+}
diff --git a/src/playground/render-gui.jsx b/src/playground/render-gui.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..adec922b8c206c9211888da34a8c426362b9a10b
--- /dev/null
+++ b/src/playground/render-gui.jsx
@@ -0,0 +1,31 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
+
+import AppStateHOC from '../lib/app-state-hoc.jsx';
+import GUI from '../containers/gui.jsx';
+import HashParserHOC from '../lib/hash-parser-hoc.jsx';
+
+/*
+ * Render the GUI playground. This is a separate function because importing anything
+ * that instantiates the VM causes unsupported browsers to crash
+ * {object} appTarget - the DOM element to render to
+ */
+export default appTarget => {
+    GUI.setAppElement(appTarget);
+    const WrappedGui = HashParserHOC(AppStateHOC(GUI));
+
+    // TODO a hack for testing the backpack, allow backpack host to be set by url param
+    const backpackHostMatches = window.location.href.match(/[?&]backpack_host=([^&]*)&?/);
+    const backpackHost = backpackHostMatches ? backpackHostMatches[1] : null;
+
+    const backpackOptions = {
+        visible: true,
+        host: backpackHost
+    };
+    if (process.env.NODE_ENV === 'production' && typeof window === 'object') {
+        // Warn before navigating away
+        window.onbeforeunload = () => true;
+    }
+
+    ReactDOM.render(<WrappedGui backpackOptions={backpackOptions} />, appTarget);
+};