diff --git a/package.json b/package.json
index 00718bd23e6230581e5146499ae66f6a78dcf544..7cadf34d5da3ce08ddf66e59dbcd30a86d785f0e 100644
--- a/package.json
+++ b/package.json
@@ -82,8 +82,8 @@
     "react-draggable": "3.0.5",
     "react-ga": "2.5.3",
     "react-intl": "2.4.0",
-    "react-modal": "3.5.1",
-    "react-popover": "0.5.7",
+    "react-modal": "3.6.1",
+    "react-popover": "0.5.10",
     "react-redux": "5.0.7",
     "react-responsive": "5.0.0",
     "react-style-proptype": "3.2.2",
@@ -96,13 +96,13 @@
     "redux-throttle": "0.1.1",
     "rimraf": "^2.6.1",
     "scratch-audio": "0.1.0-prerelease.20180625202813",
-    "scratch-blocks": "0.1.0-prerelease.1537303399",
-    "scratch-l10n": "3.0.20180918211645",
-    "scratch-paint": "0.2.0-prerelease.20180918203812",
-    "scratch-render": "0.1.0-prerelease.20180918201144",
+    "scratch-blocks": "0.1.0-prerelease.1537975589",
+    "scratch-l10n": "3.0.20180926203705",
+    "scratch-paint": "0.2.0-prerelease.20180926191006",
+    "scratch-render": "0.1.0-prerelease.20180926153819",
     "scratch-storage": "1.0.2",
-    "scratch-svg-renderer": "0.2.0-prerelease.20180907141232",
-    "scratch-vm": "0.2.0-prerelease.20180918201814",
+    "scratch-svg-renderer": "0.2.0-prerelease.20180926143036",
+    "scratch-vm": "0.2.0-prerelease.20180925190229",
     "selenium-webdriver": "3.6.0",
     "startaudiocontext": "1.2.1",
     "style-loader": "^0.23.0",
diff --git a/src/components/alerts/alert.css b/src/components/alerts/alert.css
new file mode 100644
index 0000000000000000000000000000000000000000..250c79d2a9f77fd6f0872244dc25b2dc3eefbb15
--- /dev/null
+++ b/src/components/alerts/alert.css
@@ -0,0 +1,34 @@
+@import "../../css/units.css";
+@import "../../css/colors.css";
+@import "../../css/z-index.css";
+
+.alert {
+    width: 100%;
+    background: #FFF0DF;
+    display: flex;
+    flex-direction: row;
+    overflow: hidden;
+    align-items: left;
+    border: 1px solid #FF8C1A;
+    border-radius: 8px;
+    padding: 12px;
+    box-shadow: 2px 2px 2px 2px rgba(255, 140, 26, 0.25);
+    margin-bottom: 7px;
+}
+
+.alert-icon {
+    vertical-align: middle;
+    margin-right: 5px;
+}
+
+.alert-message {
+    color: #555;
+    font-weight: bold;
+    font-size: 12px;
+    line-height: 22pt;
+    width: 100%;
+}
+
+.alert-remove-button {
+    color: #FF8C1A;
+}
diff --git a/src/components/alerts/alert.jsx b/src/components/alerts/alert.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..63ac06f5123945c8fbbc54b0faf8b48b05d372b2
--- /dev/null
+++ b/src/components/alerts/alert.jsx
@@ -0,0 +1,41 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+
+import Box from '../box/box.jsx';
+import Button from '../button/button.jsx';
+
+import styles from './alert.css';
+
+const AlertComponent = ({
+    iconURL,
+    message,
+    onCloseAlert
+}) => (
+    <Box
+        className={styles.alert}
+    >
+        <div className={styles.alertMessage}>
+            {iconURL ? (
+                <img
+                    className={styles.alertIcon}
+                    src={iconURL}
+                />
+            ) : null}
+            {message}
+        </div>
+        <Button
+            className={styles.alertRemoveButton}
+            onClick={onCloseAlert}
+        >
+            {'x'}
+        </Button>
+    </Box>
+);
+
+AlertComponent.propTypes = {
+    iconURL: PropTypes.string,
+    message: PropTypes.string,
+    onCloseAlert: PropTypes.func.isRequired
+};
+
+export default AlertComponent;
diff --git a/src/components/gui/gui.css b/src/components/gui/gui.css
index 086011f4ba0a975499f0f7892fcc2eaa3bcee5b5..16f79ea4d684308cf8a6095bb4246b7966eb87af 100644
--- a/src/components/gui/gui.css
+++ b/src/components/gui/gui.css
@@ -278,3 +278,15 @@ $fade-out-distance: 15px;
 .extension-button > div {
     margin-top: 0;
 }
+
+/* Alerts */
+
+.alerts-container {
+    width: 448px;
+    z-index: $z-index-alerts;
+    left: 0;
+    right: 0;
+    margin: auto;
+    position: absolute;
+    margin-top: 53px;
+}
diff --git a/src/components/gui/gui.jsx b/src/components/gui/gui.jsx
index aa1dfc0393388111d80c4179e21b7a0d62fa95f1..9cdafaa8cfb31143e74cf879ffcfaef8a0d7e6ca 100644
--- a/src/components/gui/gui.jsx
+++ b/src/components/gui/gui.jsx
@@ -27,6 +27,7 @@ import ImportModal from '../../containers/import-modal.jsx';
 import WebGlModal from '../../containers/webgl-modal.jsx';
 import TipsLibrary from '../../containers/tips-library.jsx';
 import Cards from '../../containers/cards.jsx';
+import Alerts from '../../containers/alerts.jsx';
 import DragLayer from '../../containers/drag-layer.jsx';
 
 import layout, {STAGE_SIZE_MODES} from '../../lib/layout-constants';
@@ -52,7 +53,9 @@ let isRendererSupported = null;
 
 const GUIComponent = props => {
     const {
+        accountNavOpen,
         activeTabIndex,
+        alertsVisible,
         basePath,
         backdropLibraryVisible,
         backpackOptions,
@@ -67,14 +70,21 @@ const GUIComponent = props => {
         isPlayerOnly,
         isRtl,
         loading,
-        onExtensionButtonClick,
+        renderLogin,
+        onClickAccountNav,
+        onCloseAccountNav,
+        onLogOut,
+        onOpenRegistration,
+        onToggleLoginOpen,
+        onUpdateProjectTitle,
         onActivateCostumesTab,
         onActivateSoundsTab,
         onActivateTab,
+        onExtensionButtonClick,
         onRequestCloseBackdropLibrary,
         onRequestCloseCostumeLibrary,
         onSeeCommunity,
-        onUpdateProjectTitle,
+        onShare,
         previewInfoVisible,
         targetIsStage,
         soundsTabVisible,
@@ -108,7 +118,11 @@ const GUIComponent = props => {
                 isRendererSupported={isRendererSupported}
                 stageSize={stageSize}
                 vm={vm}
-            />
+            >
+                {alertsVisible ? (
+                    <Alerts className={styles.alertsContainer} />
+                ) : null}
+            </StageWrapper>
         ) : (
             <Box
                 className={styles.pageWrapper}
@@ -133,6 +147,9 @@ const GUIComponent = props => {
                 {cardsVisible ? (
                     <Cards />
                 ) : null}
+                {alertsVisible ? (
+                    <Alerts className={styles.alertsContainer} />
+                ) : null}
                 {costumeLibraryVisible ? (
                     <CostumeLibrary
                         vm={vm}
@@ -146,8 +163,16 @@ const GUIComponent = props => {
                     />
                 ) : null}
                 <MenuBar
+                    accountNavOpen={accountNavOpen}
                     enableCommunity={enableCommunity}
+                    renderLogin={renderLogin}
+                    onClickAccountNav={onClickAccountNav}
+                    onCloseAccountNav={onCloseAccountNav}
+                    onLogOut={onLogOut}
+                    onOpenRegistration={onOpenRegistration}
                     onSeeCommunity={onSeeCommunity}
+                    onShare={onShare}
+                    onToggleLoginOpen={onToggleLoginOpen}
                     onUpdateProjectTitle={onUpdateProjectTitle}
                 />
                 <Box className={styles.bodyWrapper}>
@@ -270,6 +295,7 @@ const GUIComponent = props => {
 };
 
 GUIComponent.propTypes = {
+    accountNavOpen: PropTypes.bool,
     activeTabIndex: PropTypes.number,
     backdropLibraryVisible: PropTypes.bool,
     backpackOptions: PropTypes.shape({
@@ -291,13 +317,20 @@ GUIComponent.propTypes = {
     onActivateCostumesTab: PropTypes.func,
     onActivateSoundsTab: PropTypes.func,
     onActivateTab: PropTypes.func,
+    onClickAccountNav: PropTypes.func,
+    onCloseAccountNav: PropTypes.func,
     onExtensionButtonClick: PropTypes.func,
+    onLogOut: PropTypes.func,
+    onOpenRegistration: PropTypes.func,
     onRequestCloseBackdropLibrary: PropTypes.func,
     onRequestCloseCostumeLibrary: PropTypes.func,
     onSeeCommunity: PropTypes.func,
+    onShare: PropTypes.func,
     onTabSelect: PropTypes.func,
+    onToggleLoginOpen: PropTypes.func,
     onUpdateProjectTitle: PropTypes.func,
     previewInfoVisible: PropTypes.bool,
+    renderLogin: PropTypes.func,
     soundsTabVisible: PropTypes.bool,
     stageSizeMode: PropTypes.oneOf(Object.keys(STAGE_SIZE_MODES)),
     targetIsStage: PropTypes.bool,
diff --git a/src/components/library/library.jsx b/src/components/library/library.jsx
index 9d63fc3bfad63cab11cf3f8df27bec40d7f9c13e..9ff4ead42b382166655a48335ced57026dee7ee3 100644
--- a/src/components/library/library.jsx
+++ b/src/components/library/library.jsx
@@ -13,17 +13,22 @@ import analytics from '../../lib/analytics';
 
 import styles from './library.css';
 
-const ALL_TAG_TITLE = 'All';
-const tagListPrefix = [{title: ALL_TAG_TITLE}];
-
 const messages = defineMessages({
     filterPlaceholder: {
         id: 'gui.library.filterPlaceholder',
         defaultMessage: 'Search',
         description: 'Placeholder text for library search field'
+    },
+    allTag: {
+        id: 'gui.library.allTag',
+        defaultMessage: 'All',
+        description: 'Label for library tag to revert to all items after filtering by tag.'
     }
 });
 
+const ALL_TAG = {tag: 'all', intlLabel: messages.allTag};
+const tagListPrefix = [ALL_TAG];
+
 class LibraryComponent extends React.Component {
     constructor (props) {
         super(props);
@@ -42,7 +47,7 @@ class LibraryComponent extends React.Component {
         this.state = {
             selectedItem: null,
             filterQuery: '',
-            selectedTag: ALL_TAG_TITLE.toLowerCase()
+            selectedTag: ALL_TAG.tag
         };
     }
     componentDidUpdate (prevProps, prevState) {
@@ -80,7 +85,7 @@ class LibraryComponent extends React.Component {
     handleFilterChange (event) {
         this.setState({
             filterQuery: event.target.value,
-            selectedTag: ALL_TAG_TITLE.toLowerCase()
+            selectedTag: ALL_TAG.tag
         });
     }
     handleFilterClear () {
@@ -141,7 +146,7 @@ class LibraryComponent extends React.Component {
                             <div className={styles.tagWrapper}>
                                 {tagListPrefix.concat(this.props.tags).map((tagProps, id) => (
                                     <TagButton
-                                        active={this.state.selectedTag === tagProps.title.toLowerCase()}
+                                        active={this.state.selectedTag === tagProps.tag.toLowerCase()}
                                         className={classNames(
                                             styles.filterBarItem,
                                             styles.tagButton,
diff --git a/src/components/menu-bar/account-nav.css b/src/components/menu-bar/account-nav.css
new file mode 100644
index 0000000000000000000000000000000000000000..c04f0709ee8fc463535399a5149e3a443aecf42e
--- /dev/null
+++ b/src/components/menu-bar/account-nav.css
@@ -0,0 +1,50 @@
+@import "../../css/colors.css";
+@import "../../css/units.css";
+
+.user-info {
+    display: inline-flex;
+    flex-wrap: nowrap;
+    justify-content: center;
+    align-items: center;
+    align-content: center;
+    padding: 0 0.95rem;
+    max-width: 260px;
+    height: $menu-bar-height;
+    overflow: hidden;
+    text-decoration: none;
+    text-overflow: ellipsis;
+    white-space: nowrap;
+    color: $ui-white;
+    font-size: .75rem;
+    font-weight: normal;
+}
+
+[dir="ltr"] .user-info .avatar {
+    margin-right: calc($space * .8125);
+}
+
+[dir="rtl"] .user-info .avatar {
+    margin-left: calc($space * .8125);
+}
+
+.user-info .avatar {
+    margin-right: calc($space * .8125);
+    border-radius: $form-radius;
+    width: 2rem;
+    height: 2rem;
+    vertical-align: middle;
+}
+
+[dir="ltr"] .user-info .dropdown-caret-position {
+  margin-left: calc($space * .8125);
+}
+
+[dir="rtl"] .user-info .dropdown-caret-position {
+  margin-right: calc($space * .8125);
+}
+
+.user-info .dropdown-caret-position {
+  display: inline-block;
+  padding-bottom: .125rem;
+  vertical-align: middle;
+}
diff --git a/src/components/menu-bar/account-nav.jsx b/src/components/menu-bar/account-nav.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..351ae2136557e41da84df8f9d52ee1e3c9534144
--- /dev/null
+++ b/src/components/menu-bar/account-nav.jsx
@@ -0,0 +1,110 @@
+/*
+NOTE: this file only temporarily resides in scratch-gui.
+Nearly identical code appears in scratch-www, and the two should
+eventually be consolidated.
+*/
+
+import classNames from 'classnames';
+import {FormattedMessage} from 'react-intl';
+import PropTypes from 'prop-types';
+import React from 'react';
+
+import MenuBarMenu from './menu-bar-menu.jsx';
+import {MenuSection} from '../menu/menu.jsx';
+import MenuItemContainer from '../../containers/menu-item.jsx';
+import dropdownCaret from './dropdown-caret.svg';
+
+import styles from './account-nav.css';
+
+const AccountNavComponent = ({
+    className,
+    classroomId,
+    isEducator,
+    isOpen,
+    isRtl,
+    isStudent,
+    menuBarMenuClassName,
+    onClick,
+    onClose,
+    onLogOut,
+    profileUrl,
+    thumbnailUrl,
+    username
+}) => (
+    <React.Fragment>
+        <div
+            className={classNames(
+                styles.userInfo,
+                className
+            )}
+            onMouseUp={onClick}
+        >
+            {thumbnailUrl ? (
+                <img
+                    className={styles.avatar}
+                    src={thumbnailUrl}
+                />
+            ) : null}
+            <span className={styles.profileName}>
+                {username}
+            </span>
+            <div className={styles.dropdownCaretPosition}>
+                <img
+                    className={styles.dropdownCaretIcon}
+                    src={dropdownCaret}
+                />
+            </div>
+        </div>
+        <MenuBarMenu
+            className={menuBarMenuClassName}
+            open={isOpen}
+            // note: the Rtl styles are switched here, because this menu is justified
+            // opposite all the others
+            place={isRtl ? 'right' : 'left'}
+            onRequestClose={onClose}
+        >
+            <MenuItemContainer href={profileUrl}>
+                <FormattedMessage id="general.profile" />
+            </MenuItemContainer>
+            <MenuItemContainer href="/mystuff/">
+                <FormattedMessage id="general.myStuff" />
+            </MenuItemContainer>
+            {isEducator ? (
+                <MenuItemContainer href="/educators/classes/">
+                    <FormattedMessage id="general.myClasses" />
+                </MenuItemContainer>
+            ) : null}
+            {isStudent ? (
+                <MenuItemContainer href={`/classes/${classroomId}/`}>
+                    <FormattedMessage id="general.myClass" />
+                </MenuItemContainer>
+            ) : null}
+            <MenuItemContainer href="/accounts/settings/">
+                <FormattedMessage id="general.accountSettings" />
+            </MenuItemContainer>
+            <MenuSection>
+                <MenuItemContainer onClick={onLogOut}>
+                    <FormattedMessage id="navigation.signOut" />
+                </MenuItemContainer>
+            </MenuSection>
+        </MenuBarMenu>
+    </React.Fragment>
+);
+
+AccountNavComponent.propTypes = {
+    className: PropTypes.string,
+    classroomId: PropTypes.string,
+    isEducator: PropTypes.bool,
+    isOpen: PropTypes.bool,
+    isRtl: PropTypes.bool,
+    isStudent: PropTypes.bool,
+    menuBarMenuClassName: PropTypes.string,
+    onClick: PropTypes.func,
+    onClose: PropTypes.func,
+    onLogOut: PropTypes.func,
+    profileUrl: PropTypes.string,
+    thumbnailUrl: PropTypes.string,
+    username: PropTypes.string
+};
+
+export default AccountNavComponent;
diff --git a/src/components/language-selector/dropdown-caret.svg b/src/components/menu-bar/dropdown-caret.svg
similarity index 100%
rename from src/components/language-selector/dropdown-caret.svg
rename to src/components/menu-bar/dropdown-caret.svg
diff --git a/src/components/menu-bar/login-dropdown.css b/src/components/menu-bar/login-dropdown.css
new file mode 100644
index 0000000000000000000000000000000000000000..8b16b7fdbfe18a0ad05de7438b72ab6cbff242d5
--- /dev/null
+++ b/src/components/menu-bar/login-dropdown.css
@@ -0,0 +1,4 @@
+
+.login {
+    padding: .625rem;
+}
diff --git a/src/components/menu-bar/login-dropdown.jsx b/src/components/menu-bar/login-dropdown.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..11d23856c8f35edbec895c3b311073b8cc7ec743
--- /dev/null
+++ b/src/components/menu-bar/login-dropdown.jsx
@@ -0,0 +1,50 @@
+/*
+NOTE: this file only temporarily resides in scratch-gui.
+Nearly identical code appears in scratch-www, and the two should
+eventually be consolidated.
+*/
+
+import classNames from 'classnames';
+import PropTypes from 'prop-types';
+import React from 'react';
+
+import MenuBarMenu from './menu-bar-menu.jsx';
+
+import styles from './login-dropdown.css';
+
+const LoginDropdown = ({
+    className,
+    isOpen,
+    isRtl,
+    onClose,
+    renderLogin
+}) => (
+    <MenuBarMenu
+        className={className}
+        open={isOpen}
+        // note: the Rtl styles are switched here, because this menu is justified
+        // opposite all the others
+        place={isRtl ? 'right' : 'left'}
+        onRequestClose={onClose}
+    >
+        <div
+            className={classNames(
+                styles.login
+            )}
+        >
+            {renderLogin({
+                onClose: onClose
+            })}
+        </div>
+    </MenuBarMenu>
+);
+
+LoginDropdown.propTypes = {
+    className: PropTypes.string,
+    isOpen: PropTypes.bool,
+    isRtl: PropTypes.bool,
+    onClose: PropTypes.func,
+    renderLogin: PropTypes.func
+};
+
+export default LoginDropdown;
diff --git a/src/components/menu-bar/menu-bar-menu.jsx b/src/components/menu-bar/menu-bar-menu.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..d71d9e04930c95ab79222881d5d87253597472c9
--- /dev/null
+++ b/src/components/menu-bar/menu-bar-menu.jsx
@@ -0,0 +1,31 @@
+import PropTypes from 'prop-types';
+import React from 'react';
+import Menu from '../../containers/menu.jsx';
+
+const MenuBarMenu = ({
+    children,
+    className,
+    onRequestClose,
+    open,
+    place = 'right'
+}) => (
+    <div className={className}>
+        <Menu
+            open={open}
+            place={place}
+            onRequestClose={onRequestClose}
+        >
+            {children}
+        </Menu>
+    </div>
+);
+
+MenuBarMenu.propTypes = {
+    children: PropTypes.node,
+    className: PropTypes.string,
+    onRequestClose: PropTypes.func,
+    open: PropTypes.bool,
+    place: PropTypes.oneOf(['left', 'right'])
+};
+
+export default MenuBarMenu;
diff --git a/src/components/menu-bar/menu-bar.css b/src/components/menu-bar/menu-bar.css
index 46fd7313c16c6a12c55799979d43a876f6e33eff..dcc230672499634335229e1e3295bd626319970a 100644
--- a/src/components/menu-bar/menu-bar.css
+++ b/src/components/menu-bar/menu-bar.css
@@ -58,11 +58,6 @@
     width: $language-selector-width;
 }
 
-.menu {
-    z-index: $z-index-menu-bar;
-    top: $menu-bar-height;
-}
-
 .menu-bar-item {
     display: flex;
     padding: 0 0.25rem;
@@ -105,6 +100,11 @@
     padding: 0 0.75rem;
 }
 
+.menu-bar-menu {
+    margin-top: $menu-bar-height;
+    z-index: $z-index-menu-bar;
+}
+
 .feedback-link {
     color: $motion-primary;
     text-decoration: none;
@@ -139,13 +139,16 @@
     opacity: 0.5;
 }
 
-.account-info-wrapper {
+.account-info-group {
     display: flex;
     flex-direction: row;
-    padding: 0 .5rem;
     align-items: center;
 }
 
+.account-info-group .menu-bar-item {
+    padding: 0 0.75rem;
+}
+
 .mystuff-icon {
     margin: 0 .25rem;
     height: 1rem;
@@ -197,3 +200,22 @@
     filter: hue-rotate(360deg);
   }
 }
+
+.mystuff > a {
+  background-repeat: no-repeat;
+  background-position: center center;
+  background-size: 45%;
+  padding-right: 10px;
+  padding-left: 10px;
+  width: 30px;
+  overflow: hidden;
+  text-indent: 50px;
+  white-space: nowrap;
+}
+.mystuff > a:hover {
+  background-size: 50%;
+}
+
+.mystuff > a {
+  background-image: url("/images/mystuff.png");
+}
diff --git a/src/components/menu-bar/menu-bar.jsx b/src/components/menu-bar/menu-bar.jsx
index 897fedc3f3166da91678c56be805566d0200b35d..7d321ede9ecb4c068e1fc2c8a0de36af2ff6cc9a 100644
--- a/src/components/menu-bar/menu-bar.jsx
+++ b/src/components/menu-bar/menu-bar.jsx
@@ -11,9 +11,11 @@ import {ComingSoonTooltip} from '../coming-soon/coming-soon.jsx';
 import Divider from '../divider/divider.jsx';
 import LanguageSelector from '../../containers/language-selector.jsx';
 import ProjectLoader from '../../containers/project-loader.jsx';
-import Menu from '../../containers/menu.jsx';
+import MenuBarMenu from './menu-bar-menu.jsx';
 import {MenuItem, MenuSection} from '../menu/menu.jsx';
 import ProjectTitleInput from './project-title-input.jsx';
+import AccountNav from '../../containers/account-nav.jsx';
+import LoginDropdown from './login-dropdown.jsx';
 import ProjectSaver from '../../containers/project-saver.jsx';
 import DeletionRestorer from '../../containers/deletion-restorer.jsx';
 import TurboMode from '../../containers/turbo-mode.jsx';
@@ -21,6 +23,9 @@ import TurboMode from '../../containers/turbo-mode.jsx';
 import {openTipsLibrary} from '../../reducers/modals';
 import {setPlayer} from '../../reducers/mode';
 import {
+    openAccountMenu,
+    closeAccountMenu,
+    accountMenuOpen,
     openFileMenu,
     closeFileMenu,
     fileMenuOpen,
@@ -29,7 +34,10 @@ import {
     editMenuOpen,
     openLanguageMenu,
     closeLanguageMenu,
-    languageMenuOpen
+    languageMenuOpen,
+    openLoginMenu,
+    closeLoginMenu,
+    loginMenuOpen
 } from '../../reducers/menus';
 
 import styles from './menu-bar.css';
@@ -39,7 +47,7 @@ import mystuffIcon from './icon--mystuff.png';
 import feedbackIcon from './icon--feedback.svg';
 import profileIcon from './icon--profile.png';
 import communityIcon from './icon--see-community.svg';
-import dropdownCaret from '../language-selector/dropdown-caret.svg';
+import dropdownCaret from './dropdown-caret.svg';
 import languageIcon from '../language-selector/language-icon.svg';
 
 import scratchLogo from './scratch-logo.svg';
@@ -111,28 +119,6 @@ MenuItemTooltip.propTypes = {
     isRtl: PropTypes.bool
 };
 
-const MenuBarMenu = ({
-    children,
-    onRequestClose,
-    open,
-    place = 'right'
-}) => (
-    <Menu
-        className={styles.menu}
-        open={open}
-        place={place}
-        onRequestClose={onRequestClose}
-    >
-        {children}
-    </Menu>
-);
-
-MenuBarMenu.propTypes = {
-    children: PropTypes.node,
-    onRequestClose: PropTypes.func,
-    open: PropTypes.bool,
-    place: PropTypes.oneOf(['left', 'right'])
-};
 class MenuBar extends React.Component {
     constructor (props) {
         super(props);
@@ -210,6 +196,18 @@ class MenuBar extends React.Component {
                 id="gui.menuBar.saveNow"
             />
         );
+        const shareButton = (
+            <Button
+                className={classNames(styles.shareButton)}
+                onClick={this.props.onShare}
+            >
+                <FormattedMessage
+                    defaultMessage="Share"
+                    description="Label for project share button"
+                    id="gui.menuBar.share"
+                />
+            </Button>
+        );
         return (
             <Box
                 className={classNames(styles.menuBar, {
@@ -253,14 +251,13 @@ class MenuBar extends React.Component {
                             })}
                             onMouseUp={this.props.onClickFile}
                         >
-                            <div className={classNames(styles.fileMenu)}>
-                                <FormattedMessage
-                                    defaultMessage="File"
-                                    description="Text for file dropdown menu"
-                                    id="gui.menuBar.file"
-                                />
-                            </div>
+                            <FormattedMessage
+                                defaultMessage="File"
+                                description="Text for file dropdown menu"
+                                id="gui.menuBar.file"
+                            />
                             <MenuBarMenu
+                                className={classNames(styles.menuBarMenu)}
                                 open={this.props.fileMenuOpen}
                                 place={this.props.isRtl ? 'left' : 'right'}
                                 onRequestClose={this.props.onRequestCloseFile}
@@ -301,7 +298,8 @@ class MenuBar extends React.Component {
                                                 defaultMessage="Save as a copy"
                                                 description="Menu bar item for saving as a copy"
                                                 id="gui.menuBar.saveAsCopy"
-                                            /></MenuItem>
+                                            />
+                                        </MenuItem>
                                     </MenuItemTooltip>
                                 </MenuSection>
                                 <MenuSection>
@@ -346,6 +344,7 @@ class MenuBar extends React.Component {
                                 />
                             </div>
                             <MenuBarMenu
+                                className={classNames(styles.menuBarMenu)}
                                 open={this.props.editMenuOpen}
                                 place={this.props.isRtl ? 'left' : 'right'}
                                 onRequestClose={this.props.onRequestCloseEdit}
@@ -405,15 +404,11 @@ class MenuBar extends React.Component {
                         </MenuBarItemTooltip>
                     </div>
                     <div className={classNames(styles.menuBarItem)}>
-                        <MenuBarItemTooltip id="share-button">
-                            <Button className={classNames(styles.shareButton)}>
-                                <FormattedMessage
-                                    defaultMessage="Share"
-                                    description="Label for project share button"
-                                    id="gui.menuBar.share"
-                                />
-                            </Button>
-                        </MenuBarItemTooltip>
+                        {this.props.onShare ? shareButton : (
+                            <MenuBarItemTooltip id="share-button">
+                                {shareButton}
+                            </MenuBarItemTooltip>
+                        )}
                     </div>
                     <div className={classNames(styles.menuBarItem, styles.communityButtonWrapper)}>
                         {this.props.enableCommunity ?
@@ -445,64 +440,145 @@ class MenuBar extends React.Component {
                         }
                     </div>
                 </div>
-                <div className={classNames(styles.menuBarItem, styles.feedbackButtonWrapper)}>
-                    <a
-                        className={styles.feedbackLink}
-                        href="https://scratch.mit.edu/discuss/topic/312261/"
-                        rel="noopener noreferrer"
-                        target="_blank"
-                    >
-                        <Button
-                            className={styles.feedbackButton}
-                            iconSrc={feedbackIcon}
-                        >
-                            <FormattedMessage
-                                defaultMessage="Give Feedback"
-                                description="Label for feedback form modal button"
-                                id="gui.menuBar.giveFeedback"
-                            />
-                        </Button>
-                    </a>
-                </div>
-                <div className={styles.accountInfoWrapper}>
-                    <MenuBarItemTooltip id="mystuff">
-                        <div
-                            className={classNames(
-                                styles.menuBarItem,
-                                styles.hoverable,
-                                styles.mystuffButton
-                            )}
-                        >
-                            <img
-                                className={styles.mystuffIcon}
-                                src={mystuffIcon}
-                            />
-                        </div>
-                    </MenuBarItemTooltip>
-                    <MenuBarItemTooltip
-                        id="account-nav"
-                        place={this.props.isRtl ? 'right' : 'left'}
-                    >
-                        <div
-                            className={classNames(
-                                styles.menuBarItem,
-                                styles.hoverable,
-                                styles.accountNavMenu
-                            )}
-                        >
-                            <img
-                                className={styles.profileIcon}
-                                src={profileIcon}
-                            />
-                            <span>
-                                {'scratch-cat' /* @todo username */}
-                            </span>
-                            <img
-                                className={styles.dropdownCaretIcon}
-                                src={dropdownCaret}
-                            />
-                        </div>
-                    </MenuBarItemTooltip>
+
+                {/* show the proper UI in the account menu, given whether the user is
+                logged in, and whether a session is available to log in with */}
+                <div className={styles.accountInfoGroup}>
+                    {this.props.sessionExists ? (
+                        this.props.username ? (
+                            // ************ user is logged in ************
+                            <React.Fragment>
+                                <a href="/mystuff/">
+                                    <div
+                                        className={classNames(
+                                            styles.menuBarItem,
+                                            styles.hoverable,
+                                            styles.mystuffButton
+                                        )}
+                                    >
+                                        <img
+                                            className={styles.mystuffIcon}
+                                            src={mystuffIcon}
+                                        />
+                                    </div>
+                                </a>
+                                <AccountNav
+                                    className={classNames(
+                                        styles.menuBarItem,
+                                        styles.hoverable,
+                                        {[styles.active]: this.props.accountMenuOpen}
+                                    )}
+                                    isOpen={this.props.accountMenuOpen}
+                                    isRtl={this.props.isRtl}
+                                    menuBarMenuClassName={classNames(styles.menuBarMenu)}
+                                    onClick={this.props.onClickAccount}
+                                    onClose={this.props.onRequestCloseAccount}
+                                    onLogOut={this.props.onLogOut}
+                                />
+                            </React.Fragment>
+                        ) : (
+                            // ********* user not logged in, but a session exists
+                            // ********* so they can choose to log in
+                            <React.Fragment>
+                                <div
+                                    className={classNames(
+                                        styles.menuBarItem,
+                                        styles.hoverable
+                                    )}
+                                    key="join"
+                                    onMouseUp={this.props.onOpenRegistration}
+                                >
+                                    <FormattedMessage
+                                        defaultMessage="Join Scratch"
+                                        description="Link for creating a Scratch account"
+                                        id="gui.menuBar.joinScratch"
+                                    />
+                                </div>
+                                <div
+                                    className={classNames(
+                                        styles.menuBarItem,
+                                        styles.hoverable
+                                    )}
+                                    key="login"
+                                    onMouseUp={this.props.onClickLogin}
+                                >
+                                    <FormattedMessage
+                                        defaultMessage="Sign in"
+                                        description="Link for signing in to your Scratch account"
+                                        id="gui.menuBar.signIn"
+                                    />
+                                    <LoginDropdown
+                                        className={classNames(styles.menuBarMenu)}
+                                        isOpen={this.props.loginMenuOpen}
+                                        isRtl={this.props.isRtl}
+                                        renderLogin={this.props.renderLogin}
+                                        onClose={this.props.onRequestCloseLogin}
+                                    />
+                                </div>
+                            </React.Fragment>
+                        )
+                    ) : (
+                        // ******** no login session is available, so don't show login stuff
+                        <React.Fragment>
+                            <div className={classNames(styles.menuBarItem, styles.feedbackButtonWrapper)}>
+                                <a
+                                    className={styles.feedbackLink}
+                                    href="https://scratch.mit.edu/discuss/topic/312261/"
+                                    rel="noopener noreferrer"
+                                    target="_blank"
+                                >
+                                    <Button
+                                        className={styles.feedbackButton}
+                                        iconSrc={feedbackIcon}
+                                    >
+                                        <FormattedMessage
+                                            defaultMessage="Give Feedback"
+                                            description="Label for feedback form modal button"
+                                            id="gui.menuBar.giveFeedback"
+                                        />
+                                    </Button>
+                                </a>
+                            </div>
+                            <MenuBarItemTooltip id="mystuff">
+                                <div
+                                    className={classNames(
+                                        styles.menuBarItem,
+                                        styles.hoverable,
+                                        styles.mystuffButton
+                                    )}
+                                >
+                                    <img
+                                        className={styles.mystuffIcon}
+                                        src={mystuffIcon}
+                                    />
+                                </div>
+                            </MenuBarItemTooltip>
+                            <MenuBarItemTooltip
+                                id="account-nav"
+                                place={this.props.isRtl ? 'right' : 'left'}
+                            >
+                                <div
+                                    className={classNames(
+                                        styles.menuBarItem,
+                                        styles.hoverable,
+                                        styles.accountNavMenu
+                                    )}
+                                >
+                                    <img
+                                        className={styles.profileIcon}
+                                        src={profileIcon}
+                                    />
+                                    <span>
+                                        {'scratch-cat'}
+                                    </span>
+                                    <img
+                                        className={styles.dropdownCaretIcon}
+                                        src={dropdownCaret}
+                                    />
+                                </div>
+                            </MenuBarItemTooltip>
+                        </React.Fragment>
+                    )}
                 </div>
             </Box>
         );
@@ -510,6 +586,7 @@ class MenuBar extends React.Component {
 }
 
 MenuBar.propTypes = {
+    accountMenuOpen: PropTypes.bool,
     canUpdateProject: PropTypes.bool,
     editMenuOpen: PropTypes.bool,
     enableCommunity: PropTypes.bool,
@@ -517,33 +594,53 @@ MenuBar.propTypes = {
     intl: intlShape,
     isRtl: PropTypes.bool,
     languageMenuOpen: PropTypes.bool,
+    loginMenuOpen: PropTypes.bool,
+    onClickAccount: PropTypes.func,
     onClickEdit: PropTypes.func,
     onClickFile: PropTypes.func,
     onClickLanguage: PropTypes.func,
+    onClickLogin: PropTypes.func,
+    onLogOut: PropTypes.func,
+    onOpenRegistration: PropTypes.func,
     onOpenTipLibrary: PropTypes.func,
+    onRequestCloseAccount: PropTypes.func,
     onRequestCloseEdit: PropTypes.func,
     onRequestCloseFile: PropTypes.func,
     onRequestCloseLanguage: PropTypes.func,
+    onRequestCloseLogin: PropTypes.func,
     onSeeCommunity: PropTypes.func,
-    onUpdateProjectTitle: PropTypes.func
+    onToggleLoginOpen: PropTypes.func,
+    onUpdateProjectTitle: PropTypes.func,
+    renderLogin: PropTypes.func,
+    sessionExists: PropTypes.bool,
+    username: PropTypes.string
 };
 
 const mapStateToProps = state => ({
     canUpdateProject: typeof (state.session && state.session.session && state.session.session.user) !== 'undefined',
+    accountMenuOpen: accountMenuOpen(state),
     fileMenuOpen: fileMenuOpen(state),
     editMenuOpen: editMenuOpen(state),
     isRtl: state.locales.isRtl,
-    languageMenuOpen: languageMenuOpen(state)
+    languageMenuOpen: languageMenuOpen(state),
+    loginMenuOpen: loginMenuOpen(state),
+    sessionExists: state.session && typeof state.session.session !== 'undefined',
+    username: state.session && state.session.session && state.session.session.user ?
+        state.session.session.user.username : null
 });
 
 const mapDispatchToProps = dispatch => ({
     onOpenTipLibrary: () => dispatch(openTipsLibrary()),
+    onClickAccount: () => dispatch(openAccountMenu()),
+    onRequestCloseAccount: () => dispatch(closeAccountMenu()),
     onClickFile: () => dispatch(openFileMenu()),
     onRequestCloseFile: () => dispatch(closeFileMenu()),
     onClickEdit: () => dispatch(openEditMenu()),
     onRequestCloseEdit: () => dispatch(closeEditMenu()),
     onClickLanguage: () => dispatch(openLanguageMenu()),
     onRequestCloseLanguage: () => dispatch(closeLanguageMenu()),
+    onClickLogin: () => dispatch(openLoginMenu()),
+    onRequestCloseLogin: () => dispatch(closeLoginMenu()),
     onSeeCommunity: () => dispatch(setPlayer(true))
 });
 
diff --git a/src/components/menu/menu.jsx b/src/components/menu/menu.jsx
index ce443db55cd0bc63a5a867b97ebd6c0760967cc8..4e6a6b229170d5d8f0a7844c3fa58ff3425972bc 100644
--- a/src/components/menu/menu.jsx
+++ b/src/components/menu/menu.jsx
@@ -32,6 +32,7 @@ MenuComponent.propTypes = {
     place: PropTypes.oneOf(['left', 'right'])
 };
 
+
 const MenuItem = ({
     children,
     className,
@@ -51,6 +52,7 @@ MenuItem.propTypes = {
     onClick: PropTypes.func
 };
 
+
 const addDividerClassToFirstChild = (child, id) => (
     React.cloneElement(child, {
         className: classNames(child.className, {
diff --git a/src/components/mic-indicator/mic-indicator.css b/src/components/mic-indicator/mic-indicator.css
new file mode 100644
index 0000000000000000000000000000000000000000..9fb3970297b19fbc9c4db523bc9bbe42674ed682
--- /dev/null
+++ b/src/components/mic-indicator/mic-indicator.css
@@ -0,0 +1,10 @@
+@keyframes popIn {
+    from {transform: scale(0.5)}
+    to {transform: scale(1)}
+}
+
+.mic-img {
+    margin: 10px;
+    transform-origin: center;
+    animation: popIn 0.1s ease-in-out;
+}
diff --git a/src/components/mic-indicator/mic-indicator.jsx b/src/components/mic-indicator/mic-indicator.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..fe0eb602b531e5c33de0cd2a696ab5a5dc907962
--- /dev/null
+++ b/src/components/mic-indicator/mic-indicator.jsx
@@ -0,0 +1,29 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import styles from './mic-indicator.css';
+import micIcon from './mic-indicator.svg';
+import {stageSizeToTransform} from '../../lib/screen-utils';
+
+const MicIndicatorComponent = props => (
+    <div
+        className={props.className}
+        style={stageSizeToTransform(props.stageSize)}
+    >
+        <img
+            className={styles.micImg}
+            src={micIcon}
+        />
+    </div>
+);
+
+MicIndicatorComponent.propTypes = {
+    className: PropTypes.string,
+    stageSize: PropTypes.shape({
+        width: PropTypes.number,
+        height: PropTypes.number,
+        widthDefault: PropTypes.number,
+        heightDefault: PropTypes.number
+    }).isRequired
+};
+
+export default MicIndicatorComponent;
diff --git a/src/components/mic-indicator/mic-indicator.svg b/src/components/mic-indicator/mic-indicator.svg
new file mode 100644
index 0000000000000000000000000000000000000000..78726389d13b811a8b7f31bbfa32a98df118a932
Binary files /dev/null and b/src/components/mic-indicator/mic-indicator.svg differ
diff --git a/src/components/monitor-list/monitor-list.jsx b/src/components/monitor-list/monitor-list.jsx
index 5cb11424113808e07f9c8573a9b2626ec3a838d5..f7b02080fdf71aeadbf673bf53a9fa23990d6760 100644
--- a/src/components/monitor-list/monitor-list.jsx
+++ b/src/components/monitor-list/monitor-list.jsx
@@ -4,20 +4,10 @@ import Box from '../box/box.jsx';
 import Monitor from '../../containers/monitor.jsx';
 import PropTypes from 'prop-types';
 import {OrderedMap} from 'immutable';
+import {stageSizeToTransform} from '../../lib/screen-utils';
 
 import styles from './monitor-list.css';
 
-const stageSizeToTransform = ({width, height, widthDefault, heightDefault}) => {
-    const scaleX = width / widthDefault;
-    const scaleY = height / heightDefault;
-    if (scaleX === 1 && scaleY === 1) {
-        // Do not set a transform if the scale is 1 because
-        // it messes up `position: fixed` elements like the context menu.
-        return;
-    }
-    return {transform: `scale(${scaleX},${scaleY})`};
-};
-
 const MonitorList = props => (
     <Box
         // Use static `monitor-overlay` class for bounds of draggables
diff --git a/src/components/question/question.css b/src/components/question/question.css
index 4550df75cdbe1b3ad51b2e5c45e54f47d1de7627..a0f8116ed1a1d9c85957d1167ee792f1ee8515f4 100644
--- a/src/components/question/question.css
+++ b/src/components/question/question.css
@@ -1,13 +1,6 @@
 @import "../../css/units.css";
 @import "../../css/colors.css";
 
-.question-wrapper {
-    position: absolute;
-    bottom: 0;
-    left: 0;
-    width: 100%;
-}
-
 .question-container {
     margin: $space;
     border: 1px solid $ui-black-transparent;
diff --git a/src/components/question/question.jsx b/src/components/question/question.jsx
index c57fb085d37de1f42a75f3a709c05d0f313f76ac..373f7cb919cd86fdbab96665457fdec29cb5fb4e 100644
--- a/src/components/question/question.jsx
+++ b/src/components/question/question.jsx
@@ -7,13 +7,14 @@ import enterIcon from './icon--enter.svg';
 const QuestionComponent = props => {
     const {
         answer,
+        className,
         question,
         onChange,
         onClick,
         onKeyPress
     } = props;
     return (
-        <div className={styles.questionWrapper}>
+        <div className={className}>
             <div className={styles.questionContainer}>
                 {question ? (
                     <div className={styles.questionLabel}>{question}</div>
@@ -43,6 +44,7 @@ const QuestionComponent = props => {
 
 QuestionComponent.propTypes = {
     answer: PropTypes.string,
+    className: PropTypes.string,
     onChange: PropTypes.func.isRequired,
     onClick: PropTypes.func.isRequired,
     onKeyPress: PropTypes.func.isRequired,
diff --git a/src/components/stage/stage.css b/src/components/stage/stage.css
index 7ce4d55d9f6a953541564e6e12b51fc1fed9be78..98a0c42a715d8aace5fc8294dfc5d9f895226dd2 100644
--- a/src/components/stage/stage.css
+++ b/src/components/stage/stage.css
@@ -74,10 +74,6 @@
     border: none;
 }
 
-.question-wrapper {
-    position: absolute;
-}
-
 /* adjust monitors when stage is standard size:
 shift them down and right to compensate for the stage's border */
 .stage-wrapper .monitor-wrapper {
@@ -93,7 +89,7 @@ to adjust for the border using a different method */
     padding-bottom: calc($stage-full-screen-stage-padding + $stage-full-screen-border-width);
 }
 
-.monitor-wrapper, .color-picker-wrapper, .queston-wrapper {
+.monitor-wrapper, .color-picker-wrapper {
     position: absolute;
     top: 0;
     left: 0;
@@ -110,3 +106,24 @@ to adjust for the border using a different method */
     z-index: $z-index-dragging-sprite;
     filter: drop-shadow(5px 5px 5px $ui-black-transparent);
  }
+
+.stage-bottom-wrapper {
+    position: absolute;
+    display: flex;
+    flex-direction: column;
+    justify-content: flex-end;
+    top: 0;
+    overflow: hidden;
+    pointer-events: none;
+}
+
+.mic-indicator {
+    transform-origin: bottom right;
+    z-index: $z-index-stage-indicator;
+    pointer-events: none;
+    align-self: flex-end;
+}
+
+.question-wrapper {
+    pointer-events: auto;
+}
diff --git a/src/components/stage/stage.jsx b/src/components/stage/stage.jsx
index d8c0d07749bec8eb360820988b5e360318513e71..9e1f3454dd1686be6e745dd5fb8035ec97b16b41 100644
--- a/src/components/stage/stage.jsx
+++ b/src/components/stage/stage.jsx
@@ -7,6 +7,7 @@ import DOMElementRenderer from '../../containers/dom-element-renderer.jsx';
 import Loupe from '../loupe/loupe.jsx';
 import MonitorList from '../../containers/monitor-list.jsx';
 import Question from '../../containers/question.jsx';
+import MicIndicator from '../mic-indicator/mic-indicator.jsx';
 import {STAGE_DISPLAY_SIZES} from '../../lib/layout-constants.js';
 import {getStageDimensions} from '../../lib/screen-utils.js';
 import styles from './stage.css';
@@ -18,6 +19,7 @@ const StageComponent = props => {
         isColorPicking,
         isFullScreen,
         colorInfo,
+        micIndicator,
         question,
         stageSize,
         useEditorDragStyle,
@@ -66,13 +68,22 @@ const StageComponent = props => {
                         <Loupe colorInfo={colorInfo} />
                     </Box>
                 ) : null}
-                {question === null ? null : (
-                    <div
-                        className={classNames(
-                            styles.stageOverlayContent,
-                            styles.stageOverlayContentBorderOverride
-                        )}
-                    >
+                <div
+                    className={styles.stageBottomWrapper}
+                    style={{
+                        width: stageDimensions.width,
+                        height: stageDimensions.height,
+                        left: '50%',
+                        marginLeft: stageDimensions.width * -0.5
+                    }}
+                >
+                    {micIndicator ? (
+                        <MicIndicator
+                            className={styles.micIndicator}
+                            stageSize={stageDimensions}
+                        />
+                    ) : null}
+                    {question === null ? null : (
                         <div
                             className={styles.questionWrapper}
                             style={{width: stageDimensions.width}}
@@ -82,8 +93,8 @@ const StageComponent = props => {
                                 onQuestionAnswered={onQuestionAnswered}
                             />
                         </div>
-                    </div>
-                )}
+                    )}
+                </div>
                 <canvas
                     className={styles.draggingSprite}
                     height={0}
diff --git a/src/components/tag-button/tag-button.jsx b/src/components/tag-button/tag-button.jsx
index 5e1318f90d993ea74d3ac59faf5719a522fe7ef6..e36a2ed19867de20a09cd6d98580b43b876b13c0 100644
--- a/src/components/tag-button/tag-button.jsx
+++ b/src/components/tag-button/tag-button.jsx
@@ -1,6 +1,7 @@
 import classNames from 'classnames';
 import PropTypes from 'prop-types';
 import React from 'react';
+import {FormattedMessage} from 'react-intl';
 
 import Button from '../button/button.jsx';
 
@@ -10,7 +11,8 @@ const TagButtonComponent = ({
     active,
     iconClassName,
     className,
-    title,
+    tag, // eslint-disable-line no-unused-vars
+    intlLabel,
     ...props
 }) => (
     <Button
@@ -26,17 +28,19 @@ const TagButtonComponent = ({
         )}
         {...props}
     >
-        {title}
+        <FormattedMessage {...intlLabel} />
     </Button>
 );
 
 TagButtonComponent.propTypes = {
     ...Button.propTypes,
     active: PropTypes.bool,
-    title: PropTypes.oneOfType([
-        PropTypes.string,
-        PropTypes.object // FormattedMessage
-    ]).isRequired
+    intlLabel: PropTypes.shape({
+        defaultMessage: PropTypes.string,
+        description: PropTypes.string,
+        id: PropTypes.string
+    }).isRequired,
+    tag: PropTypes.string.isRequired
 };
 
 TagButtonComponent.defaultProps = {
diff --git a/src/containers/account-nav.jsx b/src/containers/account-nav.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..d0ce3b0628b078612bb83b368a5bcd93f9d3eb1f
--- /dev/null
+++ b/src/containers/account-nav.jsx
@@ -0,0 +1,53 @@
+/*
+NOTE: this file only temporarily resides in scratch-gui.
+Nearly identical code appears in scratch-www, and the two should
+eventually be consolidated.
+*/
+
+import {injectIntl} from 'react-intl';
+import PropTypes from 'prop-types';
+import React from 'react';
+import {connect} from 'react-redux';
+
+import AccountNavComponent from '../components/menu-bar/account-nav.jsx';
+
+const AccountNav = function (props) {
+    const {
+        ...componentProps
+    } = props;
+    return (
+        <AccountNavComponent
+            {...componentProps}
+        />
+    );
+};
+
+AccountNav.propTypes = {
+    classroomId: PropTypes.string,
+    isEducator: PropTypes.bool,
+    isRtl: PropTypes.bool,
+    isStudent: PropTypes.bool,
+    profileUrl: PropTypes.string,
+    thumbnailUrl: PropTypes.string,
+    username: PropTypes.string
+};
+
+const mapStateToProps = state => ({
+    classroomId: state.session && state.session.session && state.session.session.user ?
+        state.session.session.user.classroomId : '',
+    isEducator: state.session && state.session.permissions && state.session.permissions.educator,
+    isStudent: state.session && state.session.permissions && state.session.permissions.student,
+    profileUrl: state.session && state.session.session && state.session.session.user ?
+        `/users/${state.session.session.user.username}` : '',
+    thumbnailUrl: state.session && state.session.session && state.session.session.user ?
+        state.session.session.user.thumbnailUrl : null,
+    username: state.session && state.session.session && state.session.session.user ?
+        state.session.session.user.username : ''
+});
+
+const mapDispatchToProps = () => ({});
+
+export default injectIntl(connect(
+    mapStateToProps,
+    mapDispatchToProps
+)(AccountNav));
diff --git a/src/containers/alert.jsx b/src/containers/alert.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..90f2505dc16d8c4d62354ca83fd9cdebcb044ddb
--- /dev/null
+++ b/src/containers/alert.jsx
@@ -0,0 +1,35 @@
+import React from 'react';
+import bindAll from 'lodash.bindall';
+import PropTypes from 'prop-types';
+
+import AlertComponent from '../components/alerts/alert.jsx';
+
+class Alert extends React.Component {
+    constructor (props) {
+        super(props);
+        bindAll(this, [
+            'handleOnCloseAlert'
+        ]);
+    }
+    handleOnCloseAlert () {
+        this.props.onCloseAlert(this.props.index);
+    }
+    render () {
+        return (
+            <AlertComponent
+                iconURL={this.props.iconURL}
+                message={this.props.message}
+                onCloseAlert={this.handleOnCloseAlert}
+            />
+        );
+    }
+}
+
+Alert.propTypes = {
+    iconURL: PropTypes.string,
+    index: PropTypes.number,
+    message: PropTypes.string,
+    onCloseAlert: PropTypes.func.isRequired
+};
+
+export default Alert;
diff --git a/src/containers/alerts.jsx b/src/containers/alerts.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..c6d7e668dee8f44f0dd878deadc43300941fa9c9
--- /dev/null
+++ b/src/containers/alerts.jsx
@@ -0,0 +1,51 @@
+import classNames from 'classnames';
+import React from 'react';
+import PropTypes from 'prop-types';
+import {connect} from 'react-redux';
+
+import {
+    closeAlert
+} from '../reducers/alerts';
+
+import Box from '../components/box/box.jsx';
+import Alert from '../containers/alert.jsx';
+
+const Alerts = ({
+    alertsList,
+    className,
+    onCloseAlert
+}) => (
+    <Box
+        bounds="parent"
+        className={classNames(className)}
+    >
+        {alertsList.map((a, index) => (
+            <Alert
+                iconURL={a.iconURL}
+                index={index}
+                key={index}
+                message={a.message}
+                onCloseAlert={onCloseAlert}
+            />
+        ))}
+    </Box>
+);
+
+Alerts.propTypes = {
+    alertsList: PropTypes.arrayOf(PropTypes.object),
+    className: PropTypes.string,
+    onCloseAlert: PropTypes.func
+};
+
+const mapStateToProps = state => ({
+    alertsList: state.scratchGui.alerts.alertsList
+});
+
+const mapDispatchToProps = dispatch => ({
+    onCloseAlert: index => dispatch(closeAlert(index))
+});
+
+export default connect(
+    mapStateToProps,
+    mapDispatchToProps
+)(Alerts);
diff --git a/src/containers/blocks.jsx b/src/containers/blocks.jsx
index 499d77636116b287d0acb3de60c939a0d31f0bd3..bf759bb1b89eade4eb1f4b01446faeecfe075c81 100644
--- a/src/containers/blocks.jsx
+++ b/src/containers/blocks.jsx
@@ -8,6 +8,7 @@ import VMScratchBlocks from '../lib/blocks';
 import VM from 'scratch-vm';
 
 import analytics from '../lib/analytics';
+import log from '../lib/log.js';
 import Prompt from './prompt.jsx';
 import ConnectionModal from './connection-modal.jsx';
 import BlocksComponent from '../components/blocks/blocks.jsx';
@@ -300,7 +301,21 @@ class Blocks extends React.Component {
         // Remove and reattach the workspace listener (but allow flyout events)
         this.workspace.removeChangeListener(this.props.vm.blockListener);
         const dom = this.ScratchBlocks.Xml.textToDom(data.xml);
-        this.ScratchBlocks.Xml.clearWorkspaceAndLoadFromXml(dom, this.workspace);
+        try {
+            this.ScratchBlocks.Xml.clearWorkspaceAndLoadFromXml(dom, this.workspace);
+        } catch (error) {
+            // The workspace is likely incomplete. What did update should be
+            // functional.
+            //
+            // Instead of throwing the error, by logging it and continuing as
+            // normal lets the other workspace update processes complete in the
+            // gui and vm, which lets the vm run even if the workspace is
+            // incomplete. Throwing the error would keep things like setting the
+            // correct editing target from happening which can interfere with
+            // some blocks and processes in the vm.
+            error.message = `Workspace Update Error: ${error.message}`;
+            log.error(error);
+        }
         this.workspace.addChangeListener(this.props.vm.blockListener);
 
         if (this.props.vm.editingTarget && this.state.workspaceMetrics[this.props.vm.editingTarget.id]) {
diff --git a/src/containers/gui.jsx b/src/containers/gui.jsx
index 04c6142dce25e073c4f0a0676cd2ab3e5c37590d..6ef14439ba7c19b2f39301e15e3154fc59dd25b8 100644
--- a/src/containers/gui.jsx
+++ b/src/containers/gui.jsx
@@ -125,6 +125,7 @@ GUI.propTypes = {
 
 const mapStateToProps = (state, ownProps) => ({
     activeTabIndex: state.scratchGui.editorTab.activeTabIndex,
+    alertsVisible: state.scratchGui.alerts.visible,
     backdropLibraryVisible: state.scratchGui.modals.backdropLibrary,
     blocksTabVisible: state.scratchGui.editorTab.activeTabIndex === BLOCKS_TAB_INDEX,
     cardsVisible: state.scratchGui.cards.visible,
diff --git a/src/containers/language-selector.jsx b/src/containers/language-selector.jsx
index c3f108efbe3315dd732a3b450479efddc7bec00d..8eb621bb1b79b441627a49245245ab80549bf109 100644
--- a/src/containers/language-selector.jsx
+++ b/src/containers/language-selector.jsx
@@ -13,11 +13,13 @@ class LanguageSelector extends React.Component {
         bindAll(this, [
             'handleChange'
         ]);
+        document.documentElement.lang = props.currentLocale;
     }
     handleChange (e) {
         const newLocale = e.target.value;
         if (this.props.supportedLocales.includes(newLocale)) {
             this.props.onChangeLanguage(newLocale);
+            document.documentElement.lang = newLocale;
         }
     }
     render () {
diff --git a/src/containers/menu-item.jsx b/src/containers/menu-item.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..8755c19f7343db268ef10afd4976dfa4a14bffb9
--- /dev/null
+++ b/src/containers/menu-item.jsx
@@ -0,0 +1,43 @@
+import bindAll from 'lodash.bindall';
+import PropTypes from 'prop-types';
+import React from 'react';
+
+import {MenuItem as MenuItemComponent} from '../components/menu/menu.jsx';
+
+class MenuItem extends React.Component {
+    constructor (props) {
+        super(props);
+        bindAll(this, [
+            'navigateToHref'
+        ]);
+    }
+    navigateToHref () {
+        if (this.props.href) window.location.href = this.props.href;
+    }
+    render () {
+        const {
+            children,
+            className,
+            onClick
+        } = this.props;
+        const clickAction = onClick ? onClick : this.navigateToHref;
+        return (
+            <MenuItemComponent
+                className={className}
+                onClick={clickAction}
+            >
+                {children}
+            </MenuItemComponent>
+        );
+    }
+}
+
+MenuItem.propTypes = {
+    children: PropTypes.node,
+    className: PropTypes.string,
+    // can take an onClick prop, or take an href and build an onClick handler
+    href: PropTypes.string,
+    onClick: PropTypes.func
+};
+
+export default MenuItem;
diff --git a/src/containers/stage.jsx b/src/containers/stage.jsx
index 0aa3f2a4b64ef883e29cf0b47ff0a4f1d8d93cd3..dc2b87043956be682e4dcb68dc4e6a9657f9bcf3 100644
--- a/src/containers/stage.jsx
+++ b/src/containers/stage.jsx
@@ -75,7 +75,8 @@ class Stage extends React.Component {
             this.props.isColorPicking !== nextProps.isColorPicking ||
             this.state.colorInfo !== nextState.colorInfo ||
             this.props.isFullScreen !== nextProps.isFullScreen ||
-            this.state.question !== nextState.question;
+            this.state.question !== nextState.question ||
+            this.props.micIndicator !== nextProps.micIndicator;
     }
     componentDidUpdate (prevProps) {
         if (this.props.isColorPicking && !prevProps.isColorPicking) {
@@ -402,6 +403,7 @@ Stage.defaultProps = {
 const mapStateToProps = state => ({
     isColorPicking: state.scratchGui.colorPicker.active,
     isFullScreen: state.scratchGui.mode.isFullScreen,
+    micIndicator: state.scratchGui.micIndicator,
     // Do not use editor drag style in fullscreen or player mode.
     useEditorDragStyle: !(state.scratchGui.mode.isFullScreen || state.scratchGui.mode.isPlayerOnly)
 });
diff --git a/src/containers/tag-button.jsx b/src/containers/tag-button.jsx
index e5ad61f4080abbaced07fe2c6f7ab45bcca7f086..3e7dd84b1bdb105aab8588cd8b9a9ee85e799a91 100644
--- a/src/containers/tag-button.jsx
+++ b/src/containers/tag-button.jsx
@@ -12,7 +12,7 @@ class TagButton extends React.Component {
         ]);
     }
     handleClick () {
-        this.props.onClick(this.props.title);
+        this.props.onClick(this.props.tag);
     }
     render () {
         return (
@@ -26,11 +26,7 @@ class TagButton extends React.Component {
 
 TagButton.propTypes = {
     ...TagButtonComponent.propTypes,
-    onClick: PropTypes.func,
-    title: PropTypes.oneOfType([
-        PropTypes.string,
-        PropTypes.object
-    ]).isRequired
+    onClick: PropTypes.func
 };
 
 export default TagButton;
diff --git a/src/css/z-index.css b/src/css/z-index.css
index 26893f08fa5147930328992359d709777bef0ec7..06e2015658b563f88c84a901a35f36cad91c8fcb 100644
--- a/src/css/z-index.css
+++ b/src/css/z-index.css
@@ -8,6 +8,7 @@ $z-index-extension-button: 50; /* Force extension button above the ScratchBlocks
 $z-index-menu-bar: 50; /* blocklyToolboxDiv is 40 */
 
 $z-index-monitor: 100;
+$z-index-stage-indicator: 110;
 $z-index-add-button: 120;
 $z-index-tooltip: 130; /* tooltips should go over add buttons if they overlap */
 
@@ -23,6 +24,7 @@ $z-index-stage-color-picker-background: 2000;
 $z-index-stage-with-color-picker: 2010;
 $z-index-stage-header: 5000;
 $z-index-stage-wrapper-overlay: 5000;
+$z-index-alerts: 5010;
 
 /* in most interfaces, the context menu is always on top */
 $z-index-context-menu: 10000;
diff --git a/src/index.js b/src/index.js
index dfd32a399de9c132135e2cb0f0596bd8eecc74a2..237c1093d65b545b595a75574a19eae69ad5e57c 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,4 +1,5 @@
 import GUI from './containers/gui.jsx';
+import AppStateHOC from './lib/app-state-hoc.jsx';
 import GuiReducer, {guiInitialState, guiMiddleware, initFullScreen, initPlayer} from './reducers/gui';
 import LocalesReducer, {localesInitialState, initLocale} from './reducers/locales';
 import {ScratchPaintReducer} from 'scratch-paint';
@@ -13,6 +14,7 @@ const guiReducers = {
 
 export {
     GUI as default,
+    AppStateHOC,
     setAppElement,
     guiReducers,
     guiInitialState,
diff --git a/src/lib/libraries/backdrop-tags.js b/src/lib/libraries/backdrop-tags.js
index 7bafa3410aa3afe94692d0e4d495a9f8ecaeb256..77374719d2b20eebde64ea3d6b3dc40296818eb8 100644
--- a/src/lib/libraries/backdrop-tags.js
+++ b/src/lib/libraries/backdrop-tags.js
@@ -1,10 +1,11 @@
+import messages from './tag-messages.js';
 export default [
-    {title: 'Fantasy'},
-    {title: 'Music'},
-    {title: 'Sports'},
-    {title: 'Outdoors'},
-    {title: 'Indoors'},
-    {title: 'Space'},
-    {title: 'Underwater'},
-    {title: 'Patterns'}
+    {tag: 'fantasy', intlLabel: messages.fantasy},
+    {tag: 'music', intlLabel: messages.music},
+    {tag: 'sports', intlLabel: messages.sports},
+    {tag: 'outdoors', intlLabel: messages.outdoors},
+    {tag: 'indoors', intlLabel: messages.indoors},
+    {tag: 'space', intlLabel: messages.space},
+    {tag: 'underwater', intlLabel: messages.underwater},
+    {tag: 'patterns', intlLabel: messages.patterns}
 ];
diff --git a/src/lib/libraries/sound-tags.js b/src/lib/libraries/sound-tags.js
index 61a37002e6279ff0e24fe9a9208bb4549e9c686b..4cba1d57b34441b9ed7ab3f22575c964bc5356de 100644
--- a/src/lib/libraries/sound-tags.js
+++ b/src/lib/libraries/sound-tags.js
@@ -1,11 +1,12 @@
+import messages from './tag-messages.js';
 export default [
-    {title: 'Animals'},
-    {title: 'Effects'},
-    {title: 'Loops'},
-    {title: 'Notes'},
-    {title: 'Percussion'},
-    {title: 'Space'},
-    {title: 'Sports'},
-    {title: 'Voice'},
-    {title: 'Wacky'}
+    {tag: 'animals', intlLabel: messages.animals},
+    {tag: 'effects', intlLabel: messages.effects},
+    {tag: 'loops', intlLabel: messages.loops},
+    {tag: 'notes', intlLabel: messages.notes},
+    {tag: 'percussion', intlLabel: messages.percussion},
+    {tag: 'space', intlLabel: messages.space},
+    {tag: 'sports', intlLabel: messages.sports},
+    {tag: 'voice', intlLabel: messages.voice},
+    {tag: 'wacky', intlLabel: messages.wacky}
 ];
diff --git a/src/lib/libraries/sprite-tags.js b/src/lib/libraries/sprite-tags.js
index f1fbe3d5647910980f1133dd57aa05569ee05334..8a12e1e41e54fbbee6945361b429435c07b67dad 100644
--- a/src/lib/libraries/sprite-tags.js
+++ b/src/lib/libraries/sprite-tags.js
@@ -1,10 +1,11 @@
+import messages from './tag-messages.js';
 export default [
-    {title: 'Animals'},
-    {title: 'People'},
-    {title: 'Fantasy'},
-    {title: 'Dance'},
-    {title: 'Music'},
-    {title: 'Sports'},
-    {title: 'Food'},
-    {title: 'Fashion'}
+    {tag: 'animals', intlLabel: messages.animals},
+    {tag: 'people', intlLabel: messages.people},
+    {tag: 'fantasy', intlLabel: messages.fantasy},
+    {tag: 'dance', intlLabel: messages.dance},
+    {tag: 'music', intlLabel: messages.music},
+    {tag: 'sports', intlLabel: messages.sports},
+    {tag: 'food', intlLabel: messages.food},
+    {tag: 'fashion', intlLabel: messages.fashion}
 ];
diff --git a/src/lib/libraries/tag-messages.js b/src/lib/libraries/tag-messages.js
new file mode 100644
index 0000000000000000000000000000000000000000..60a6d13fc504d8745205db7be798c8ffd4f526b3
--- /dev/null
+++ b/src/lib/libraries/tag-messages.js
@@ -0,0 +1,104 @@
+import {defineMessages} from 'react-intl';
+
+export default defineMessages({
+    all: {
+        defaultMessage: 'All',
+        description: 'Tag for filtering a library for everything',
+        id: 'gui.libraryTags.all'
+    },
+    animals: {
+        defaultMessage: 'Animals',
+        description: 'Tag for filtering a library for animals',
+        id: 'gui.libraryTags.animals'
+    },
+    dance: {
+        defaultMessage: 'Dance',
+        description: 'Tag for filtering a library for dance',
+        id: 'gui.libraryTags.dance'
+    },
+    effects: {
+        defaultMessage: 'Effects',
+        description: 'Tag for filtering a library for effects',
+        id: 'gui.libraryTags.effects'
+    },
+    fantasy: {
+        defaultMessage: 'Fantasy',
+        description: 'Tag for filtering a library for fantasy',
+        id: 'gui.libraryTags.fantasy'
+    },
+    fashion: {
+        defaultMessage: 'Fashion',
+        description: 'Tag for filtering a library for fashion',
+        id: 'gui.libraryTags.fashion'
+    },
+    food: {
+        defaultMessage: 'Food',
+        description: 'Tag for filtering a library for food',
+        id: 'gui.libraryTags.food'
+    },
+    indoors: {
+        defaultMessage: 'Indoors',
+        description: 'Tag for filtering a library for indoors',
+        id: 'gui.libraryTags.indoors'
+    },
+    loops: {
+        defaultMessage: 'Loops',
+        description: 'Tag for filtering a library for loops',
+        id: 'gui.libraryTags.loops'
+    },
+    music: {
+        defaultMessage: 'Music',
+        description: 'Tag for filtering a library for music',
+        id: 'gui.libraryTags.music'
+    },
+    notes: {
+        defaultMessage: 'Notes',
+        description: 'Tag for filtering a library for notes',
+        id: 'gui.libraryTags.notes'
+    },
+    outdoors: {
+        defaultMessage: 'Outdoors',
+        description: 'Tag for filtering a library for outdoors',
+        id: 'gui.libraryTags.outdoors'
+    },
+    patterns: {
+        defaultMessage: 'Patterns',
+        description: 'Tag for filtering a library for patterns',
+        id: 'gui.libraryTags.patterns'
+    },
+    people: {
+        defaultMessage: 'People',
+        description: 'Tag for filtering a library for people',
+        id: 'gui.libraryTags.people'
+    },
+    percussion: {
+        defaultMessage: 'Percussion',
+        description: 'Tag for filtering a library for percussion',
+        id: 'gui.libraryTags.percussion'
+    },
+    space: {
+        defaultMessage: 'Space',
+        description: 'Tag for filtering a library for space',
+        id: 'gui.libraryTags.space'
+    },
+    sports: {
+        defaultMessage: 'Sports',
+        description: 'Tag for filtering a library for sports',
+        id: 'gui.libraryTags.sports'
+    },
+    underwater: {
+        defaultMessage: 'Underwater',
+        description: 'Tag for filtering a library for underwater',
+        id: 'gui.libraryTags.underwater'
+    },
+    voice: {
+        defaultMessage: 'Voice',
+        description: 'Tag for filtering a library for voice',
+        id: 'gui.libraryTags.voice'
+    },
+    wacky: {
+        defaultMessage: 'Wacky',
+        description: 'Tag for filtering a library for wacky',
+        id: 'gui.libraryTags.wacky'
+    }
+});
diff --git a/src/lib/screen-utils.js b/src/lib/screen-utils.js
index afef90d84c845119d881c11eef4e27c4f3a0b2ae..d612017841291b990e3fb1fa6af539f99afee1f5 100644
--- a/src/lib/screen-utils.js
+++ b/src/lib/screen-utils.js
@@ -72,7 +72,29 @@ const getStageDimensions = (stageSize, isFullScreen) => {
     return stageDimensions;
 };
 
+/**
+ * Take a pair of sizes for the stage (a target height and width and a default height and width),
+ * calculate the ratio between them, and return a CSS transform to scale to that ratio.
+ * @param {object} sizeInfo An object containing dimensions of the target and default stage sizes.
+ * @param {number} sizeInfo.width The target width
+ * @param {number} sizeInfo.height The target height
+ * @param {number} sizeInfo.widthDefault The default width
+ * @param {number} sizeInfo.heightDefault The default height
+ * @returns {object} the CSS transform
+ */
+const stageSizeToTransform = ({width, height, widthDefault, heightDefault}) => {
+    const scaleX = width / widthDefault;
+    const scaleY = height / heightDefault;
+    if (scaleX === 1 && scaleY === 1) {
+        // Do not set a transform if the scale is 1 because
+        // it messes up `position: fixed` elements like the context menu.
+        return;
+    }
+    return {transform: `scale(${scaleX},${scaleY})`};
+};
+
 export {
     getStageDimensions,
-    resolveStageSize
+    resolveStageSize,
+    stageSizeToTransform
 };
diff --git a/src/lib/vm-listener-hoc.jsx b/src/lib/vm-listener-hoc.jsx
index 67e70f90019e90b92c262fdef5067c12c9c927ff..6b8d1777792c8e68790a6f5b5d0859e21952088c 100644
--- a/src/lib/vm-listener-hoc.jsx
+++ b/src/lib/vm-listener-hoc.jsx
@@ -9,6 +9,8 @@ import {updateTargets} from '../reducers/targets';
 import {updateBlockDrag} from '../reducers/block-drag';
 import {updateMonitors} from '../reducers/monitors';
 import {setRunningState, setTurboState} from '../reducers/vm-status';
+import {showAlert} from '../reducers/alerts';
+import {updateMicIndicator} from '../reducers/mic-indicator';
 
 /*
  * Higher Order Component to manage events emitted by the VM
@@ -36,6 +38,9 @@ const vmListenerHOC = function (WrappedComponent) {
             this.props.vm.on('TURBO_MODE_OFF', this.props.onTurboModeOff);
             this.props.vm.on('PROJECT_RUN_START', this.props.onProjectRunStart);
             this.props.vm.on('PROJECT_RUN_STOP', this.props.onProjectRunStop);
+            this.props.vm.on('PERIPHERAL_ERROR', this.props.onShowAlert);
+            this.props.vm.on('MIC_LISTENING', this.props.onMicListeningUpdate);
+
         }
         componentDidMount () {
             if (this.props.attachKeyboardEvents) {
@@ -87,12 +92,14 @@ const vmListenerHOC = function (WrappedComponent) {
                 onBlockDragUpdate,
                 onKeyDown,
                 onKeyUp,
+                onMicListeningUpdate,
                 onMonitorsUpdate,
                 onTargetsUpdate,
                 onProjectRunStart,
                 onProjectRunStop,
                 onTurboModeOff,
                 onTurboModeOn,
+                onShowAlert,
                 /* eslint-enable no-unused-vars */
                 ...props
             } = this.props;
@@ -104,9 +111,11 @@ const vmListenerHOC = function (WrappedComponent) {
         onBlockDragUpdate: PropTypes.func.isRequired,
         onKeyDown: PropTypes.func,
         onKeyUp: PropTypes.func,
+        onMicListeningUpdate: PropTypes.func.isRequired,
         onMonitorsUpdate: PropTypes.func.isRequired,
         onProjectRunStart: PropTypes.func.isRequired,
         onProjectRunStop: PropTypes.func.isRequired,
+        onShowAlert: PropTypes.func.isRequired,
         onTargetsUpdate: PropTypes.func.isRequired,
         onTurboModeOff: PropTypes.func.isRequired,
         onTurboModeOn: PropTypes.func.isRequired,
@@ -118,8 +127,8 @@ const vmListenerHOC = function (WrappedComponent) {
     };
     const mapStateToProps = state => ({
         vm: state.scratchGui.vm,
-        username: state.session && state.session.session ?
-            state.session.session.username : ''
+        username: state.session && state.session.session && state.session.session.user ?
+            state.session.session.user.username : ''
     });
     const mapDispatchToProps = dispatch => ({
         onTargetsUpdate: data => {
@@ -134,7 +143,13 @@ const vmListenerHOC = function (WrappedComponent) {
         onProjectRunStart: () => dispatch(setRunningState(true)),
         onProjectRunStop: () => dispatch(setRunningState(false)),
         onTurboModeOn: () => dispatch(setTurboState(true)),
-        onTurboModeOff: () => dispatch(setTurboState(false))
+        onTurboModeOff: () => dispatch(setTurboState(false)),
+        onShowAlert: data => {
+            dispatch(showAlert(data));
+        },
+        onMicListeningUpdate: listening => {
+            dispatch(updateMicIndicator(listening));
+        }
     });
     return connect(
         mapStateToProps,
diff --git a/src/reducers/alerts.js b/src/reducers/alerts.js
new file mode 100644
index 0000000000000000000000000000000000000000..e0fdd72a968e5ff0fc5c7f5459da22a41017f254
--- /dev/null
+++ b/src/reducers/alerts.js
@@ -0,0 +1,77 @@
+import extensionData from '../lib/libraries/extensions/index.jsx';
+
+const CLOSE_ALERT = 'scratch-gui/alerts/CLOSE_ALERT';
+const SHOW_ALERT = 'scratch-gui/alerts/SHOW_ALERT';
+
+const initialState = {
+    visible: true,
+    alertsList: []
+};
+
+const reducer = function (state, action) {
+    if (typeof state === 'undefined') state = initialState;
+    switch (action.type) {
+    case SHOW_ALERT: {
+        const newList = state.alertsList.slice();
+        const newAlert = {message: action.data.message};
+        const extensionId = action.data.extensionId;
+        if (extensionId) { // if it's an extension
+            const extension = extensionData.find(ext => ext.extensionId === extensionId);
+            if (extension && extension.name) {
+                // TODO: is this the right place to assemble this message?
+                newAlert.message = `${newAlert.message} ${extension.name}.`;
+            }
+            if (extension && extension.smallPeripheralImage) {
+                newAlert.iconURL = extension.smallPeripheralImage;
+            }
+        }
+        // TODO: add cases for other kinds of alerts here?
+        newList.push(newAlert);
+        return Object.assign({}, state, {
+            alertsList: newList
+        });
+    }
+    case CLOSE_ALERT: {
+        const newList = state.alertsList.slice();
+        newList.splice(action.index, 1);
+        return Object.assign({}, state, {
+            alertsList: newList
+        });
+    }
+    default:
+        return state;
+    }
+};
+
+/**
+ * Function to close an alert with the given index.
+ *
+ * @param {object} index - the index of the alert to close.
+ * @return {object} - an object to be passed to the reducer.
+ */
+const closeAlert = function (index) {
+    return {
+        type: CLOSE_ALERT,
+        index
+    };
+};
+
+/**
+ * Function to show an alert with the given input data.
+ *
+ * @param {object} data - data with the following props: {message, extensionId=null}
+ * @return {object} - an object to be passed to the reducer.
+ */
+const showAlert = function (data) {
+    return {
+        type: SHOW_ALERT,
+        data
+    };
+};
+
+export {
+    reducer as default,
+    initialState as alertsInitialState,
+    closeAlert,
+    showAlert
+};
diff --git a/src/reducers/gui.js b/src/reducers/gui.js
index 8869c1a35031e1d46b8dd28f4f408d2500946840..bce112a1c327c6e6a2761cbbeb6e63f29dbf87da 100644
--- a/src/reducers/gui.js
+++ b/src/reducers/gui.js
@@ -1,4 +1,5 @@
 import {applyMiddleware, compose, combineReducers} from 'redux';
+import alertsReducer, {alertsInitialState} from './alerts';
 import assetDragReducer, {assetDragInitialState} from './asset-drag';
 import cardsReducer, {cardsInitialState} from './cards';
 import colorPickerReducer, {colorPickerInitialState} from './color-picker';
@@ -7,6 +8,7 @@ import blockDragReducer, {blockDragInitialState} from './block-drag';
 import editorTabReducer, {editorTabInitialState} from './editor-tab';
 import hoveredTargetReducer, {hoveredTargetInitialState} from './hovered-target';
 import menuReducer, {menuInitialState} from './menus';
+import micIndicatorReducer, {micIndicatorInitialState} from './mic-indicator';
 import modalReducer, {modalsInitialState} from './modals';
 import modeReducer, {modeInitialState} from './mode';
 import monitorReducer, {monitorsInitialState} from './monitors';
@@ -26,6 +28,7 @@ import decks from '../lib/libraries/decks/index.jsx';
 const guiMiddleware = compose(applyMiddleware(throttle(300, {leading: true, trailing: true})));
 
 const guiInitialState = {
+    alerts: alertsInitialState,
     assetDrag: assetDragInitialState,
     blockDrag: blockDragInitialState,
     cards: cardsInitialState,
@@ -36,6 +39,7 @@ const guiInitialState = {
     hoveredTarget: hoveredTargetInitialState,
     stageSize: stageSizeInitialState,
     menus: menuInitialState,
+    micIndicator: micIndicatorInitialState,
     modals: modalsInitialState,
     monitors: monitorsInitialState,
     monitorLayout: monitorLayoutInitialState,
@@ -91,6 +95,7 @@ const initTutorialCard = function (currentState, deckId) {
 };
 
 const guiReducer = combineReducers({
+    alerts: alertsReducer,
     assetDrag: assetDragReducer,
     blockDrag: blockDragReducer,
     cards: cardsReducer,
@@ -101,6 +106,7 @@ const guiReducer = combineReducers({
     hoveredTarget: hoveredTargetReducer,
     stageSize: stageSizeReducer,
     menus: menuReducer,
+    micIndicator: micIndicatorReducer,
     modals: modalReducer,
     monitors: monitorReducer,
     monitorLayout: monitorLayoutReducer,
diff --git a/src/reducers/menus.js b/src/reducers/menus.js
index 6693160c177fc988232fbd505f5a6c1e5af8d134..4cc494c60b860d324e4fa49e96a748f17a7233ca 100644
--- a/src/reducers/menus.js
+++ b/src/reducers/menus.js
@@ -1,15 +1,19 @@
 const OPEN_MENU = 'scratch-gui/menus/OPEN_MENU';
 const CLOSE_MENU = 'scratch-gui/menus/CLOSE_MENU';
 
+const MENU_ACCOUNT = 'accountMenu';
 const MENU_FILE = 'fileMenu';
 const MENU_EDIT = 'editMenu';
 const MENU_LANGUAGE = 'languageMenu';
+const MENU_LOGIN = 'loginMenu';
 
 
 const initialState = {
+    [MENU_ACCOUNT]: false,
     [MENU_FILE]: false,
     [MENU_EDIT]: false,
-    [MENU_LANGUAGE]: false
+    [MENU_LANGUAGE]: false,
+    [MENU_LOGIN]: false
 };
 
 const reducer = function (state, action) {
@@ -35,6 +39,9 @@ const closeMenu = menu => ({
     type: CLOSE_MENU,
     menu: menu
 });
+const openAccountMenu = () => openMenu(MENU_ACCOUNT);
+const closeAccountMenu = () => closeMenu(MENU_ACCOUNT);
+const accountMenuOpen = state => state.scratchGui.menus[MENU_ACCOUNT];
 const openFileMenu = () => openMenu(MENU_FILE);
 const closeFileMenu = () => closeMenu(MENU_FILE);
 const fileMenuOpen = state => state.scratchGui.menus[MENU_FILE];
@@ -44,17 +51,26 @@ const editMenuOpen = state => state.scratchGui.menus[MENU_EDIT];
 const openLanguageMenu = () => openMenu(MENU_LANGUAGE);
 const closeLanguageMenu = () => closeMenu(MENU_LANGUAGE);
 const languageMenuOpen = state => state.scratchGui.menus[MENU_LANGUAGE];
+const openLoginMenu = () => openMenu(MENU_LOGIN);
+const closeLoginMenu = () => closeMenu(MENU_LOGIN);
+const loginMenuOpen = state => state.scratchGui.menus[MENU_LOGIN];
 
 export {
     reducer as default,
     initialState as menuInitialState,
+    openAccountMenu,
+    closeAccountMenu,
+    accountMenuOpen,
     openFileMenu,
     closeFileMenu,
+    fileMenuOpen,
     openEditMenu,
     closeEditMenu,
+    editMenuOpen,
     openLanguageMenu,
     closeLanguageMenu,
-    fileMenuOpen,
-    editMenuOpen,
-    languageMenuOpen
+    languageMenuOpen,
+    openLoginMenu,
+    closeLoginMenu,
+    loginMenuOpen
 };
diff --git a/src/reducers/mic-indicator.js b/src/reducers/mic-indicator.js
new file mode 100644
index 0000000000000000000000000000000000000000..2548abbb41e785c086036b7e18e48b18f9e3542f
--- /dev/null
+++ b/src/reducers/mic-indicator.js
@@ -0,0 +1,26 @@
+const UPDATE = 'scratch-gui/mic-indicator/UPDATE';
+
+const initialState = false;
+
+const reducer = function (state, action) {
+    if (typeof state === 'undefined') state = initialState;
+    switch (action.type) {
+    case UPDATE:
+        return action.visible;
+    default:
+        return state;
+    }
+};
+
+const updateMicIndicator = function (visible) {
+    return {
+        type: UPDATE,
+        visible: visible
+    };
+};
+
+export {
+    reducer as default,
+    initialState as micIndicatorInitialState,
+    updateMicIndicator
+};