diff --git a/.travis.yml b/.travis.yml
index 9ef064ce111a7c278c285ca2008e46817e80189d..714d64bb83ee776b0ab26fe831b4e481cf6b15cd 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -20,22 +20,24 @@ script:
 - npm test
 - if [ "$TRAVIS_EVENT_TYPE" == "pull_request" ] && [ "$TRAVIS_BRANCH" == "master" ]; then  npm run test:smoke; fi
 before_deploy:
-- npm --no-git-tag-version version 0.1.0-prerelease.$(date +%Y%m%d%H%M%S)
-- if [ "$TRAVIS_BRANCH" == "develop" ]; then export NPM_TAG=develop; fi
-- git config --global user.email $(git log --pretty=format:"%ae" -n1)
-- git config --global user.name $(git log --pretty=format:"%an" -n1)
+- >
+  if [ -z "$BEFORE_DEPLOY_RAN" ]; then
+    npm --no-git-tag-version version 0.1.0-prerelease.$(date +%Y%m%d%H%M%S)
+    if [ "$TRAVIS_BRANCH" == "develop" ]; then export NPM_TAG=develop; fi
+    git config --global user.email $(git log --pretty=format:"%ae" -n1)
+    git config --global user.name $(git log --pretty=format:"%an" -n1)
+    export BEFORE_DEPLOY_RAN=true
+  fi
 deploy:
 - provider: script
   on:
     all_branches: true
-    condition: $TRAVIS_BRANCH != master
   skip_cleanup: true
   script: npm run deploy -- -x -e $TRAVIS_BRANCH -r https://${GH_TOKEN}@github.com/${TRAVIS_REPO_SLUG}.git
 - provider: script
   on:
-    branch: master
-  skip_cleanup: true
-  script: npm run --silent deploy -- -x -a -r https://${GH_TOKEN}@github.com/${TRAVIS_REPO_SLUG}.git
+    all_branches: true
+  script: npm run prune -- https://${GH_TOKEN}@github.com/${TRAVIS_REPO_SLUG}.git
 - provider: npm
   on:
     branch:
diff --git a/package.json b/package.json
index 4e60e963fab86da413c505fdfdb0d3b6660c2c42..a5875e0f7bc893903cafafe6348b03209b57ffde 100644
--- a/package.json
+++ b/package.json
@@ -7,6 +7,7 @@
     "build": "npm run clean && webpack --progress --colors --bail",
     "clean": "rimraf ./build && mkdirp build && rimraf ./dist && mkdirp dist",
     "deploy": "touch build/.nojekyll && gh-pages -t -d build -m \"Build for $(git log --pretty=format:%H -n1)\"",
+    "prune": "./prune-gh-pages.sh",
     "i18n:src": "babel src > tmp.js && rimraf tmp.js && build-i18n-src ./translations/messages/ ./translations/",
     "start": "webpack-dev-server",
     "test": "npm run test:lint && npm run test:unit && npm run build && npm run test:integration",
@@ -39,8 +40,8 @@
     "babel-preset-env": "^1.6.1",
     "babel-preset-react": "^6.22.0",
     "bowser": "1.9.3",
-    "chromedriver": "2.39.0",
-    "classnames": "2.2.5",
+    "chromedriver": "2.40.0",
+    "classnames": "2.2.6",
     "copy-webpack-plugin": "^4.5.1",
     "css-loader": "^0.28.11",
     "enzyme": "^3.1.0",
@@ -57,6 +58,7 @@
     "html-webpack-plugin": "^3.2.0",
     "immutable": "3.8.2",
     "jest": "^21.0.0",
+    "keymirror": "0.1.1",
     "lodash.bindall": "4.4.0",
     "lodash.debounce": "4.0.8",
     "lodash.defaultsdeep": "4.6.0",
@@ -91,14 +93,14 @@
     "redux-mock-store": "^1.2.3",
     "redux-throttle": "0.1.1",
     "rimraf": "^2.6.1",
-    "scratch-audio": "0.1.0-prerelease.1528210666",
-    "scratch-blocks": "0.1.0-prerelease.1528400332",
-    "scratch-l10n": "3.0.20180604162003",
-    "scratch-paint": "0.2.0-prerelease.20180607153112",
-    "scratch-render": "0.1.0-prerelease.20180605145739",
-    "scratch-storage": "0.5.0",
-    "scratch-vm": "0.1.0-prerelease.1528399883",
-    "scratch-svg-renderer": "0.2.0-prerelease.20180607141644",
+    "scratch-audio": "0.1.0-prerelease.1528996828",
+    "scratch-blocks": "0.1.0-prerelease.1529016587",
+    "scratch-l10n": "3.0.20180611175036",
+    "scratch-paint": "0.2.0-prerelease.20180614161221",
+    "scratch-render": "0.1.0-prerelease.20180615131212",
+    "scratch-storage": "0.5.1",
+    "scratch-svg-renderer": "0.2.0-prerelease.20180613184320",
+    "scratch-vm": "0.1.0-prerelease.1529017807",
     "selenium-webdriver": "3.6.0",
     "startaudiocontext": "1.2.1",
     "style-loader": "^0.21.0",
diff --git a/prune-gh-pages.sh b/prune-gh-pages.sh
new file mode 100755
index 0000000000000000000000000000000000000000..c7d594fe01c3d24fb0d3c9e7ad7bc084ee0a8b91
--- /dev/null
+++ b/prune-gh-pages.sh
@@ -0,0 +1,64 @@
+#!/bin/bash
+# gh-pages cleanup script: Switches to gh-pages branch, and removes all
+# directories that aren't listed as remote branches
+
+function deslash () {
+    # Recursively build a string of a directory's parents. E.g.,
+    # deslashed "feature/test/branch" returns feature/test feature
+    deslashed=$(dirname $1)
+    if [[ $deslashed =~ .*/.* ]]
+    then
+        echo $deslashed $(deslash $deslashed)
+    else
+        echo $deslashed
+    fi
+}
+
+repository=origin
+
+if [[ $1 != "" ]]
+then
+    repository=$1
+fi
+
+# Cache current branch
+current=$(git rev-parse --abbrev-ref HEAD)
+
+# Checkout most recent gh-pages
+git fetch --force $repository gh-pages:gh-pages
+git checkout gh-pages
+git clean -fdx
+
+# Make an array of directories to not delete, from the list of remote branches
+branches=$(git ls-remote --refs --quiet $repository | awk '{print $2}' | sed -e 's/refs\/heads\///')
+
+# Add parent directories of branches to the exclusion list (e.g. greenkeeper/)
+for branch in $branches; do
+    if [[ $branch =~ .*/.* ]]; then
+        branches+=" $(deslash $branch)"
+    fi
+done
+
+# Dedupe all the greenkeepers (or other duplicate parent directories)
+branches=$(echo "${branches[@]}" | tr ' ' '\n' | sort -u | tr '\n' ' ')
+
+# Remove all directories that don't have corresponding branches
+# It would be nice if we could exclude everything in .gitignore, but we're
+# not on the branch with the .gitignore anymore... so we can't.
+find . -type d \
+    \( \
+        -path ./.git -o \
+        -path ./node_modules \
+        $(printf " -o -path ./%s" $branches) \
+    \) -prune \
+    -o -mindepth 1 -type d \
+    -exec rm -rfv {} \;
+
+# Push
+git add -u
+git commit -m "Remove stale directories"
+git push $repository gh-pages
+
+# Return to where we were
+git checkout -f $current
+exit
diff --git a/src/components/action-menu/action-menu.jsx b/src/components/action-menu/action-menu.jsx
index 69d154fcc4df73d92e66cb6e935cc6f64e14495c..2725c502b421007f85c7ec4d8621ff213f35b162 100644
--- a/src/components/action-menu/action-menu.jsx
+++ b/src/components/action-menu/action-menu.jsx
@@ -76,8 +76,8 @@ class ActionMenu extends React.Component {
         // (possibly slow) action is started.
         return event => {
             ReactTooltip.hide();
+            if (fn) fn(event);
             this.setState({forceHide: true, isOpen: false}, () => {
-                if (fn) fn(event);
                 setTimeout(() => this.setState({forceHide: false}));
             });
         };
diff --git a/src/components/asset-panel/selector.css b/src/components/asset-panel/selector.css
index 98dff10c272eb58441dda16f7d1da69590a0d97a..3dc4e803d9e9d7f9c26c2c8c5a1da69b388bd943 100644
--- a/src/components/asset-panel/selector.css
+++ b/src/components/asset-panel/selector.css
@@ -47,12 +47,14 @@ $fade-out-distance: 100px;
     height: 0;
     flex-grow: 1;
     overflow-y: scroll;
+    display: flex;
+    flex-direction: column;
 }
 
 .list-item {
     width: 5rem;
     min-height: 5rem;
-    margin: 1rem auto;
+    margin: 0.5rem auto;
 }
 
 @media only screen and (max-width: $full-size-paint) {
@@ -64,3 +66,9 @@ $fade-out-distance: 100px;
         width: 4rem;
     }
 }
+
+
+.list-item.placeholder {
+    background: black;
+    filter: opacity(15%) brightness(20%);
+}
diff --git a/src/components/asset-panel/selector.jsx b/src/components/asset-panel/selector.jsx
index 6c3f72edb19f70f6c35958b62bd40a19058139cf..a9f69134ba43a1c3e79c1467f11875acbb8b535a 100644
--- a/src/components/asset-panel/selector.jsx
+++ b/src/components/asset-panel/selector.jsx
@@ -1,22 +1,33 @@
 import PropTypes from 'prop-types';
 import React from 'react';
-
+import classNames from 'classnames';
 import SpriteSelectorItem from '../../containers/sprite-selector-item.jsx';
-
 import Box from '../box/box.jsx';
 import ActionMenu from '../action-menu/action-menu.jsx';
+import SortableAsset from './sortable-asset.jsx';
+import SortableHOC from '../../lib/sortable-hoc.jsx';
+import DragConstants from '../../lib/drag-constants';
+
 import styles from './selector.css';
 
 const Selector = props => {
     const {
         buttons,
+        dragType,
         items,
         selectedItemIndex,
+        draggingIndex,
+        draggingType,
+        ordering,
+        onAddSortable,
+        onRemoveSortable,
         onDeleteClick,
         onDuplicateClick,
         onItemClick
     } = props;
 
+    const isRelevantDrag = draggingType === dragType;
+
     let newButtonSection = null;
 
     if (buttons.length > 0) {
@@ -38,20 +49,31 @@ const Selector = props => {
         <Box className={styles.wrapper}>
             <Box className={styles.listArea}>
                 {items.map((item, index) => (
-                    <SpriteSelectorItem
-                        assetId={item.assetId}
-                        className={styles.listItem}
-                        costumeURL={item.url}
-                        details={item.details}
-                        id={index}
-                        key={`asset-${index}`}
-                        name={item.name}
-                        number={index + 1 /* 1-indexed */}
-                        selected={index === selectedItemIndex}
-                        onClick={onItemClick}
-                        onDeleteButtonClick={onDeleteClick}
-                        onDuplicateButtonClick={onDuplicateClick}
-                    />
+                    <SortableAsset
+                        id={item.name}
+                        index={isRelevantDrag ? ordering.indexOf(index) : index}
+                        key={item.name}
+                        onAddSortable={onAddSortable}
+                        onRemoveSortable={onRemoveSortable}
+                    >
+                        <SpriteSelectorItem
+                            assetId={item.assetId}
+                            className={classNames(styles.listItem, {
+                                [styles.placeholder]: isRelevantDrag && index === draggingIndex
+                            })}
+                            costumeURL={item.url}
+                            details={item.details}
+                            dragType={dragType}
+                            id={index}
+                            index={index}
+                            name={item.name}
+                            number={index + 1 /* 1-indexed */}
+                            selected={index === selectedItemIndex}
+                            onClick={onItemClick}
+                            onDeleteButtonClick={onDeleteClick}
+                            onDuplicateButtonClick={onDuplicateClick}
+                        />
+                    </SortableAsset>
                 ))}
             </Box>
             {newButtonSection}
@@ -65,14 +87,20 @@ Selector.propTypes = {
         img: PropTypes.string.isRequired,
         onClick: PropTypes.func
     })),
+    dragType: PropTypes.oneOf(Object.keys(DragConstants)),
+    draggingIndex: PropTypes.number,
+    draggingType: PropTypes.oneOf(Object.keys(DragConstants)),
     items: PropTypes.arrayOf(PropTypes.shape({
         url: PropTypes.string,
         name: PropTypes.string.isRequired
     })),
+    onAddSortable: PropTypes.func,
     onDeleteClick: PropTypes.func,
     onDuplicateClick: PropTypes.func,
     onItemClick: PropTypes.func.isRequired,
+    onRemoveSortable: PropTypes.func,
+    ordering: PropTypes.arrayOf(PropTypes.number),
     selectedItemIndex: PropTypes.number.isRequired
 };
 
-export default Selector;
+export default SortableHOC(Selector);
diff --git a/src/components/asset-panel/sortable-asset.jsx b/src/components/asset-panel/sortable-asset.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..838ce6ebde5f6e6c347c5ccd542b8baa1b7a0119
--- /dev/null
+++ b/src/components/asset-panel/sortable-asset.jsx
@@ -0,0 +1,45 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+
+import bindAll from 'lodash.bindall';
+
+class SortableAsset extends React.Component {
+    constructor (props) {
+        super(props);
+        bindAll(this, [
+            'setRef'
+        ]);
+    }
+    componentDidMount () {
+        this.props.onAddSortable(this.ref);
+    }
+    componentWillUnmount () {
+        this.props.onRemoveSortable(this.ref);
+    }
+    setRef (ref) {
+        this.ref = ref;
+    }
+    render () {
+        return (
+            <div
+                className={this.props.className}
+                ref={this.setRef}
+                style={{
+                    order: this.props.index
+                }}
+            >
+                {this.props.children}
+            </div>
+        );
+    }
+}
+
+SortableAsset.propTypes = {
+    children: PropTypes.node.isRequired,
+    className: PropTypes.string,
+    index: PropTypes.number.isRequired,
+    onAddSortable: PropTypes.func.isRequired,
+    onRemoveSortable: PropTypes.func.isRequired
+};
+
+export default SortableAsset;
diff --git a/src/components/browser-modal/browser-modal.css b/src/components/browser-modal/browser-modal.css
index b93eb0db43f2ad5e67e4ecb30f552bd0ba8502f3..aa8840c4cc822574ff565273a858d52ced169227 100644
--- a/src/components/browser-modal/browser-modal.css
+++ b/src/components/browser-modal/browser-modal.css
@@ -1,6 +1,7 @@
 @import "../../css/colors.css";
 @import "../../css/units.css";
 @import "../../css/typography.css";
+@import "../../css/z-index.css";
 
 .modal-overlay {
     position: fixed;
@@ -8,7 +9,7 @@
     left: 0;
     right: 0;
     bottom: 0;
-    z-index: 1000;
+    z-index: $z-index-modal;
     background-color: $ui-modal-overlay;
 }
 
diff --git a/src/components/cards/card.css b/src/components/cards/card.css
index 84b6e6bf784773577d29e29954b663d19f74ab5a..1aeb1cb718d4e8c0877a12d5d70bfec9b99cd405 100644
--- a/src/components/cards/card.css
+++ b/src/components/cards/card.css
@@ -1,9 +1,11 @@
 @import "../../css/units.css";
 @import "../../css/colors.css";
+@import "../../css/z-index.css";
 
 .card-container {
     position:absolute;
-    z-index: 100;
+    z-index: $z-index-card;
+    margin: 0.5rem 2rem;
 }
 
 .left-card, .right-card {
@@ -13,7 +15,7 @@
     background: $ui-white;
     border: 1px solid $ui-tertiary;
     width: 10px;
-    z-index: 99;
+    z-index: 10;
     opacity: 0.9;
     overflow: hidden;
 }
@@ -46,7 +48,7 @@
     position: absolute;
     top: 50%;
     margin-top: -15px;
-    z-index: 101;
+    z-index: 20;
     user-select: none;
     cursor: pointer;
     background: $motion-primary;
@@ -73,7 +75,7 @@
     display: flex;
     flex-direction: column;
     cursor: move;
-    z-index: 101;
+    z-index: 20;
     overflow: hidden;
     box-shadow: 0px 5px 25px 5px $ui-black-transparent;
     align-items: center;
diff --git a/src/components/coming-soon/coming-soon.css b/src/components/coming-soon/coming-soon.css
index db737e1c80f5a8eefca91d8b1a5e66e8a0d629cd..bdd7f87eb4f031a9155aec4a0fd3f6affda1ad0d 100644
--- a/src/components/coming-soon/coming-soon.css
+++ b/src/components/coming-soon/coming-soon.css
@@ -5,17 +5,18 @@
  */
 
 @import "../../css/colors.css";
+@import "../../css/z-index.css";
 
 .coming-soon {
     background-color: $data-primary !important;
-    border: 1px solid $ui-black-transparent) !important;
+    border: 1px solid $ui-black-transparent !important;
     border-radius: .25rem !important;
     box-shadow: 0 0 .5rem $ui-black-transparent !important;
     padding: .75rem 1rem !important;
     font-family: "Helvetica Neue", Helvetica, Arial, sans-serif !important;
     font-size: 1rem !important;
     line-height: 1.25rem !important;
-    z-index: 100 !important;
+    z-index: $z-index-coming-soon !important;
 }
 
 .coming-soon:after {
diff --git a/src/components/context-menu/context-menu.css b/src/components/context-menu/context-menu.css
index ff2505ff955e0c318c8edcd01864f21e20253abc..5a02ce797d9498990738f75f497a299acf441608 100644
--- a/src/components/context-menu/context-menu.css
+++ b/src/components/context-menu/context-menu.css
@@ -1,5 +1,6 @@
 @import "../../css/colors.css";
 @import "../../css/units.css";
+@import "../../css/z-index.css";
 
 .context-menu {
     min-width: 130px;
@@ -13,7 +14,7 @@
     box-shadow: 0px 0px 5px 1px $ui-black-transparent;
     pointer-events: none;
     transition: opacity 0.2s ease;
-    z-index: 200; /* Above the stage */
+    z-index: $z-index-context-menu;
 }
 
 .menu-item {
diff --git a/src/components/drag-layer/drag-layer.css b/src/components/drag-layer/drag-layer.css
index 711b5eea813d6a2d80b97d75c98c40fbb68e8457..af6ae5dac2cb920bebbc6ee83351894e60826aa8 100644
--- a/src/components/drag-layer/drag-layer.css
+++ b/src/components/drag-layer/drag-layer.css
@@ -1,10 +1,11 @@
 @import "../../css/units.css";
 @import "../../css/colors.css";
+@import "../../css/z-index.css";
 
 .drag-layer {
     position: fixed;
     pointer-events: none;
-    z-index: 1000; /* Above everything */
+    z-index: $z-index-drag-layer;
     left: 0;
     top: 0;
     width: 100%;
@@ -18,11 +19,19 @@
 
 .image {
     max-width: 80px;
+    max-height: 80px;
+    min-width: 50px;
+    min-height: 50px;
 
     /* Center the dragging image on the given position */
     margin-left: -50%;
     margin-top: -50%;
 
+    padding: 0.25rem;
+    border: 2px solid $motion-primary;
+    background: $ui-white;
+    border-radius: 0.5rem;
+
     /* Use the same drop shadow as stage dragging */
-    filter: drop-shadow(5px 5px 5px $ui-black-transparent);
+    box-shadow: 5px 5px 5px $ui-black-transparent;
 }
diff --git a/src/components/gui/gui.css b/src/components/gui/gui.css
index 2e4e7b846811c8fced93abbc6c004c19fde51ad8..af7500fb0a3d9e0fd57c3bda81a3fa3fe5d97a95 100644
--- a/src/components/gui/gui.css
+++ b/src/components/gui/gui.css
@@ -1,5 +1,6 @@
 @import "../../css/units.css";
 @import "../../css/colors.css";
+@import "../../css/z-index.css";
 
 .page-wrapper {
     height: 100%;
@@ -162,11 +163,23 @@
     */
     display: flex;
     flex-direction: column;
+}
 
-    /* Fix the max width to max stage size (defined in layout_constants.js) + gutter size */
+.stage-and-target-wrapper.large {
+    /* Fix the max width to max large stage size (defined in layout_constants.js) + gutter size */
     max-width: calc(480px + calc($space * 2));
 }
 
+.stage-and-target-wrapper.large-constrained {
+    /* Fix the max width to max largeConstrained stage size (defined in layout_constants.js) + gutter size */
+    max-width: calc(408px + calc($space * 2));
+}
+
+.stage-and-target-wrapper.small {
+    /* Fix the max width to max small stage size (defined in layout_constants.js) + gutter size */
+    max-width: calc(240px + calc($space * 2));
+}
+
 .target-wrapper {
     display: flex;
     flex-grow: 1;
@@ -176,6 +189,8 @@
     padding-left: $space;
     padding-right: $space;
 
+    min-height: 0; /* this makes it work in Firefox */
+    
     /*
         For making the sprite-selector a scrollable pane
         @todo: Not working in Safari
@@ -190,7 +205,7 @@
     position: absolute;
     bottom: 0;
     left: 0;
-    z-index: 50; /** Force extension button above the ScratchBlocks flyout. */
+    z-index: $z-index-extension-button;
     background: $motion-primary;
 
     border: 1px solid $motion-primary;
diff --git a/src/components/gui/gui.jsx b/src/components/gui/gui.jsx
index 66a08f64f3d611c7d51a93a75df274710a7b0250..2d3cf79b8869eab536a4f3d7b2a1ab79f2fb59f7 100644
--- a/src/components/gui/gui.jsx
+++ b/src/components/gui/gui.jsx
@@ -1,7 +1,10 @@
 import classNames from 'classnames';
+import omit from 'lodash.omit';
 import PropTypes from 'prop-types';
 import React from 'react';
 import {defineMessages, FormattedMessage, injectIntl, intlShape} from 'react-intl';
+import {connect} from 'react-redux';
+import MediaQuery from 'react-responsive';
 import {Tab, Tabs, TabList, TabPanel} from 'react-tabs';
 import tabStyles from 'react-tabs/style/react-tabs.css';
 import VM from 'scratch-vm';
@@ -15,6 +18,8 @@ import StageWrapper from '../../containers/stage-wrapper.jsx';
 import Loader from '../loader/loader.jsx';
 import Box from '../box/box.jsx';
 import MenuBar from '../menu-bar/menu-bar.jsx';
+import CostumeLibrary from '../../containers/costume-library.jsx';
+import BackdropLibrary from '../../containers/backdrop-library.jsx';
 
 import Backpack from '../../containers/backpack.jsx';
 import PreviewModal from '../../containers/preview-modal.jsx';
@@ -24,6 +29,9 @@ import TipsLibrary from '../../containers/tips-library.jsx';
 import Cards from '../../containers/cards.jsx';
 import DragLayer from '../../containers/drag-layer.jsx';
 
+import layout, {STAGE_SIZE_MODES} from '../../lib/layout-constants';
+import {resolveStageSize} from '../../lib/screen-utils';
+
 import styles from './gui.css';
 import addExtensionIcon from './icon--extensions.svg';
 import codeIcon from './icon--code.svg';
@@ -38,7 +46,7 @@ const messages = defineMessages({
     }
 });
 
-// Cache this value to only retreive it once the first time.
+// Cache this value to only retrieve it once the first time.
 // Assume that it doesn't change for a session.
 let isRendererSupported = null;
 
@@ -46,10 +54,12 @@ const GUIComponent = props => {
     const {
         activeTabIndex,
         basePath,
+        backdropLibraryVisible,
         backpackOptions,
         blocksTabVisible,
         cardsVisible,
         children,
+        costumeLibraryVisible,
         costumesTabVisible,
         enableCommunity,
         importInfoVisible,
@@ -60,13 +70,16 @@ const GUIComponent = props => {
         onActivateCostumesTab,
         onActivateSoundsTab,
         onActivateTab,
+        onRequestCloseBackdropLibrary,
+        onRequestCloseCostumeLibrary,
         previewInfoVisible,
         targetIsStage,
         soundsTabVisible,
+        stageSizeMode,
         tipsLibraryVisible,
         vm,
         ...componentProps
-    } = props;
+    } = omit(props, 'dispatch');
     if (children) {
         return <Box {...componentProps}>{children}</Box>;
     }
@@ -84,149 +97,173 @@ const GUIComponent = props => {
         isRendererSupported = Renderer.isSupported();
     }
 
-    return isPlayerOnly ? (
-        <StageWrapper
-            isRendererSupported={isRendererSupported}
-            vm={vm}
-        />
-    ) : (
-        <Box
-            className={styles.pageWrapper}
-            {...componentProps}
-        >
-            {previewInfoVisible ? (
-                <PreviewModal />
-            ) : null}
-            {loading ? (
-                <Loader />
-            ) : null}
-            {importInfoVisible ? (
-                <ImportModal />
-            ) : null}
-            {isRendererSupported ? null : (
-                <WebGlModal />
-            )}
-            {tipsLibraryVisible ? (
-                <TipsLibrary />
-            ) : null}
-            {cardsVisible ? (
-                <Cards />
-            ) : null}
-            <MenuBar enableCommunity={enableCommunity} />
-            <Box className={styles.bodyWrapper}>
-                <Box className={styles.flexWrapper}>
-                    <Box className={styles.editorWrapper}>
-                        <Tabs
-                            className={tabClassNames.tabs}
-                            forceRenderTabPanel={true} // eslint-disable-line react/jsx-boolean-value
-                            selectedIndex={activeTabIndex}
-                            selectedTabClassName={tabClassNames.tabSelected}
-                            selectedTabPanelClassName={tabClassNames.tabPanelSelected}
-                            onSelect={onActivateTab}
-                        >
-                            <TabList className={tabClassNames.tabList}>
-                                <Tab className={tabClassNames.tab}>
-                                    <img
-                                        draggable={false}
-                                        src={codeIcon}
-                                    />
-                                    <FormattedMessage
-                                        defaultMessage="Code"
-                                        description="Button to get to the code panel"
-                                        id="gui.gui.codeTab"
-                                    />
-                                </Tab>
-                                <Tab
-                                    className={tabClassNames.tab}
-                                    onClick={onActivateCostumesTab}
-                                >
-                                    <img
-                                        draggable={false}
-                                        src={costumesIcon}
-                                    />
-                                    {targetIsStage ? (
-                                        <FormattedMessage
-                                            defaultMessage="Backdrops"
-                                            description="Button to get to the backdrops panel"
-                                            id="gui.gui.backdropsTab"
+    return (<MediaQuery minWidth={layout.fullSizeMinWidth}>{isFullSize => {
+        const stageSize = resolveStageSize(stageSizeMode, isFullSize);
+
+        return isPlayerOnly ? (
+            <StageWrapper
+                isRendererSupported={isRendererSupported}
+                stageSize={stageSize}
+                vm={vm}
+            />
+        ) : (
+            <Box
+                className={styles.pageWrapper}
+                {...componentProps}
+            >
+                {previewInfoVisible ? (
+                    <PreviewModal />
+                ) : null}
+                {loading ? (
+                    <Loader />
+                ) : null}
+                {importInfoVisible ? (
+                    <ImportModal />
+                ) : null}
+                {isRendererSupported ? null : (
+                    <WebGlModal />
+                )}
+                {tipsLibraryVisible ? (
+                    <TipsLibrary />
+                ) : null}
+                {cardsVisible ? (
+                    <Cards />
+                ) : null}
+                {costumeLibraryVisible ? (
+                    <CostumeLibrary
+                        vm={vm}
+                        onRequestClose={onRequestCloseCostumeLibrary}
+                    />
+                ) : null}
+                {backdropLibraryVisible ? (
+                    <BackdropLibrary
+                        vm={vm}
+                        onRequestClose={onRequestCloseBackdropLibrary}
+                    />
+                ) : null}
+                <MenuBar enableCommunity={enableCommunity} />
+                <Box className={styles.bodyWrapper}>
+                    <Box className={styles.flexWrapper}>
+                        <Box className={styles.editorWrapper}>
+                            <Tabs
+                                forceRenderTabPanel
+                                className={tabClassNames.tabs}
+                                selectedIndex={activeTabIndex}
+                                selectedTabClassName={tabClassNames.tabSelected}
+                                selectedTabPanelClassName={tabClassNames.tabPanelSelected}
+                                onSelect={onActivateTab}
+                            >
+                                <TabList className={tabClassNames.tabList}>
+                                    <Tab className={tabClassNames.tab}>
+                                        <img
+                                            draggable={false}
+                                            src={codeIcon}
                                         />
-                                    ) : (
                                         <FormattedMessage
-                                            defaultMessage="Costumes"
-                                            description="Button to get to the costumes panel"
-                                            id="gui.gui.costumesTab"
+                                            defaultMessage="Code"
+                                            description="Button to get to the code panel"
+                                            id="gui.gui.codeTab"
                                         />
-                                    )}
-                                </Tab>
-                                <Tab
-                                    className={tabClassNames.tab}
-                                    onClick={onActivateSoundsTab}
-                                >
-                                    <img
-                                        draggable={false}
-                                        src={soundsIcon}
-                                    />
-                                    <FormattedMessage
-                                        defaultMessage="Sounds"
-                                        description="Button to get to the sounds panel"
-                                        id="gui.gui.soundsTab"
-                                    />
-                                </Tab>
-                            </TabList>
-                            <TabPanel className={tabClassNames.tabPanel}>
-                                <Box className={styles.blocksWrapper}>
-                                    <Blocks
-                                        grow={1}
-                                        isVisible={blocksTabVisible}
-                                        options={{
-                                            media: `${basePath}static/blocks-media/`
-                                        }}
-                                        vm={vm}
-                                    />
-                                </Box>
-                                <Box className={styles.extensionButtonContainer}>
-                                    <button
-                                        className={styles.extensionButton}
-                                        title={intl.formatMessage(messages.addExtension)}
-                                        onClick={onExtensionButtonClick}
+                                    </Tab>
+                                    <Tab
+                                        className={tabClassNames.tab}
+                                        onClick={onActivateCostumesTab}
                                     >
                                         <img
-                                            className={styles.extensionButtonIcon}
                                             draggable={false}
-                                            src={addExtensionIcon}
+                                            src={costumesIcon}
                                         />
-                                    </button>
-                                </Box>
-                            </TabPanel>
-                            <TabPanel className={tabClassNames.tabPanel}>
-                                {costumesTabVisible ? <CostumeTab vm={vm} /> : null}
-                            </TabPanel>
-                            <TabPanel className={tabClassNames.tabPanel}>
-                                {soundsTabVisible ? <SoundTab vm={vm} /> : null}
-                            </TabPanel>
-                        </Tabs>
-                        {backpackOptions.visible ? (
-                            <Backpack host={backpackOptions.host} />
-                        ) : null}
-                    </Box>
+                                        {targetIsStage ? (
+                                            <FormattedMessage
+                                                defaultMessage="Backdrops"
+                                                description="Button to get to the backdrops panel"
+                                                id="gui.gui.backdropsTab"
+                                            />
+                                        ) : (
+                                            <FormattedMessage
+                                                defaultMessage="Costumes"
+                                                description="Button to get to the costumes panel"
+                                                id="gui.gui.costumesTab"
+                                            />
+                                        )}
+                                    </Tab>
+                                    <Tab
+                                        className={tabClassNames.tab}
+                                        onClick={onActivateSoundsTab}
+                                    >
+                                        <img
+                                            draggable={false}
+                                            src={soundsIcon}
+                                        />
+                                        <FormattedMessage
+                                            defaultMessage="Sounds"
+                                            description="Button to get to the sounds panel"
+                                            id="gui.gui.soundsTab"
+                                        />
+                                    </Tab>
+                                </TabList>
+                                <TabPanel className={tabClassNames.tabPanel}>
+                                    <Box className={styles.blocksWrapper}>
+                                        <Blocks
+                                            grow={1}
+                                            isVisible={blocksTabVisible}
+                                            options={{
+                                                media: `${basePath}static/blocks-media/`
+                                            }}
+                                            stageSize={stageSize}
+                                            vm={vm}
+                                        />
+                                    </Box>
+                                    <Box className={styles.extensionButtonContainer}>
+                                        <button
+                                            className={styles.extensionButton}
+                                            title={intl.formatMessage(messages.addExtension)}
+                                            onClick={onExtensionButtonClick}
+                                        >
+                                            <img
+                                                className={styles.extensionButtonIcon}
+                                                draggable={false}
+                                                src={addExtensionIcon}
+                                            />
+                                        </button>
+                                    </Box>
+                                </TabPanel>
+                                <TabPanel className={tabClassNames.tabPanel}>
+                                    {costumesTabVisible ? <CostumeTab vm={vm} /> : null}
+                                </TabPanel>
+                                <TabPanel className={tabClassNames.tabPanel}>
+                                    {soundsTabVisible ? <SoundTab vm={vm} /> : null}
+                                </TabPanel>
+                            </Tabs>
+                            {backpackOptions.visible ? (
+                                <Backpack host={backpackOptions.host} />
+                            ) : null}
+                        </Box>
 
-                    <Box className={styles.stageAndTargetWrapper}>
-                        <StageWrapper
-                            isRendererSupported={isRendererSupported}
-                            vm={vm}
-                        />
-                        <Box className={styles.targetWrapper}>
-                            <TargetPane vm={vm} />
+                        <Box className={classNames(styles.stageAndTargetWrapper, styles[stageSize])}>
+                            <StageWrapper
+                                isRendererSupported={isRendererSupported}
+                                stageSize={stageSize}
+                                vm={vm}
+                            />
+                            <Box className={styles.targetWrapper}>
+                                <TargetPane
+                                    stageSize={stageSize}
+                                    vm={vm}
+                                />
+                            </Box>
                         </Box>
                     </Box>
                 </Box>
+                <DragLayer />
             </Box>
-            <DragLayer />
-        </Box>
-    );
+        );
+    }}</MediaQuery>);
 };
+
 GUIComponent.propTypes = {
     activeTabIndex: PropTypes.number,
+    backdropLibraryVisible: PropTypes.bool,
     backpackOptions: PropTypes.shape({
         host: PropTypes.string,
         visible: PropTypes.bool
@@ -235,6 +272,7 @@ GUIComponent.propTypes = {
     blocksTabVisible: PropTypes.bool,
     cardsVisible: PropTypes.bool,
     children: PropTypes.node,
+    costumeLibraryVisible: PropTypes.bool,
     costumesTabVisible: PropTypes.bool,
     enableCommunity: PropTypes.bool,
     importInfoVisible: PropTypes.bool,
@@ -245,9 +283,12 @@ GUIComponent.propTypes = {
     onActivateSoundsTab: PropTypes.func,
     onActivateTab: PropTypes.func,
     onExtensionButtonClick: PropTypes.func,
+    onRequestCloseBackdropLibrary: PropTypes.func,
+    onRequestCloseCostumeLibrary: PropTypes.func,
     onTabSelect: PropTypes.func,
     previewInfoVisible: PropTypes.bool,
     soundsTabVisible: PropTypes.bool,
+    stageSizeMode: PropTypes.oneOf(Object.keys(STAGE_SIZE_MODES)),
     targetIsStage: PropTypes.bool,
     tipsLibraryVisible: PropTypes.bool,
     vm: PropTypes.instanceOf(VM).isRequired
@@ -257,6 +298,15 @@ GUIComponent.defaultProps = {
         host: null,
         visible: false
     },
-    basePath: './'
+    basePath: './',
+    stageSizeMode: STAGE_SIZE_MODES.large
 };
-export default injectIntl(GUIComponent);
+
+const mapStateToProps = state => ({
+    // This is the button's mode, as opposed to the actual current state
+    stageSizeMode: state.scratchGui.stageSize.stageSize
+});
+
+export default injectIntl(connect(
+    mapStateToProps
+)(GUIComponent));
diff --git a/src/components/import-modal/import-modal.css b/src/components/import-modal/import-modal.css
index 93480429b19e92ee3f8d4fa08883d2a87b9a7530..b41c02b1baaa92923fc92af9ecbe3d22667cef1f 100644
--- a/src/components/import-modal/import-modal.css
+++ b/src/components/import-modal/import-modal.css
@@ -1,6 +1,7 @@
 @import "../../css/colors.css";
 @import "../../css/units.css";
 @import "../../css/typography.css";
+@import "../../css/z-index.css";
 
 .modal-overlay {
     position: fixed;
@@ -8,7 +9,7 @@
     left: 0;
     right: 0;
     bottom: 0;
-    z-index: 1000;
+    z-index: $z-index-modal;
     background-color: $ui-modal-overlay;
 }
 
diff --git a/src/components/language-selector/language-selector.css b/src/components/language-selector/language-selector.css
index 5da79cf25f3375ae6a00bdcbde105205c2e4dc61..d8ae588ae13fd05fc9d6b07eb6316de7427a4613 100644
--- a/src/components/language-selector/language-selector.css
+++ b/src/components/language-selector/language-selector.css
@@ -1,13 +1,6 @@
 @import "../../css/colors.css";
 @import "../../css/units.css";
 
-.group {
-    display: inline-flex;
-    flex-direction: row; /* makes columns, for each label/form group */
-    align-items: center;
-    vertical-align: middle;
-}
-
 .language-icon {
     height:  1.5rem;
 }
@@ -17,7 +10,7 @@
 }
 
 .language-select {
-    width: 100%;
+    margin: .5rem;
     height: 1.85rem;
     border: 1px solid $motion-primary;
     user-select: none;
diff --git a/src/components/language-selector/language-selector.jsx b/src/components/language-selector/language-selector.jsx
index befe4797681a1097ad47c05f360129b045658b65..ad7a9aa90f96aac8e27e7c97994f29049d98edcf 100644
--- a/src/components/language-selector/language-selector.jsx
+++ b/src/components/language-selector/language-selector.jsx
@@ -1,56 +1,48 @@
-import classNames from 'classnames';
 import PropTypes from 'prop-types';
 import React from 'react';
 
 import Box from '../box/box.jsx';
 import locales from 'scratch-l10n';
-import languageIcon from './language-icon.svg';
-import dropdownCaret from './dropdown-caret.svg';
 import styles from './language-selector.css';
 
-const LanguageSelector = ({
-    currentLocale,
-    onChange,
-    open,
-    ...props
-}) => (
-    <Box {...props}>
-        <div className={styles.group}>
-            {open ? (
-                <select
-                    disabled
-                    aria-label="language selector"
-                    className={styles.languageSelect}
-                    value={currentLocale}
-                    onChange={onChange}
+class LanguageSelector extends React.Component {
+    render () {
+        const {
+            componentRef,
+            currentLocale,
+            onChange,
+            ...componentProps
+        } = this.props;
+        return (
+            <Box
+                {...componentProps}
+            >
+                <div
+                    className={styles.group}
+                    ref={componentRef}
                 >
-                    {Object.keys(locales).map(locale => (
-                        <option
-                            key={locale}
-                            value={locale}
-                        >
-                            {locales[locale].name}
-                        </option>
-                    ))}
-                </select>
-            ) : (
-                <React.Fragment>
-                    <img
-                        className={classNames(styles.languageIcon, styles.disabled)}
-                        src={languageIcon}
-                    />
-                    <img
-                        className={classNames(styles.dropdownCaret, styles.disabled)}
-                        src={dropdownCaret}
-                    />
-                </React.Fragment>
-            )}
-        </div>
-    </Box>
-);
-
+                    <select
+                        className={styles.languageSelect}
+                        value={currentLocale}
+                        onChange={onChange}
+                    >
+                        {Object.keys(locales).map(locale => (
+                            <option
+                                key={locale}
+                                value={locale}
+                            >
+                                {locales[locale].name}
+                            </option>
+                        ))}
+                    </select>
+                </div>
+            </Box>
+        );
+    }
+}
 
 LanguageSelector.propTypes = {
+    componentRef: PropTypes.func,
     currentLocale: PropTypes.string,
     onChange: PropTypes.func,
     open: PropTypes.bool
diff --git a/src/components/library-item/library-item.css b/src/components/library-item/library-item.css
index 7b91cc96acf857c8957877fa73ab431f239385b9..3258b4a7bd16162f239d73990a26df77894f2ea0 100644
--- a/src/components/library-item/library-item.css
+++ b/src/components/library-item/library-item.css
@@ -5,7 +5,7 @@
     display: flex;
     flex-direction: column;
     align-items: center;
-    justify-content: center;
+    justify-content: flex-start;
     flex-basis: 160px;
     max-width: 160px;
     height: 160px;
diff --git a/src/components/library/library.css b/src/components/library/library.css
index 5a2b6e1f0f47a9f58822298f791453d860ed5042..8c132c389347c66ca9d539c58f8deed967764e76 100644
--- a/src/components/library/library.css
+++ b/src/components/library/library.css
@@ -5,6 +5,7 @@
     display: flex;
     justify-content: flex-start;
     align-content: flex-start;
+    align-items: flex-start;
     background: $ui-secondary;
     flex-grow: 1;
     flex-wrap: wrap;
diff --git a/src/components/loader/loader.css b/src/components/loader/loader.css
index 8ebbdb5cfe9d47e7a9eb880ee66e38272c8d86d4..9f97b3e811596bb6a6811d707229caaaada97159 100644
--- a/src/components/loader/loader.css
+++ b/src/components/loader/loader.css
@@ -1,4 +1,5 @@
 @import "../../css/colors.css";
+@import "../../css/z-index.css";
 
 .background {
     position: fixed;
@@ -6,7 +7,7 @@
     left: 0;
     width: 100%;
     height: 100%;
-    z-index: 999; /* Below preview modal */
+    z-index: $z-index-loader;
     display: flex;
     justify-content: center;
     align-items: center;
diff --git a/src/components/menu-bar/menu-bar.css b/src/components/menu-bar/menu-bar.css
index 96c64b345fd7e60c9ff9d1bc52c01dcdc9aa6ed8..eba5f885eff82eae922578bdb57acaa75efbecd0 100644
--- a/src/components/menu-bar/menu-bar.css
+++ b/src/components/menu-bar/menu-bar.css
@@ -1,5 +1,6 @@
 @import "../../css/colors.css";
 @import "../../css/units.css";
+@import "../../css/z-index.css";
 
 .menu-bar {
     display: flex;
@@ -43,9 +44,16 @@
     vertical-align: middle;
 }
 
+.language-icon {
+    height:  1.5rem;
+}
+
+.language-menu {
+    display: inline-flex;
+}
+
 .menu {
-    /* blocklyToolboxDiv is 40 */
-    z-index: 50;
+    z-index: $z-index-menu-bar;
     top: $menu-bar-height;
 }
 
diff --git a/src/components/menu-bar/menu-bar.jsx b/src/components/menu-bar/menu-bar.jsx
index 7742e210e362ac0d1772e98c447cb792ecec8a46..e4de61ffded598b2c3f60db74d61457aab486009 100644
--- a/src/components/menu-bar/menu-bar.jsx
+++ b/src/components/menu-bar/menu-bar.jsx
@@ -1,6 +1,6 @@
 import classNames from 'classnames';
 import {connect} from 'react-redux';
-import {FormattedMessage} from 'react-intl';
+import {defineMessages, FormattedMessage, injectIntl, intlShape} from 'react-intl';
 import PropTypes from 'prop-types';
 import React from 'react';
 
@@ -22,7 +22,10 @@ import {
     fileMenuOpen,
     openEditMenu,
     closeEditMenu,
-    editMenuOpen
+    editMenuOpen,
+    openLanguageMenu,
+    closeLanguageMenu,
+    languageMenuOpen
 } from '../../reducers/menus';
 
 import styles from './menu-bar.css';
@@ -32,29 +35,56 @@ 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 languageIcon from '../language-selector/language-icon.svg';
+
 import scratchLogo from './scratch-logo.svg';
 
 import helpIcon from './icon--help.svg';
 
+const ariaMessages = defineMessages({
+    language: {
+        id: 'gui.menuBar.LanguageSelector',
+        defaultMessage: 'language selector',
+        description: 'accessibility text for the language selection menu'
+    },
+    howTo: {
+        id: 'gui.menuBar.howToLibrary',
+        defaultMessage: 'How-to Library',
+        description: 'accessibility text for the how-to library button'
+    }
+});
+
 const MenuBarItemTooltip = ({
     children,
     className,
+    enable,
     id,
     place = 'bottom'
-}) => (
-    <ComingSoonTooltip
-        className={classNames(styles.comingSoon, className)}
-        place={place}
-        tooltipClassName={styles.comingSoonTooltip}
-        tooltipId={id}
-    >
-        {children}
-    </ComingSoonTooltip>
-);
+}) => {
+    if (enable) {
+        return (
+            <React.Fragment>
+                {children}
+            </React.Fragment>
+        );
+    }
+    return (
+        <ComingSoonTooltip
+            className={classNames(styles.comingSoon, className)}
+            place={place}
+            tooltipClassName={styles.comingSoonTooltip}
+            tooltipId={id}
+        >
+            {children}
+        </ComingSoonTooltip>
+    );
+};
+
 
 MenuBarItemTooltip.propTypes = {
     children: PropTypes.node,
     className: PropTypes.string,
+    enable: PropTypes.bool,
     id: PropTypes.string,
     place: PropTypes.oneOf(['top', 'bottom', 'left', 'right'])
 };
@@ -111,12 +141,37 @@ const MenuBar = props => (
                         src={scratchLogo}
                     />
                 </div>
-                <div className={classNames(styles.menuBarItem, styles.hoverable)}>
+                <div
+                    className={classNames(styles.menuBarItem, styles.hoverable, {
+                        [styles.active]: props.languageMenuOpen
+                    })}
+                    onMouseUp={props.onClickLanguage}
+                >
                     <MenuBarItemTooltip
+                        enable={window.location.search.indexOf('enable=language') !== -1}
                         id="menubar-selector"
                         place="right"
                     >
-                        <LanguageSelector />
+                        <div
+                            aria-label={props.intl.formatMessage(ariaMessages.language)}
+                            className={classNames(styles.languageMenu)}
+                        >
+                            <img
+                                className={styles.languageIcon}
+                                src={languageIcon}
+                            />
+                            <img
+                                className={styles.dropdownCaret}
+                                src={dropdownCaret}
+                            />
+                        </div>
+                        <MenuBarMenu
+                            open={props.languageMenuOpen}
+                            onRequestClose={props.onRequestCloseLanguage}
+                        >
+                            <LanguageSelector />
+                        </MenuBarMenu>
+
                     </MenuBarItemTooltip>
                 </div>
                 <div
@@ -314,7 +369,7 @@ const MenuBar = props => (
         </div>
         <div className={styles.accountInfoWrapper}>
             <div
-                aria-label="How-to Library"
+                aria-label={props.intl.formatMessage(ariaMessages.howTo)}
                 className={classNames(styles.menuBarItem, styles.hoverable)}
                 onClick={props.onOpenTipLibrary}
             >
@@ -369,17 +424,22 @@ MenuBar.propTypes = {
     editMenuOpen: PropTypes.bool,
     enableCommunity: PropTypes.bool,
     fileMenuOpen: PropTypes.bool,
+    intl: intlShape,
+    languageMenuOpen: PropTypes.bool,
     onClickEdit: PropTypes.func,
     onClickFile: PropTypes.func,
+    onClickLanguage: PropTypes.func,
     onOpenTipLibrary: PropTypes.func,
     onRequestCloseEdit: PropTypes.func,
     onRequestCloseFile: PropTypes.func,
+    onRequestCloseLanguage: PropTypes.func,
     onSeeCommunity: PropTypes.func
 };
 
 const mapStateToProps = state => ({
     fileMenuOpen: fileMenuOpen(state),
-    editMenuOpen: editMenuOpen(state)
+    editMenuOpen: editMenuOpen(state),
+    languageMenuOpen: languageMenuOpen(state)
 });
 
 const mapDispatchToProps = dispatch => ({
@@ -388,10 +448,12 @@ const mapDispatchToProps = dispatch => ({
     onRequestCloseFile: () => dispatch(closeFileMenu()),
     onClickEdit: () => dispatch(openEditMenu()),
     onRequestCloseEdit: () => dispatch(closeEditMenu()),
+    onClickLanguage: () => dispatch(openLanguageMenu()),
+    onRequestCloseLanguage: () => dispatch(closeLanguageMenu()),
     onSeeCommunity: () => dispatch(setPlayer(true))
 });
 
-export default connect(
+export default injectIntl(connect(
     mapStateToProps,
     mapDispatchToProps
-)(MenuBar);
+)(MenuBar));
diff --git a/src/components/modal/modal.css b/src/components/modal/modal.css
index 2a69d8ba63df54fa1973bcc98e8141dcbc499126..3aa21280beba80b61181349e5ae8a1bc38b52459 100644
--- a/src/components/modal/modal.css
+++ b/src/components/modal/modal.css
@@ -1,5 +1,6 @@
 @import "../../css/colors.css";
 @import "../../css/units.css";
+@import "../../css/z-index.css";
 
 .modal-overlay {
     position: fixed;
@@ -7,7 +8,7 @@
     left: 0;
     right: 0;
     bottom: 0;
-    z-index: 1000;
+    z-index: $z-index-modal;
     background-color: $ui-modal-overlay;
 }
 
diff --git a/src/components/monitor/monitor.css b/src/components/monitor/monitor.css
index f7337e90f6c9217f112f08169ee96332e0873aae..97a62e02d23916200db10035b79b42f2adc4aa3d 100644
--- a/src/components/monitor/monitor.css
+++ b/src/components/monitor/monitor.css
@@ -1,10 +1,11 @@
 @import "../../css/units.css";
 @import "../../css/colors.css";
+@import "../../css/z-index.css";
 
 .monitor-container {
     position: absolute;
     background: $ui-primary;
-    z-index: 100;
+    z-index: $z-index-monitor;
     border: 1px solid $ui-black-transparent;
     border-radius: calc($space / 2);
     font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
@@ -19,7 +20,7 @@
 }
 
 .dragging {
-    z-index: 101;
+    z-index: $z-index-monitor-dragging;
     box-shadow: 3px 3px 5px #888888;
 }
 
diff --git a/src/components/monitor/monitor.jsx b/src/components/monitor/monitor.jsx
index 77726194d14885371f269a79560728574c1f38e4..7be0875e5bc5960206ec5fa084c1350463bd3cae 100644
--- a/src/components/monitor/monitor.jsx
+++ b/src/components/monitor/monitor.jsx
@@ -1,4 +1,5 @@
 import React from 'react';
+import ReactDOM from 'react-dom';
 import PropTypes from 'prop-types';
 import Draggable from 'react-draggable';
 import {FormattedMessage} from 'react-intl';
@@ -52,7 +53,11 @@ const MonitorComponent = props => (
                 })}
             </Box>
         </Draggable>
-        {props.mode === 'list' ? null : (
+        {props.mode === 'list' ? null : ReactDOM.createPortal((
+            // Use a portal to render the context menu outside the flow to avoid
+            // positioning conflicts between the monitors `transform: scale` and
+            // the context menus `position: fixed`. For more details, see
+            // http://meyerweb.com/eric/thoughts/2011/09/12/un-fixing-fixed-elements-with-css-transforms/
             <ContextMenu id={`monitor-${props.label}`}>
                 <MenuItem onClick={props.onSetModeToDefault}>
                     <FormattedMessage
@@ -78,7 +83,7 @@ const MonitorComponent = props => (
                     </MenuItem>
                 ) : null}
             </ContextMenu>
-        )}
+        ), document.body)}
     </ContextMenuTrigger>
 
 );
diff --git a/src/components/preview-modal/preview-modal.css b/src/components/preview-modal/preview-modal.css
index 837ed92cfef69fd1ff77f9567483455f7c02d074..2c35acf63e0f78fbca407f74b063e691e105f2ec 100644
--- a/src/components/preview-modal/preview-modal.css
+++ b/src/components/preview-modal/preview-modal.css
@@ -1,6 +1,7 @@
 @import "../../css/colors.css";
 @import "../../css/units.css";
 @import "../../css/typography.css";
+@import "../../css/z-index.css";
 
 .modal-overlay {
     position: fixed;
@@ -8,7 +9,7 @@
     left: 0;
     right: 0;
     bottom: 0;
-    z-index: 1000;
+    z-index: $z-index-modal;
     background-color: $ui-modal-overlay;
 }
 
diff --git a/src/components/sprite-info/sprite-info.jsx b/src/components/sprite-info/sprite-info.jsx
index 7213277b4a265b2b1cb3e2100202da8941db3efe..09d5c96c7a0da7253f18b5652d22a4ae6d7c5272 100644
--- a/src/components/sprite-info/sprite-info.jsx
+++ b/src/components/sprite-info/sprite-info.jsx
@@ -1,7 +1,6 @@
 import classNames from 'classnames';
 import PropTypes from 'prop-types';
 import React from 'react';
-import MediaQuery from 'react-responsive';
 
 import Box from '../box/box.jsx';
 import Label from '../forms/label.jsx';
@@ -9,7 +8,7 @@ import Input from '../forms/input.jsx';
 import BufferedInputHOC from '../forms/buffered-input-hoc.jsx';
 import {injectIntl, intlShape, defineMessages, FormattedMessage} from 'react-intl';
 
-import layout from '../../lib/layout-constants.js';
+import {STAGE_DISPLAY_SIZES} from '../../lib/layout-constants.js';
 import styles from './sprite-info.css';
 
 import xIcon from './icon--x.svg';
@@ -34,12 +33,17 @@ class SpriteInfo extends React.Component {
             this.props.disabled !== nextProps.disabled ||
             this.props.name !== nextProps.name ||
             this.props.size !== nextProps.size ||
+            this.props.stageSize !== nextProps.stageSize ||
             this.props.visible !== nextProps.visible ||
             this.props.x !== nextProps.x ||
             this.props.y !== nextProps.y
         );
     }
     render () {
+        const {
+            stageSize
+        } = this.props;
+
         const sprite = (
             <FormattedMessage
                 defaultMessage="Sprite"
@@ -68,80 +72,110 @@ class SpriteInfo extends React.Component {
                 id="gui.SpriteInfo.direction"
             />
         );
-        return (
-            <Box
-                className={styles.spriteInfo}
-            >
-                <div className={classNames(styles.row, styles.rowPrimary)}>
-                    <div className={styles.group}>
-                        <Label text={sprite}>
-                            <BufferedInput
-                                className={styles.spriteInput}
-                                disabled={this.props.disabled}
-                                placeholder={this.props.intl.formatMessage(messages.spritePlaceholder)}
-                                tabIndex="0"
-                                type="text"
-                                value={this.props.disabled ? '' : this.props.name}
-                                onSubmit={this.props.onChangeName}
+
+        const spriteNameInput = (
+            <BufferedInput
+                className={styles.spriteInput}
+                disabled={this.props.disabled}
+                placeholder={this.props.intl.formatMessage(messages.spritePlaceholder)}
+                tabIndex="0"
+                type="text"
+                value={this.props.disabled ? '' : this.props.name}
+                onSubmit={this.props.onChangeName}
+            />
+        );
+
+        const xPosition = (
+            <div className={styles.group}>
+                {
+                    (stageSize === STAGE_DISPLAY_SIZES.large) ?
+                        <div className={styles.iconWrapper}>
+                            <img
+                                aria-hidden="true"
+                                className={classNames(styles.xIcon, styles.icon)}
+                                src={xIcon}
                             />
-                        </Label>
-                    </div>
+                        </div> :
+                        null
+                }
+                <Label text="x">
+                    <BufferedInput
+                        small
+                        disabled={this.props.disabled}
+                        placeholder="x"
+                        tabIndex="0"
+                        type="text"
+                        value={this.props.disabled ? '' : this.props.x}
+                        onSubmit={this.props.onChangeX}
+                    />
+                </Label>
+            </div>
+        );
 
-                    <div className={styles.group}>
-                        <MediaQuery minWidth={layout.fullSizeMinWidth}>
-                            <div className={styles.iconWrapper}>
-                                <img
-                                    aria-hidden="true"
-                                    className={classNames(styles.xIcon, styles.icon)}
-                                    src={xIcon}
-                                />
-                            </div>
-                        </MediaQuery>
-                        <Label text="x">
-                            <BufferedInput
-                                small
-                                disabled={this.props.disabled}
-                                placeholder="x"
-                                tabIndex="0"
-                                type="text"
-                                value={this.props.disabled ? '' : this.props.x}
-                                onSubmit={this.props.onChangeX}
+        const yPosition = (
+            <div className={styles.group}>
+                {
+                    (stageSize === STAGE_DISPLAY_SIZES.large) ?
+                        <div className={styles.iconWrapper}>
+                            <img
+                                aria-hidden="true"
+                                className={classNames(styles.yIcon, styles.icon)}
+                                src={yIcon}
                             />
-                        </Label>
+                        </div> :
+                        null
+                }
+                <Label text="y">
+                    <BufferedInput
+                        small
+                        disabled={this.props.disabled}
+                        placeholder="y"
+                        tabIndex="0"
+                        type="text"
+                        value={this.props.disabled ? '' : this.props.y}
+                        onSubmit={this.props.onChangeY}
+                    />
+                </Label>
+            </div>
+        );
+
+        if (stageSize === STAGE_DISPLAY_SIZES.small) {
+            return (
+                <Box className={styles.spriteInfo}>
+                    <div className={classNames(styles.row, styles.rowPrimary)}>
+                        <div className={styles.group}>
+                            {spriteNameInput}
+                        </div>
+                    </div>
+                    <div className={classNames(styles.row, styles.rowSecondary)}>
+                        {xPosition}
+                        {yPosition}
                     </div>
+                </Box>
+            );
+        }
 
+        return (
+            <Box className={styles.spriteInfo}>
+                <div className={classNames(styles.row, styles.rowPrimary)}>
                     <div className={styles.group}>
-                        <MediaQuery minWidth={layout.fullSizeMinWidth}>
-                            <div className={styles.iconWrapper}>
-                                <img
-                                    aria-hidden="true"
-                                    className={classNames(styles.yIcon, styles.icon)}
-                                    src={yIcon}
-                                />
-                            </div>
-                        </MediaQuery>
-                        <Label text="y">
-                            <BufferedInput
-                                small
-                                disabled={this.props.disabled}
-                                placeholder="y"
-                                tabIndex="0"
-                                type="text"
-                                value={this.props.disabled ? '' : this.props.y}
-                                onSubmit={this.props.onChangeY}
-                            />
+                        <Label text={sprite}>
+                            {spriteNameInput}
                         </Label>
                     </div>
+                    {xPosition}
+                    {yPosition}
                 </div>
-
                 <div className={classNames(styles.row, styles.rowSecondary)}>
                     <div className={styles.group}>
-                        <MediaQuery minWidth={layout.fullSizeMinWidth}>
-                            <Label
-                                secondary
-                                text={showLabel}
-                            />
-                        </MediaQuery>
+                        {
+                            stageSize === STAGE_DISPLAY_SIZES.large ?
+                                <Label
+                                    secondary
+                                    text={showLabel}
+                                /> :
+                                null
+                        }
                         <div>
                             <div
                                 className={classNames(
@@ -242,6 +276,7 @@ SpriteInfo.propTypes = {
         PropTypes.string,
         PropTypes.number
     ]),
+    stageSize: PropTypes.oneOf(Object.keys(STAGE_DISPLAY_SIZES)).isRequired,
     visible: PropTypes.bool,
     x: PropTypes.oneOfType([
         PropTypes.string,
diff --git a/src/components/sprite-selector-item/sprite-selector-item.css b/src/components/sprite-selector-item/sprite-selector-item.css
index ed95f7134a3e93480a17e4d984aa45253d663b44..b54e8f720743d4559b1b4e0bb4604233835a7dc4 100644
--- a/src/components/sprite-selector-item/sprite-selector-item.css
+++ b/src/components/sprite-selector-item/sprite-selector-item.css
@@ -19,6 +19,8 @@
     text-align: center;
     cursor: pointer;
     transition: 0.25s ease-out;
+
+    user-select: none;
 }
 
 .sprite-selector-item.is-selected {
diff --git a/src/components/sprite-selector/sprite-list.jsx b/src/components/sprite-selector/sprite-list.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..f36a2f1f5ac07a0f5b30cf65d9338281b389faa0
--- /dev/null
+++ b/src/components/sprite-selector/sprite-list.jsx
@@ -0,0 +1,108 @@
+import PropTypes from 'prop-types';
+import React from 'react';
+import classNames from 'classnames';
+
+import DragConstants from '../../lib/drag-constants';
+
+import Box from '../box/box.jsx';
+import SpriteSelectorItem from '../../containers/sprite-selector-item.jsx';
+import SortableHOC from '../../lib/sortable-hoc.jsx';
+import SortableAsset from '../asset-panel/sortable-asset.jsx';
+
+import styles from './sprite-selector.css';
+
+const SpriteList = function (props) {
+    const {
+        editingTarget,
+        draggingIndex,
+        draggingType,
+        hoveredTarget,
+        onDeleteSprite,
+        onDuplicateSprite,
+        onSelectSprite,
+        onAddSortable,
+        onRemoveSortable,
+        ordering,
+        raised,
+        selectedId,
+        items
+    } = props;
+
+    const isSpriteDrag = draggingType === DragConstants.SPRITE;
+
+    return (
+        <Box className={styles.itemsWrapper}>
+            {items.map((sprite, index) => {
+
+                // If the sprite has just received a block drop, used for green highlight
+                const receivedBlocks = (
+                    hoveredTarget.sprite === sprite.id &&
+                    sprite.id !== editingTarget &&
+                    hoveredTarget.receivedBlocks
+                );
+
+                // If the sprite is indicating it can receive block dropping, used for blue highlight
+                const isRaised = !receivedBlocks && raised && sprite.id !== editingTarget;
+
+                return (
+                    <SortableAsset
+                        className={classNames(styles.itemWrapper, {
+                            [styles.placeholder]: isSpriteDrag && index === draggingIndex})}
+                        index={isSpriteDrag ? ordering.indexOf(index) : index}
+                        key={sprite.name}
+                        onAddSortable={onAddSortable}
+                        onRemoveSortable={onRemoveSortable}
+                    >
+                        <SpriteSelectorItem
+                            assetId={sprite.costume && sprite.costume.assetId}
+                            className={classNames(styles.sprite, {
+                                [styles.raised]: isRaised,
+                                [styles.receivedBlocks]: receivedBlocks
+                            })}
+                            dragType={DragConstants.SPRITE}
+                            id={sprite.id}
+                            index={index}
+                            key={sprite.id}
+                            name={sprite.name}
+                            selected={sprite.id === selectedId}
+                            onClick={onSelectSprite}
+                            onDeleteButtonClick={onDeleteSprite}
+                            onDuplicateButtonClick={onDuplicateSprite}
+                        />
+                    </SortableAsset>
+                );
+            })}
+        </Box>
+    );
+};
+
+SpriteList.propTypes = {
+    draggingIndex: PropTypes.number,
+    draggingType: PropTypes.oneOf(Object.keys(DragConstants)),
+    editingTarget: PropTypes.string,
+    hoveredTarget: PropTypes.shape({
+        hoveredSprite: PropTypes.string,
+        receivedBlocks: PropTypes.bool
+    }),
+    items: PropTypes.arrayOf(PropTypes.shape({
+        costume: PropTypes.shape({
+            url: PropTypes.string,
+            name: PropTypes.string.isRequired,
+            bitmapResolution: PropTypes.number.isRequired,
+            rotationCenterX: PropTypes.number.isRequired,
+            rotationCenterY: PropTypes.number.isRequired
+        }),
+        name: PropTypes.string.isRequired,
+        order: PropTypes.number.isRequired
+    })),
+    onAddSortable: PropTypes.func,
+    onDeleteSprite: PropTypes.func,
+    onDuplicateSprite: PropTypes.func,
+    onRemoveSortable: PropTypes.func,
+    onSelectSprite: PropTypes.func,
+    ordering: PropTypes.arrayOf(PropTypes.number),
+    raised: PropTypes.bool,
+    selectedId: PropTypes.string
+};
+
+export default SortableHOC(SpriteList);
diff --git a/src/components/sprite-selector/sprite-selector.css b/src/components/sprite-selector/sprite-selector.css
index 8b1e76df56f5788c24803ae2a9b50e680e5285a9..cbd785d82f7fee6bcc7daef090f8407ab2b60ddb 100644
--- a/src/components/sprite-selector/sprite-selector.css
+++ b/src/components/sprite-selector/sprite-selector.css
@@ -1,5 +1,6 @@
 @import "../../css/colors.css";
 @import "../../css/units.css";
+@import "../../css/z-index.css";
 
 .sprite-selector {
     flex-grow: 1;
@@ -47,7 +48,7 @@
               then back up, introduces white space in the outside the page container.
     */
     height: calc(100% - $sprite-info-height);
-    overflow-y: scroll;
+    overflow-y: auto;
 }
 
 .items-wrapper {
@@ -65,7 +66,7 @@
     position: absolute;
     bottom: 0.75rem;
     right: 1rem;
-    z-index: 1; /* TODO overlaps the stage, this doesn't work, fix! */
+    z-index: $z-index-add-button;
 }
 
 .raised {
@@ -103,3 +104,8 @@
     90% { box-shadow: 0 0 10px #7fff1e; }
     100% { box-shadow: none; }
 }
+
+.placeholder > .sprite {
+    background: black;
+    filter: opacity(15%) brightness(20%);
+}
diff --git a/src/components/sprite-selector/sprite-selector.jsx b/src/components/sprite-selector/sprite-selector.jsx
index 21cef41e9a6c66b2072b2625d6ad3f1a828f7154..9907dffce3a78d58c056ce74ab33a0c59e2b4c10 100644
--- a/src/components/sprite-selector/sprite-selector.jsx
+++ b/src/components/sprite-selector/sprite-selector.jsx
@@ -1,12 +1,13 @@
 import PropTypes from 'prop-types';
 import React from 'react';
-import classNames from 'classnames';
 import {defineMessages, injectIntl, intlShape} from 'react-intl';
 
+
 import Box from '../box/box.jsx';
 import SpriteInfo from '../../containers/sprite-info.jsx';
-import SpriteSelectorItem from '../../containers/sprite-selector-item.jsx';
+import SpriteList from './sprite-list.jsx';
 import ActionMenu from '../action-menu/action-menu.jsx';
+import {STAGE_DISPLAY_SIZES} from '../../lib/layout-constants';
 
 import styles from './sprite-selector.css';
 
@@ -49,6 +50,7 @@ const SpriteSelectorComponent = function (props) {
         onChangeSpriteVisibility,
         onChangeSpriteX,
         onChangeSpriteY,
+        onDrop,
         onDeleteSprite,
         onDuplicateSprite,
         onFileUploadClick,
@@ -61,6 +63,7 @@ const SpriteSelectorComponent = function (props) {
         selectedId,
         spriteFileInput,
         sprites,
+        stageSize,
         ...componentProps
     } = props;
     let selectedSprite = sprites[selectedId];
@@ -80,6 +83,7 @@ const SpriteSelectorComponent = function (props) {
                 disabled={spriteInfoDisabled}
                 name={selectedSprite.name}
                 size={selectedSprite.size}
+                stageSize={stageSize}
                 visible={selectedSprite.visible}
                 x={selectedSprite.x}
                 y={selectedSprite.y}
@@ -92,31 +96,17 @@ const SpriteSelectorComponent = function (props) {
             />
 
             <Box className={styles.scrollWrapper}>
-                <Box className={styles.itemsWrapper}>
-                    {Object.keys(sprites)
-                        // Re-order by list order
-                        .sort((id1, id2) => sprites[id1].order - sprites[id2].order)
-                        .map(id => sprites[id])
-                        .map(sprite => (
-                            <SpriteSelectorItem
-                                assetId={sprite.costume && sprite.costume.assetId}
-                                className={hoveredTarget.sprite === sprite.id &&
-                                    sprite.id !== editingTarget &&
-                                    hoveredTarget.receivedBlocks ?
-                                    classNames(styles.sprite, styles.receivedBlocks) :
-                                    raised && sprite.id !== editingTarget ?
-                                        classNames(styles.sprite, styles.raised) : styles.sprite}
-                                id={sprite.id}
-                                key={sprite.id}
-                                name={sprite.name}
-                                selected={sprite.id === selectedId}
-                                onClick={onSelectSprite}
-                                onDeleteButtonClick={onDeleteSprite}
-                                onDuplicateButtonClick={onDuplicateSprite}
-                            />
-                        ))
-                    }
-                </Box>
+                <SpriteList
+                    editingTarget={editingTarget}
+                    hoveredTarget={hoveredTarget}
+                    items={Object.keys(sprites).map(id => sprites[id])}
+                    raised={raised}
+                    selectedId={selectedId}
+                    onDeleteSprite={onDeleteSprite}
+                    onDrop={onDrop}
+                    onDuplicateSprite={onDuplicateSprite}
+                    onSelectSprite={onSelectSprite}
+                />
             </Box>
             <ActionMenu
                 className={styles.addButton}
@@ -160,6 +150,7 @@ SpriteSelectorComponent.propTypes = {
     onChangeSpriteX: PropTypes.func,
     onChangeSpriteY: PropTypes.func,
     onDeleteSprite: PropTypes.func,
+    onDrop: PropTypes.func,
     onDuplicateSprite: PropTypes.func,
     onFileUploadClick: PropTypes.func,
     onNewSpriteClick: PropTypes.func,
@@ -182,7 +173,8 @@ SpriteSelectorComponent.propTypes = {
             name: PropTypes.string.isRequired,
             order: PropTypes.number.isRequired
         })
-    })
+    }),
+    stageSize: PropTypes.oneOf(Object.keys(STAGE_DISPLAY_SIZES)).isRequired
 };
 
 export default injectIntl(SpriteSelectorComponent);
diff --git a/src/components/stage-header/icon--small-stage.svg b/src/components/stage-header/icon--small-stage.svg
index f73f5b242583675de267ad84dde99a7173a7e032..377939959cf7ec1abab502c18c324e8203db0d47 100644
Binary files a/src/components/stage-header/icon--small-stage.svg and b/src/components/stage-header/icon--small-stage.svg differ
diff --git a/src/components/stage-header/stage-header.css b/src/components/stage-header/stage-header.css
index 2ac722efc5579336874b97e565ecedaab235e213..93ef5b4408a1d701fc624a0f41b9ad994844dd58 100644
--- a/src/components/stage-header/stage-header.css
+++ b/src/components/stage-header/stage-header.css
@@ -1,5 +1,6 @@
 @import "../../css/units.css";
 @import "../../css/colors.css";
+@import "../../css/z-index.css";
 
 .stage-header-wrapper {
     position: relative;
@@ -11,7 +12,7 @@
     top: 0;
     left: 0;
     right: 0;
-    z-index: 5000;
+    z-index: $z-index-stage-header;
 }
 
 .stage-menu-wrapper {
@@ -61,8 +62,6 @@
     border-bottom-right-radius: 0;
 }
 
-.stage-button-disabled {
-    opacity: .5;
-    cursor: auto;
-    background: transparent;
+.stage-button-toggled-off {
+    filter: saturate(0);
 }
diff --git a/src/components/stage-header/stage-header.jsx b/src/components/stage-header/stage-header.jsx
index efada70ed553a7321e250bd22229789a9d6697ad..21a156a95a6add7ff7caa60b21aa23c28b04b6d7 100644
--- a/src/components/stage-header/stage-header.jsx
+++ b/src/components/stage-header/stage-header.jsx
@@ -2,13 +2,14 @@ import classNames from 'classnames';
 import {defineMessages, injectIntl, intlShape} from 'react-intl';
 import PropTypes from 'prop-types';
 import React from 'react';
+import {connect} from 'react-redux';
 import VM from 'scratch-vm';
 
 import Box from '../box/box.jsx';
 import Button from '../button/button.jsx';
-import {ComingSoonTooltip} from '../coming-soon/coming-soon.jsx';
 import Controls from '../../containers/controls.jsx';
-import {getStageSize} from '../../lib/screen-utils.js';
+import {getStageDimensions} from '../../lib/screen-utils';
+import {STAGE_SIZE_MODES} from '../../lib/layout-constants';
 
 import fullScreenIcon from './icon--fullscreen.svg';
 import largeStageIcon from './icon--large-stage.svg';
@@ -51,20 +52,22 @@ const StageHeaderComponent = function (props) {
         isPlayerOnly,
         onKeyPress,
         onSetStageLarge,
+        onSetStageSmall,
         onSetStageFull,
         onSetStageUnFull,
+        stageSizeMode,
         vm
     } = props;
 
     let header = null;
-    const stageSize = getStageSize(isFullScreen);
 
     if (isFullScreen) {
+        const stageDimensions = getStageDimensions(null, true);
         header = (
             <Box className={styles.stageHeaderWrapperOverlay}>
                 <Box
                     className={styles.stageMenuWrapper}
-                    style={{width: stageSize.width}}
+                    style={{width: stageDimensions.width}}
                 >
                     <Controls vm={vm} />
                     <Button
@@ -89,31 +92,30 @@ const StageHeaderComponent = function (props) {
                 []
             ) : (
                 <div className={styles.stageSizeToggleGroup}>
-                    <ComingSoonTooltip
-                        place="left"
-                        tooltipId="small-stage-button"
-                    >
-                        <div
-                            disabled
+                    <div>
+                        <Button
                             className={classNames(
                                 styles.stageButton,
                                 styles.stageButtonLeft,
-                                styles.stageButtonDisabled
+                                (stageSizeMode === STAGE_SIZE_MODES.small) ? null : styles.stageButtonToggledOff
                             )}
-                            role="button"
+                            onClick={onSetStageSmall}
                         >
                             <img
-                                disabled
                                 alt={props.intl.formatMessage(messages.smallStageSizeMessage)}
                                 className={styles.stageButtonIcon}
                                 draggable={false}
                                 src={smallStageIcon}
                             />
-                        </div>
-                    </ComingSoonTooltip>
+                        </Button>
+                    </div>
                     <div>
                         <Button
-                            className={classNames(styles.stageButton, styles.stageButtonRight)}
+                            className={classNames(
+                                styles.stageButton,
+                                styles.stageButtonRight,
+                                (stageSizeMode === STAGE_SIZE_MODES.large) ? null : styles.stageButtonToggledOff
+                            )}
                             onClick={onSetStageLarge}
                         >
                             <img
@@ -155,6 +157,11 @@ const StageHeaderComponent = function (props) {
     return header;
 };
 
+const mapStateToProps = state => ({
+    // This is the button's mode, as opposed to the actual current state
+    stageSizeMode: state.scratchGui.stageSize.stageSize
+});
+
 StageHeaderComponent.propTypes = {
     intl: intlShape,
     isFullScreen: PropTypes.bool.isRequired,
@@ -162,8 +169,16 @@ StageHeaderComponent.propTypes = {
     onKeyPress: PropTypes.func.isRequired,
     onSetStageFull: PropTypes.func.isRequired,
     onSetStageLarge: PropTypes.func.isRequired,
+    onSetStageSmall: PropTypes.func.isRequired,
     onSetStageUnFull: PropTypes.func.isRequired,
+    stageSizeMode: PropTypes.oneOf(Object.keys(STAGE_SIZE_MODES)),
     vm: PropTypes.instanceOf(VM).isRequired
 };
 
-export default injectIntl(StageHeaderComponent);
+StageHeaderComponent.defaultProps = {
+    stageSizeMode: STAGE_SIZE_MODES.large
+};
+
+export default injectIntl(connect(
+    mapStateToProps
+)(StageHeaderComponent));
diff --git a/src/components/stage-selector/stage-selector.css b/src/components/stage-selector/stage-selector.css
index b8ae410bb5e4c338a26a72045c9de7283344f1cd..e845160faee34e2c5768d08883f121079ad06275 100644
--- a/src/components/stage-selector/stage-selector.css
+++ b/src/components/stage-selector/stage-selector.css
@@ -1,5 +1,6 @@
 @import "../../css/units.css";
 @import "../../css/colors.css";
+@import "../../css/z-index.css";
 
 $header-height: calc($stage-menu-height - 2px);
 
@@ -79,6 +80,7 @@ $header-height: calc($stage-menu-height - 2px);
 .add-button {
     position: absolute;
     bottom: 0.75rem;
+    z-index: $z-index-add-button
 }
 
 .raised, .raised .header {
diff --git a/src/components/stage-wrapper/stage-wrapper.jsx b/src/components/stage-wrapper/stage-wrapper.jsx
index 8cec64e40a9ffdd74d617e2bb611a4637e6e6b6b..eb0d129f82cc6bcbdae4aca55365e4d8ef78cef6 100644
--- a/src/components/stage-wrapper/stage-wrapper.jsx
+++ b/src/components/stage-wrapper/stage-wrapper.jsx
@@ -1,10 +1,9 @@
 import PropTypes from 'prop-types';
 import React from 'react';
-import MediaQuery from 'react-responsive';
 import VM from 'scratch-vm';
 
 import Box from '../box/box.jsx';
-import layout from '../../lib/layout-constants.js';
+import {STAGE_DISPLAY_SIZES} from '../../lib/layout-constants.js';
 import StageHeader from '../../containers/stage-header.jsx';
 import Stage from '../../containers/stage.jsx';
 
@@ -13,27 +12,28 @@ import styles from './stage-wrapper.css';
 const StageWrapperComponent = function (props) {
     const {
         isRendererSupported,
+        stageSize,
         vm
     } = props;
 
     return (
         <Box className={styles.stageWrapper}>
             <Box className={styles.stageMenuWrapper}>
-                <StageHeader vm={vm} />
+                <StageHeader
+                    stageSize={stageSize}
+                    vm={vm}
+                />
             </Box>
             <Box className={styles.stageCanvasWrapper}>
-                {/* eslint-disable arrow-body-style */}
-                <MediaQuery minWidth={layout.fullSizeMinWidth}>{isFullSize => {
-                    return isRendererSupported ? (
+                {
+                    isRendererSupported ?
                         <Stage
-                            height={isFullSize ? layout.fullStageHeight : layout.smallerStageHeight}
                             shrink={0}
+                            stageSize={stageSize}
                             vm={vm}
-                            width={isFullSize ? layout.fullStageWidth : layout.smallerStageWidth}
-                        />
-                    ) : null;
-                }}</MediaQuery>
-                {/* eslint-enable arrow-body-style */}
+                        /> :
+                        null
+                }
             </Box>
         </Box>
     );
@@ -41,6 +41,7 @@ const StageWrapperComponent = function (props) {
 
 StageWrapperComponent.propTypes = {
     isRendererSupported: PropTypes.bool.isRequired,
+    stageSize: PropTypes.oneOf(Object.keys(STAGE_DISPLAY_SIZES)).isRequired,
     vm: PropTypes.instanceOf(VM).isRequired
 };
 
diff --git a/src/components/stage/stage.css b/src/components/stage/stage.css
index d2c7894c7832a935b6794a0784a3a89afa58dda3..15ac8bcc1d0267d12903ebf472b4273a75401b9d 100644
--- a/src/components/stage/stage.css
+++ b/src/components/stage/stage.css
@@ -1,5 +1,6 @@
 @import "../../css/units.css";
 @import "../../css/colors.css";
+@import "../../css/z-index.css";
 
 .stage {
     /*
@@ -21,7 +22,7 @@
 
 .with-color-picker {
     cursor: none;
-    z-index: 2001;
+    z-index: $z-index-stage-with-color-picker;
 }
 
 .color-picker-background {
@@ -30,7 +31,7 @@
     height: 100%;
     background-color: rgba(0, 0, 0, 0.55);
     display: block;
-    z-index: 2000;
+    z-index: $z-index-stage-color-picker-background;
     top: 0;
     left: 0;
 }
@@ -45,7 +46,7 @@
     left: 0;
     right: 0;
     bottom: 0;
-    z-index: 5000;
+    z-index: $z-index-stage-wrapper-overlay;
     background-color: $ui-white;
 }
 
@@ -86,6 +87,6 @@
     position: absolute;
     top: 0;
     left: 0;
-    z-index: 1000; /* Above everything so it is draggable into other panes */
+    z-index: $z-index-dragging-sprite;
     filter: drop-shadow(5px 5px 5px $ui-black-transparent);
  }
diff --git a/src/components/stage/stage.jsx b/src/components/stage/stage.jsx
index 658114a608a69757c746b3f64da2ac43c6ce1ad3..37b8bced0b49b77d680557e42dd27fd1ce45b034 100644
--- a/src/components/stage/stage.jsx
+++ b/src/components/stage/stage.jsx
@@ -6,26 +6,26 @@ import Box from '../box/box.jsx';
 import Loupe from '../loupe/loupe.jsx';
 import MonitorList from '../../containers/monitor-list.jsx';
 import Question from '../../containers/question.jsx';
-import {getStageSize} from '../../lib/screen-utils.js';
+import {STAGE_DISPLAY_SIZES} from '../../lib/layout-constants.js';
+import {getStageDimensions} from '../../lib/screen-utils.js';
 import styles from './stage.css';
 
 const StageComponent = props => {
     const {
         canvasRef,
         dragRef,
-        height,
         isColorPicking,
         isFullScreen,
-        width,
         colorInfo,
-        onDeactivateColorPicker,
         question,
-        onQuestionAnswered,
+        stageSize,
         useEditorDragStyle,
+        onDeactivateColorPicker,
+        onQuestionAnswered,
         ...boxProps
     } = props;
 
-    const stageSize = getStageSize(isFullScreen, height, width);
+    const stageDimensions = getStageDimensions(stageSize, isFullScreen);
 
     return (
         <div>
@@ -43,14 +43,14 @@ const StageComponent = props => {
                     )}
                     componentRef={canvasRef}
                     element="canvas"
-                    height={stageSize.height}
-                    width={stageSize.width}
+                    height={stageDimensions.height}
+                    width={stageDimensions.width}
                     {...boxProps}
                 />
                 <Box className={styles.monitorWrapper}>
                     <MonitorList
                         draggable={useEditorDragStyle}
-                        stageSize={stageSize}
+                        stageSize={stageDimensions}
                     />
                 </Box>
                 {isColorPicking && colorInfo ? (
@@ -67,7 +67,7 @@ const StageComponent = props => {
                     >
                         <div
                             className={styles.questionWrapper}
-                            style={{width: stageSize.width}}
+                            style={{width: stageDimensions.width}}
                         >
                             <Question
                                 question={question}
@@ -96,19 +96,16 @@ StageComponent.propTypes = {
     canvasRef: PropTypes.func,
     colorInfo: Loupe.propTypes.colorInfo,
     dragRef: PropTypes.func,
-    height: PropTypes.number,
     isColorPicking: PropTypes.bool,
     isFullScreen: PropTypes.bool.isRequired,
     onDeactivateColorPicker: PropTypes.func,
     onQuestionAnswered: PropTypes.func,
     question: PropTypes.string,
-    useEditorDragStyle: PropTypes.bool,
-    width: PropTypes.number
+    stageSize: PropTypes.oneOf(Object.keys(STAGE_DISPLAY_SIZES)).isRequired,
+    useEditorDragStyle: PropTypes.bool
 };
 StageComponent.defaultProps = {
     canvasRef: () => {},
-    dragRef: () => {},
-    width: 480,
-    height: 360
+    dragRef: () => {}
 };
 export default StageComponent;
diff --git a/src/components/target-pane/target-pane.jsx b/src/components/target-pane/target-pane.jsx
index fb08f3b413c8da8105a40309571c4c5a95cb383b..57b10e0c99e72d4df2e28a364f82c25c66547b66 100644
--- a/src/components/target-pane/target-pane.jsx
+++ b/src/components/target-pane/target-pane.jsx
@@ -6,6 +6,7 @@ import VM from 'scratch-vm';
 import SpriteLibrary from '../../containers/sprite-library.jsx';
 import SpriteSelectorComponent from '../sprite-selector/sprite-selector.jsx';
 import StageSelector from '../../containers/stage-selector.jsx';
+import {STAGE_DISPLAY_SIZES} from '../../lib/layout-constants';
 
 import styles from './target-pane.css';
 
@@ -27,6 +28,7 @@ const TargetPane = ({
     onChangeSpriteX,
     onChangeSpriteY,
     onDeleteSprite,
+    onDrop,
     onDuplicateSprite,
     onFileUploadClick,
     onNewSpriteClick,
@@ -37,6 +39,7 @@ const TargetPane = ({
     onSurpriseSpriteClick,
     raiseSprites,
     stage,
+    stageSize,
     sprites,
     vm,
     ...componentProps
@@ -53,6 +56,7 @@ const TargetPane = ({
             selectedId={editingTarget}
             spriteFileInput={fileInputRef}
             sprites={sprites}
+            stageSize={stageSize}
             onChangeSpriteDirection={onChangeSpriteDirection}
             onChangeSpriteName={onChangeSpriteName}
             onChangeSpriteSize={onChangeSpriteSize}
@@ -60,6 +64,7 @@ const TargetPane = ({
             onChangeSpriteX={onChangeSpriteX}
             onChangeSpriteY={onChangeSpriteY}
             onDeleteSprite={onDeleteSprite}
+            onDrop={onDrop}
             onDuplicateSprite={onDuplicateSprite}
             onFileUploadClick={onFileUploadClick}
             onNewSpriteClick={onNewSpriteClick}
@@ -126,6 +131,7 @@ TargetPane.propTypes = {
     onChangeSpriteX: PropTypes.func,
     onChangeSpriteY: PropTypes.func,
     onDeleteSprite: PropTypes.func,
+    onDrop: PropTypes.func,
     onDuplicateSprite: PropTypes.func,
     onFileUploadClick: PropTypes.func,
     onNewSpriteClick: PropTypes.func,
@@ -139,6 +145,7 @@ TargetPane.propTypes = {
     spriteLibraryVisible: PropTypes.bool,
     sprites: PropTypes.objectOf(spriteShape),
     stage: spriteShape,
+    stageSize: PropTypes.oneOf(Object.keys(STAGE_DISPLAY_SIZES)).isRequired,
     vm: PropTypes.instanceOf(VM)
 };
 
diff --git a/src/components/webgl-modal/webgl-modal.css b/src/components/webgl-modal/webgl-modal.css
index 334ef8f685adb061cea151422056edb422d26d49..f1b6ff6efee57e7a40a5ad1a1ebc3325e8167dcd 100644
--- a/src/components/webgl-modal/webgl-modal.css
+++ b/src/components/webgl-modal/webgl-modal.css
@@ -1,6 +1,7 @@
 @import "../../css/colors.css";
 @import "../../css/units.css";
 @import "../../css/typography.css";
+@import "../../css/z-index.css";
 
 .modal-overlay {
     position: fixed;
@@ -8,7 +9,7 @@
     left: 0;
     right: 0;
     bottom: 0;
-    z-index: 1000;
+    z-index: $z-index-modal;
     background-color: $ui-modal-overlay;
 }
 
diff --git a/src/containers/backdrop-library.jsx b/src/containers/backdrop-library.jsx
index 98604bf18918ee3c30fd687d53f2820a5a7e8a15..5a02660385dcac2232b7598ac4483c3a31413923 100644
--- a/src/containers/backdrop-library.jsx
+++ b/src/containers/backdrop-library.jsx
@@ -2,8 +2,14 @@ import bindAll from 'lodash.bindall';
 import PropTypes from 'prop-types';
 import React from 'react';
 import {defineMessages, injectIntl, intlShape} from 'react-intl';
+import {connect} from 'react-redux';
 import VM from 'scratch-vm';
 
+import {
+    activateTab,
+    COSTUMES_TAB_INDEX
+} from '../reducers/editor-tab';
+
 import analytics from '../lib/analytics';
 import backdropLibraryContent from '../lib/libraries/backdrops.json';
 import backdropTags from '../lib/libraries/backdrop-tags';
@@ -33,6 +39,8 @@ class BackdropLibrary extends React.Component {
             bitmapResolution: item.info.length > 2 ? item.info[2] : 1,
             skinId: null
         };
+        this.props.vm.setEditingTarget(this.props.stageID);
+        this.props.onActivateTab(COSTUMES_TAB_INDEX);
         this.props.vm.addBackdrop(item.md5, vmBackdrop);
         analytics.event({
             category: 'library',
@@ -56,8 +64,21 @@ class BackdropLibrary extends React.Component {
 
 BackdropLibrary.propTypes = {
     intl: intlShape.isRequired,
+    onActivateTab: PropTypes.func.isRequired,
     onRequestClose: PropTypes.func,
+    stageID: PropTypes.string.isRequired,
     vm: PropTypes.instanceOf(VM).isRequired
 };
 
-export default injectIntl(BackdropLibrary);
+const mapStateToProps = state => ({
+    stageID: state.scratchGui.targets.stage.id
+});
+
+const mapDispatchToProps = dispatch => ({
+    onActivateTab: tab => dispatch(activateTab(tab))
+});
+
+export default injectIntl(connect(
+    mapStateToProps,
+    mapDispatchToProps
+)(BackdropLibrary));
diff --git a/src/containers/blocks.jsx b/src/containers/blocks.jsx
index ac1fd3ee3b9470b7cfa93752bca176c193521e56..5f1ec793e63b7a153ce6a628480a56b04bfbeca0 100644
--- a/src/containers/blocks.jsx
+++ b/src/containers/blocks.jsx
@@ -13,6 +13,7 @@ import BlocksComponent from '../components/blocks/blocks.jsx';
 import ExtensionLibrary from './extension-library.jsx';
 import CustomProcedures from './custom-procedures.jsx';
 import errorBoundaryHOC from '../lib/error-boundary-hoc.jsx';
+import {STAGE_DISPLAY_SIZES} from '../lib/layout-constants';
 
 import {connect} from 'react-redux';
 import {updateToolbox} from '../reducers/toolbox';
@@ -97,7 +98,8 @@ class Blocks extends React.Component {
             this.props.extensionLibraryVisible !== nextProps.extensionLibraryVisible ||
             this.props.customProceduresVisible !== nextProps.customProceduresVisible ||
             this.props.locale !== nextProps.locale ||
-            this.props.anyModalVisible !== nextProps.anyModalVisible
+            this.props.anyModalVisible !== nextProps.anyModalVisible ||
+            this.props.stageSize !== nextProps.stageSize
         );
     }
     componentDidUpdate (prevProps) {
@@ -118,6 +120,10 @@ class Blocks extends React.Component {
             }, 0);
         }
         if (this.props.isVisible === prevProps.isVisible) {
+            if (this.props.stageSize !== prevProps.stageSize) {
+                // force workspace to redraw for the new stage size
+                window.dispatchEvent(new Event('resize'));
+            }
             return;
         }
         // @todo hack to resize blockly manually in case resize happened while hidden
@@ -329,6 +335,7 @@ class Blocks extends React.Component {
             customProceduresVisible,
             extensionLibraryVisible,
             options,
+            stageSize,
             vm,
             isVisible,
             onActivateColorPicker,
@@ -409,6 +416,7 @@ Blocks.propTypes = {
         comments: PropTypes.bool,
         collapse: PropTypes.bool
     }),
+    stageSize: PropTypes.oneOf(Object.keys(STAGE_DISPLAY_SIZES)).isRequired,
     toolboxXML: PropTypes.string,
     updateToolboxState: PropTypes.func,
     vm: PropTypes.instanceOf(VM).isRequired
diff --git a/src/containers/costume-tab.jsx b/src/containers/costume-tab.jsx
index 3ce034caa200b15cac7e7f7bc8042fdf9843d2f3..965f81abef7b99417f7f521d932cd6460dbd80c9 100644
--- a/src/containers/costume-tab.jsx
+++ b/src/containers/costume-tab.jsx
@@ -6,17 +6,14 @@ import VM from 'scratch-vm';
 
 import AssetPanel from '../components/asset-panel/asset-panel.jsx';
 import PaintEditorWrapper from './paint-editor-wrapper.jsx';
-import CostumeLibrary from './costume-library.jsx';
-import BackdropLibrary from './backdrop-library.jsx';
 import CameraModal from './camera-modal.jsx';
 import {connect} from 'react-redux';
 import {handleFileUpload, costumeUpload} from '../lib/file-uploader.js';
 import errorBoundaryHOC from '../lib/error-boundary-hoc.jsx';
+import DragConstants from '../lib/drag-constants';
 
 import {
     closeCameraCapture,
-    closeCostumeLibrary,
-    closeBackdropLibrary,
     openCameraCapture,
     openCostumeLibrary,
     openBackdropLibrary
@@ -84,6 +81,7 @@ class CostumeTab extends React.Component {
             'handleFileUploadClick',
             'handleCostumeUpload',
             'handleCameraBuffer',
+            'handleDrop',
             'setFileInput'
         ]);
         const {
@@ -192,6 +190,17 @@ class CostumeTab extends React.Component {
     handleFileUploadClick () {
         this.fileInput.click();
     }
+    handleDrop (dropInfo) {
+        // Eventually will handle other kinds of drop events, right now just
+        // the reordering events.
+        if (dropInfo.dragType === DragConstants.COSTUME) {
+            const sprite = this.props.vm.editingTarget.sprite;
+            const activeCostume = sprite.costumes[this.state.selectedCostumeIndex];
+            this.props.vm.reorderCostume(this.props.vm.editingTarget.id,
+                dropInfo.index, dropInfo.newIndex);
+            this.setState({selectedCostumeIndex: sprite.costumes.indexOf(activeCostume)});
+        }
+    }
     setFileInput (input) {
         this.fileInput = input;
     }
@@ -209,36 +218,29 @@ class CostumeTab extends React.Component {
             onNewCostumeFromCameraClick,
             onNewLibraryBackdropClick,
             onNewLibraryCostumeClick,
-            backdropLibraryVisible,
             cameraModalVisible,
-            costumeLibraryVisible,
-            onRequestCloseBackdropLibrary,
             onRequestCloseCameraModal,
-            onRequestCloseCostumeLibrary,
-            editingTarget,
-            sprites,
-            stage,
             vm
         } = this.props;
 
-        const target = editingTarget && sprites[editingTarget] ? sprites[editingTarget] : stage;
-
-        if (!target) {
+        if (!vm.editingTarget) {
             return null;
         }
 
-        const addLibraryMessage = target.isStage ? messages.addLibraryBackdropMsg : messages.addLibraryCostumeMsg;
-        const addFileMessage = target.isStage ? messages.addFileBackdropMsg : messages.addFileCostumeMsg;
-        const addSurpriseFunc = target.isStage ? this.handleSurpriseBackdrop : this.handleSurpriseCostume;
-        const addLibraryFunc = target.isStage ? onNewLibraryBackdropClick : onNewLibraryCostumeClick;
-        const addLibraryIcon = target.isStage ? addLibraryBackdropIcon : addLibraryCostumeIcon;
+        const isStage = vm.editingTarget.isStage;
+        const target = vm.editingTarget.sprite;
+
+        const addLibraryMessage = isStage ? messages.addLibraryBackdropMsg : messages.addLibraryCostumeMsg;
+        const addFileMessage = isStage ? messages.addFileBackdropMsg : messages.addFileCostumeMsg;
+        const addSurpriseFunc = isStage ? this.handleSurpriseBackdrop : this.handleSurpriseCostume;
+        const addLibraryFunc = isStage ? onNewLibraryBackdropClick : onNewLibraryCostumeClick;
+        const addLibraryIcon = isStage ? addLibraryBackdropIcon : addLibraryCostumeIcon;
 
-        const costumeData = (target.costumes || []).map(costume => ({
+        const costumeData = target.costumes ? target.costumes.map(costume => ({
             name: costume.name,
             assetId: costume.assetId,
             details: costume.size ? this.formatCostumeDetails(costume.size, costume.bitmapResolution) : null
-        }));
-
+        })) : [];
         return (
             <AssetPanel
                 buttons={[
@@ -271,10 +273,12 @@ class CostumeTab extends React.Component {
                         onClick: this.handleNewBlankCostume
                     }
                 ]}
+                dragType={DragConstants.COSTUME}
                 items={costumeData}
                 selectedItemIndex={this.state.selectedCostumeIndex}
                 onDeleteClick={target && target.costumes && target.costumes.length > 1 ?
                     this.handleDeleteCostume : null}
+                onDrop={this.handleDrop}
                 onDuplicateClick={this.handleDuplicateCostume}
                 onItemClick={this.handleSelectCostume}
             >
@@ -284,18 +288,6 @@ class CostumeTab extends React.Component {
                     /> :
                     null
                 }
-                {costumeLibraryVisible ? (
-                    <CostumeLibrary
-                        vm={vm}
-                        onRequestClose={onRequestCloseCostumeLibrary}
-                    />
-                ) : null}
-                {backdropLibraryVisible ? (
-                    <BackdropLibrary
-                        vm={vm}
-                        onRequestClose={onRequestCloseBackdropLibrary}
-                    />
-                ) : null}
                 {cameraModalVisible ? (
                     <CameraModal
                         onClose={onRequestCloseCameraModal}
@@ -308,17 +300,13 @@ class CostumeTab extends React.Component {
 }
 
 CostumeTab.propTypes = {
-    backdropLibraryVisible: PropTypes.bool,
     cameraModalVisible: PropTypes.bool,
-    costumeLibraryVisible: PropTypes.bool,
     editingTarget: PropTypes.string,
     intl: intlShape,
     onNewCostumeFromCameraClick: PropTypes.func.isRequired,
     onNewLibraryBackdropClick: PropTypes.func.isRequired,
     onNewLibraryCostumeClick: PropTypes.func.isRequired,
-    onRequestCloseBackdropLibrary: PropTypes.func.isRequired,
     onRequestCloseCameraModal: PropTypes.func.isRequired,
-    onRequestCloseCostumeLibrary: PropTypes.func.isRequired,
     sprites: PropTypes.shape({
         id: PropTypes.shape({
             costumes: PropTypes.arrayOf(PropTypes.shape({
@@ -340,9 +328,8 @@ const mapStateToProps = state => ({
     editingTarget: state.scratchGui.targets.editingTarget,
     sprites: state.scratchGui.targets.sprites,
     stage: state.scratchGui.targets.stage,
-    cameraModalVisible: state.scratchGui.modals.cameraCapture,
-    costumeLibraryVisible: state.scratchGui.modals.costumeLibrary,
-    backdropLibraryVisible: state.scratchGui.modals.backdropLibrary
+    dragging: state.scratchGui.assetDrag.dragging,
+    cameraModalVisible: state.scratchGui.modals.cameraCapture
 });
 
 const mapDispatchToProps = dispatch => ({
@@ -357,12 +344,6 @@ const mapDispatchToProps = dispatch => ({
     onNewCostumeFromCameraClick: () => {
         dispatch(openCameraCapture());
     },
-    onRequestCloseBackdropLibrary: () => {
-        dispatch(closeBackdropLibrary());
-    },
-    onRequestCloseCostumeLibrary: () => {
-        dispatch(closeCostumeLibrary());
-    },
     onRequestCloseCameraModal: () => {
         dispatch(closeCameraCapture());
     }
diff --git a/src/containers/gui.jsx b/src/containers/gui.jsx
index ed2ec178587147662aa9e666f617d80ee2ae39cb..bdd552968f4f55c8c1a7fca096206e7574a63960 100644
--- a/src/containers/gui.jsx
+++ b/src/containers/gui.jsx
@@ -14,6 +14,11 @@ import {
     SOUNDS_TAB_INDEX
 } from '../reducers/editor-tab';
 
+import {
+    closeCostumeLibrary,
+    closeBackdropLibrary
+} from '../reducers/modals';
+
 import ProjectLoaderHOC from '../lib/project-loader-hoc.jsx';
 import vmListenerHOC from '../lib/vm-listener-hoc.jsx';
 
@@ -103,8 +108,10 @@ GUI.defaultProps = GUIComponent.defaultProps;
 
 const mapStateToProps = state => ({
     activeTabIndex: state.scratchGui.editorTab.activeTabIndex,
+    backdropLibraryVisible: state.scratchGui.modals.backdropLibrary,
     blocksTabVisible: state.scratchGui.editorTab.activeTabIndex === BLOCKS_TAB_INDEX,
     cardsVisible: state.scratchGui.cards.visible,
+    costumeLibraryVisible: state.scratchGui.modals.costumeLibrary,
     costumesTabVisible: state.scratchGui.editorTab.activeTabIndex === COSTUMES_TAB_INDEX,
     importInfoVisible: state.scratchGui.modals.importInfo,
     isPlayerOnly: state.scratchGui.mode.isPlayerOnly,
@@ -122,7 +129,9 @@ const mapDispatchToProps = dispatch => ({
     onExtensionButtonClick: () => dispatch(openExtensionLibrary()),
     onActivateTab: tab => dispatch(activateTab(tab)),
     onActivateCostumesTab: () => dispatch(activateTab(COSTUMES_TAB_INDEX)),
-    onActivateSoundsTab: () => dispatch(activateTab(SOUNDS_TAB_INDEX))
+    onActivateSoundsTab: () => dispatch(activateTab(SOUNDS_TAB_INDEX)),
+    onRequestCloseBackdropLibrary: () => dispatch(closeBackdropLibrary()),
+    onRequestCloseCostumeLibrary: () => dispatch(closeCostumeLibrary())
 });
 
 const ConnectedGUI = connect(
diff --git a/src/containers/language-selector.jsx b/src/containers/language-selector.jsx
index 316b1577a367a4314ee7a610f72b9bee2516874d..49fb60943ada6bdefa77f95548dc40e2b3080d79 100644
--- a/src/containers/language-selector.jsx
+++ b/src/containers/language-selector.jsx
@@ -1,18 +1,56 @@
+import bindAll from 'lodash.bindall';
+import PropTypes from 'prop-types';
+import React from 'react';
 import {connect} from 'react-redux';
+import {updateIntl} from 'react-intl-redux';
+import {closeLanguageMenu} from '../reducers/menus';
 
 import LanguageSelectorComponent from '../components/language-selector/language-selector.jsx';
 
+class LanguageSelector extends React.Component {
+    constructor (props) {
+        super(props);
+        bindAll(this, [
+            'handleChange'
+        ]);
+    }
+    handleChange (e) {
+        this.props.onChangeLanguage(e.target.value);
+    }
+    render () {
+        const {
+            onChangeLanguage, // eslint-disable-line no-unused-vars
+            children,
+            ...props
+        } = this.props;
+        return (
+            <LanguageSelectorComponent
+                onChange={this.handleChange}
+                {...props}
+            >
+                {children}
+            </LanguageSelectorComponent>
+        );
+    }
+}
+
+LanguageSelector.propTypes = {
+    children: PropTypes.node,
+    onChangeLanguage: PropTypes.func.isRequired
+};
+
 const mapStateToProps = state => ({
     currentLocale: state.intl.locale
 });
 
-const mapDispatchToProps = () => ({
-    onChange: e => {
-        e.preventDefault();
+const mapDispatchToProps = dispatch => ({
+    onChangeLanguage: locale => {
+        dispatch(updateIntl({locale: locale, messages: {}}));
+        dispatch(closeLanguageMenu());
     }
 });
 
 export default connect(
     mapStateToProps,
     mapDispatchToProps
-)(LanguageSelectorComponent);
+)(LanguageSelector);
diff --git a/src/containers/paint-editor-wrapper.jsx b/src/containers/paint-editor-wrapper.jsx
index 9b606f3dae07f3746dd5b51b5c5cd6851c4a7be3..390f6ef598b7ca63f52868529ba9720a0f652d24 100644
--- a/src/containers/paint-editor-wrapper.jsx
+++ b/src/containers/paint-editor-wrapper.jsx
@@ -43,7 +43,6 @@ class PaintEditorWrapper extends React.Component {
         return (
             <PaintEditor
                 {...this.props}
-                image={this.props.vm.getCostume(this.props.selectedCostumeIndex)}
                 onUpdateImage={this.handleUpdateImage}
                 onUpdateName={this.handleUpdateName}
             />
@@ -62,19 +61,19 @@ PaintEditorWrapper.propTypes = {
 };
 
 const mapStateToProps = (state, {selectedCostumeIndex}) => {
-    const {
-        editingTarget,
-        sprites,
-        stage
-    } = state.scratchGui.targets;
-    const target = editingTarget && sprites[editingTarget] ? sprites[editingTarget] : stage;
-    const costume = target && target.costumes[selectedCostumeIndex];
+    const targetId = state.scratchGui.vm.editingTarget.id;
+    const sprite = state.scratchGui.vm.editingTarget.sprite;
+    // Make sure the costume index doesn't go out of range.
+    const index = selectedCostumeIndex < sprite.costumes.length ?
+        selectedCostumeIndex : sprite.costumes.length - 1;
+    const costume = state.scratchGui.vm.editingTarget.sprite.costumes[index];
     return {
         name: costume && costume.name,
         rotationCenterX: costume && costume.rotationCenterX,
         rotationCenterY: costume && costume.rotationCenterY,
         imageFormat: costume && costume.dataFormat,
-        imageId: editingTarget && `${editingTarget}${costume.skinId}`,
+        imageId: targetId && `${targetId}${costume.skinId}`,
+        image: state.scratchGui.vm.getCostume(index),
         vm: state.scratchGui.vm
     };
 };
diff --git a/src/containers/sound-library.jsx b/src/containers/sound-library.jsx
index 3d68ea6f32ad19134508c847321147c4f51daf93..5d7cb67f6fdb50a941460336282b46dfac4128bd 100644
--- a/src/containers/sound-library.jsx
+++ b/src/containers/sound-library.jsx
@@ -29,20 +29,63 @@ class SoundLibrary extends React.PureComponent {
             'handleItemMouseEnter',
             'handleItemMouseLeave'
         ]);
+
+        /**
+         * AudioEngine that will decode and play sounds for us.
+         * @type {AudioEngine}
+         */
+        this.audioEngine = null;
+        /**
+         * A promise for the sound queued to play as soon as it loads and
+         * decodes.
+         * @type {Promise<SoundPlayer>}
+         */
+        this.playingSoundPromise = null;
     }
     componentDidMount () {
         this.audioEngine = new AudioEngine();
-        this.player = this.audioEngine.createPlayer();
+        this.playingSoundPromise = null;
     }
     componentWillUnmount () {
-        this.player.stopAllSounds();
+        this.stopPlayingSound();
+    }
+    stopPlayingSound () {
+        // Playback is queued, playing, or has played recently and finished
+        // normally.
+        if (this.playingSoundPromise !== null) {
+            // Queued playback began playing before this method.
+            if (this.playingSoundPromise.isPlaying) {
+                // Fetch the player from the promise and stop playback soon.
+                this.playingSoundPromise.then(soundPlayer => {
+                    soundPlayer.stop();
+                });
+            } else {
+                // Fetch the player from the promise and stop immediately. Since
+                // the sound is not playing yet, this callback will be called
+                // immediately after the sound starts playback. Stopping it
+                // immediately will have the effect of no sound being played.
+                this.playingSoundPromise.then(soundPlayer => {
+                    soundPlayer.stopImmediately();
+                });
+            }
+            // No further work should be performed on this promise and its
+            // soundPlayer.
+            this.playingSoundPromise = null;
+        }
     }
     handleItemMouseEnter (soundItem) {
         const md5ext = soundItem._md5;
         const idParts = md5ext.split('.');
         const md5 = idParts[0];
         const vm = this.props.vm;
-        vm.runtime.storage.load(vm.runtime.storage.AssetType.Sound, md5)
+
+        // In case enter is called twice without a corresponding leave
+        // inbetween, stop the last playback before queueing a new sound.
+        this.stopPlayingSound();
+
+        // Save the promise so code to stop the sound may queue the stop
+        // instruction after the play instruction.
+        this.playingSoundPromise = vm.runtime.storage.load(vm.runtime.storage.AssetType.Sound, md5)
             .then(soundAsset => {
                 const sound = {
                     md5: md5ext,
@@ -50,14 +93,23 @@ class SoundLibrary extends React.PureComponent {
                     format: soundItem.format,
                     data: soundAsset.data
                 };
-                return this.audioEngine.decodeSound(sound);
+                return this.audioEngine.decodeSoundPlayer(sound);
             })
-            .then(soundId => {
-                this.player.playSound(soundId);
+            .then(soundPlayer => {
+                soundPlayer.connect(this.audioEngine);
+                // Play the sound. Playing the sound will always come before a
+                // paired stop if the sound must stop early.
+                soundPlayer.play();
+                // Set that the sound is playing. This affects the type of stop
+                // instruction given if the sound must stop early.
+                if (this.playingSoundPromise !== null) {
+                    this.playingSoundPromise.isPlaying = true;
+                }
+                return soundPlayer;
             });
     }
     handleItemMouseLeave () {
-        this.player.stopAllSounds();
+        this.stopPlayingSound();
     }
     handleItemSelected (soundItem) {
         const vmSound = {
diff --git a/src/containers/sound-tab.jsx b/src/containers/sound-tab.jsx
index d532c5ee9bc0b3cb359dfae24a75dd03ff9eec38..097457d8a45a0e0b28ce5eaf26712f2da1a8becb 100644
--- a/src/containers/sound-tab.jsx
+++ b/src/containers/sound-tab.jsx
@@ -18,6 +18,7 @@ import SoundLibrary from './sound-library.jsx';
 import soundLibraryContent from '../lib/libraries/sounds.json';
 import {handleFileUpload, soundUpload} from '../lib/file-uploader.js';
 import errorBoundaryHOC from '../lib/error-boundary-hoc.jsx';
+import DragConstants from '../lib/drag-constants';
 
 import {connect} from 'react-redux';
 
@@ -38,6 +39,7 @@ class SoundTab extends React.Component {
             'handleSurpriseSound',
             'handleFileUploadClick',
             'handleSoundUpload',
+            'handleDrop',
             'setFileInput'
         ]);
         this.state = {selectedSoundIndex: 0};
@@ -117,6 +119,20 @@ class SoundTab extends React.Component {
         });
     }
 
+    handleDrop (dropInfo) {
+        // Eventually will handle other kinds of drop events, right now just
+        // the reordering events.
+        if (dropInfo.dragType === DragConstants.SOUND) {
+            const sprite = this.props.vm.editingTarget.sprite;
+            const activeSound = sprite.sounds[this.state.selectedSoundIndex];
+
+            this.props.vm.reorderSound(this.props.vm.editingTarget.id,
+                dropInfo.index, dropInfo.newIndex);
+
+            this.setState({selectedSoundIndex: sprite.sounds.indexOf(activeSound)});
+        }
+    }
+
     setFileInput (input) {
         this.fileInput = input;
     }
@@ -188,12 +204,11 @@ class SoundTab extends React.Component {
                     img: addSoundFromRecordingIcon,
                     onClick: onNewSoundFromRecordingClick
                 }]}
-                items={sounds.map(sound => ({
-                    url: soundIcon,
-                    ...sound
-                }))}
+                dragType={DragConstants.SOUND}
+                items={sounds}
                 selectedItemIndex={this.state.selectedSoundIndex}
                 onDeleteClick={this.handleDeleteSound}
+                onDrop={this.handleDrop}
                 onDuplicateClick={this.handleDuplicateSound}
                 onItemClick={this.handleSelectSound}
             >
diff --git a/src/containers/sprite-selector-item.jsx b/src/containers/sprite-selector-item.jsx
index 1a6c77b9d970831cda6f8b5c307de99fbf21d7f3..5efe494280f1fa004b55a4502f5027a37634e238 100644
--- a/src/containers/sprite-selector-item.jsx
+++ b/src/containers/sprite-selector-item.jsx
@@ -34,7 +34,12 @@ class SpriteSelectorItem extends React.Component {
         this.props.onDrag({
             img: null,
             currentOffset: null,
-            dragging: false
+            dragging: false,
+            dragType: null,
+            index: null
+        });
+        setTimeout(() => {
+            this.noClick = false;
         });
     }
     handleMouseMove (e) {
@@ -45,8 +50,11 @@ class SpriteSelectorItem extends React.Component {
             this.props.onDrag({
                 img: this.props.costumeURL,
                 currentOffset: currentOffset,
-                dragging: true
+                dragging: true,
+                dragType: this.props.dragType,
+                index: this.props.index
             });
+            this.noClick = true;
         }
         e.preventDefault();
     }
@@ -59,7 +67,9 @@ class SpriteSelectorItem extends React.Component {
     }
     handleClick (e) {
         e.preventDefault();
-        this.props.onClick(this.props.id);
+        if (!this.noClick) {
+            this.props.onClick(this.props.id);
+        }
     }
     handleDelete (e) {
         e.stopPropagation(); // To prevent from bubbling back to handleClick
@@ -84,6 +94,7 @@ class SpriteSelectorItem extends React.Component {
             /* eslint-disable no-unused-vars */
             assetId,
             id,
+            index,
             onClick,
             onDeleteButtonClick,
             onDuplicateButtonClick,
@@ -109,7 +120,9 @@ SpriteSelectorItem.propTypes = {
     assetId: PropTypes.string,
     costumeURL: PropTypes.string,
     dispatchSetHoveredSprite: PropTypes.func.isRequired,
+    dragType: PropTypes.string,
     id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
+    index: PropTypes.number,
     name: PropTypes.string,
     onClick: PropTypes.func,
     onDeleteButtonClick: PropTypes.func,
diff --git a/src/containers/stage-header.jsx b/src/containers/stage-header.jsx
index 883c882fa120c343673febefbe5b14b814ced929..11ffab43364bef2e843d7211a7b0bac15cf202f9 100644
--- a/src/containers/stage-header.jsx
+++ b/src/containers/stage-header.jsx
@@ -2,7 +2,8 @@ import PropTypes from 'prop-types';
 import React from 'react';
 import bindAll from 'lodash.bindall';
 import VM from 'scratch-vm';
-import {setStageSize, STAGE_SIZES} from '../reducers/stage-size';
+import {STAGE_SIZE_MODES} from '../lib/layout-constants';
+import {setStageSize} from '../reducers/stage-size';
 import {setFullScreen} from '../reducers/mode';
 
 import {connect} from 'react-redux';
@@ -45,19 +46,19 @@ StageHeader.propTypes = {
     isFullScreen: PropTypes.bool,
     isPlayerOnly: PropTypes.bool,
     onSetStageUnFull: PropTypes.func.isRequired,
-    stageSize: PropTypes.oneOf(Object.keys(STAGE_SIZES)),
+    stageSizeMode: PropTypes.oneOf(Object.keys(STAGE_SIZE_MODES)),
     vm: PropTypes.instanceOf(VM).isRequired
 };
 
 const mapStateToProps = state => ({
-    stageSize: state.scratchGui.stageSize.stageSize,
+    stageSizeMode: state.scratchGui.stageSize.stageSize,
     isFullScreen: state.scratchGui.mode.isFullScreen,
     isPlayerOnly: state.scratchGui.mode.isPlayerOnly
 });
 
 const mapDispatchToProps = dispatch => ({
-    onSetStageLarge: () => dispatch(setStageSize(STAGE_SIZES.large)),
-    onSetStageSmall: () => dispatch(setStageSize(STAGE_SIZES.small)),
+    onSetStageLarge: () => dispatch(setStageSize(STAGE_SIZE_MODES.large)),
+    onSetStageSmall: () => dispatch(setStageSize(STAGE_SIZE_MODES.small)),
     onSetStageFull: () => dispatch(setFullScreen(true)),
     onSetStageUnFull: () => dispatch(setFullScreen(false))
 });
diff --git a/src/containers/stage-selector.jsx b/src/containers/stage-selector.jsx
index 4d84746000ba09fb846937323d7dec216eab409a..99c233c7d8e7ea7b8a28f497274fe85da966948d 100644
--- a/src/containers/stage-selector.jsx
+++ b/src/containers/stage-selector.jsx
@@ -1,4 +1,5 @@
 import bindAll from 'lodash.bindall';
+import omit from 'lodash.omit';
 import PropTypes from 'prop-types';
 import React from 'react';
 
@@ -78,15 +79,8 @@ class StageSelector extends React.Component {
         this.fileInput = input;
     }
     render () {
-        const {
-            /* eslint-disable no-unused-vars */
-            assetId,
-            id,
-            onActivateTab,
-            onSelect,
-            /* eslint-enable no-unused-vars */
-            ...componentProps
-        } = this.props;
+        const componentProps = omit(this.props, [
+            'assetId', 'dispatchSetHoveredSprite', 'id', 'onActivateTab', 'onSelect']);
         return (
             <StageSelectorComponent
                 fileInputRef={this.setFileInput}
@@ -119,8 +113,7 @@ const mapStateToProps = (state, {assetId, id}) => ({
 
 const mapDispatchToProps = dispatch => ({
     onNewBackdropClick: e => {
-        e.preventDefault();
-        dispatch(activateTab(COSTUMES_TAB_INDEX));
+        e.stopPropagation();
         dispatch(openBackdropLibrary());
     },
     onActivateTab: tabIndex => {
diff --git a/src/containers/stage-wrapper.jsx b/src/containers/stage-wrapper.jsx
index 039f3ab3ca61a3b9001863ccff784cdabdfdc37b..e8ca1e76994f96d677f046f3c63b9d7781c0650d 100644
--- a/src/containers/stage-wrapper.jsx
+++ b/src/containers/stage-wrapper.jsx
@@ -1,12 +1,14 @@
 import PropTypes from 'prop-types';
 import React from 'react';
 import VM from 'scratch-vm';
+import {STAGE_DISPLAY_SIZES} from '../lib/layout-constants.js';
 import StageWrapperComponent from '../components/stage-wrapper/stage-wrapper.jsx';
 
 const StageWrapper = props => <StageWrapperComponent {...props} />;
 
 StageWrapper.propTypes = {
     isRendererSupported: PropTypes.bool.isRequired,
+    stageSize: PropTypes.oneOf(Object.keys(STAGE_DISPLAY_SIZES)).isRequired,
     vm: PropTypes.instanceOf(VM).isRequired
 };
 
diff --git a/src/containers/stage.jsx b/src/containers/stage.jsx
index 881c9c929aa3cef447a1f9b8831670158b321969..0013307f8d31bee981f324ff83d182980badf8a2 100644
--- a/src/containers/stage.jsx
+++ b/src/containers/stage.jsx
@@ -5,6 +5,7 @@ import Renderer from 'scratch-render';
 import VM from 'scratch-vm';
 import {connect} from 'react-redux';
 
+import {STAGE_DISPLAY_SIZES} from '../lib/layout-constants';
 import {getEventXY} from '../lib/touch-utils';
 import VideoProvider from '../lib/video/video-provider';
 import {SVGRenderer as V2SVGAdapter} from 'scratch-svg-renderer';
@@ -63,8 +64,7 @@ class Stage extends React.Component {
         this.props.vm.setVideoProvider(new VideoProvider());
     }
     shouldComponentUpdate (nextProps, nextState) {
-        return this.props.width !== nextProps.width ||
-            this.props.height !== nextProps.height ||
+        return this.props.stageSize !== nextProps.stageSize ||
             this.props.isColorPicking !== nextProps.isColorPicking ||
             this.state.colorInfo !== nextState.colorInfo ||
             this.props.isFullScreen !== nextProps.isFullScreen ||
@@ -380,14 +380,13 @@ class Stage extends React.Component {
 }
 
 Stage.propTypes = {
-    height: PropTypes.number,
     isColorPicking: PropTypes.bool,
     isFullScreen: PropTypes.bool.isRequired,
     onActivateColorPicker: PropTypes.func,
     onDeactivateColorPicker: PropTypes.func,
+    stageSize: PropTypes.oneOf(Object.keys(STAGE_DISPLAY_SIZES)).isRequired,
     useEditorDragStyle: PropTypes.bool,
-    vm: PropTypes.instanceOf(VM).isRequired,
-    width: PropTypes.number
+    vm: PropTypes.instanceOf(VM).isRequired
 };
 
 Stage.defaultProps = {
diff --git a/src/containers/target-pane.jsx b/src/containers/target-pane.jsx
index dd20a4742f5825851410f3b8c5f901b815c5ace5..994f923bb4c5b7af34153f6232d02da2d04373d7 100644
--- a/src/containers/target-pane.jsx
+++ b/src/containers/target-pane.jsx
@@ -10,7 +10,7 @@ import {
 
 import {activateTab, COSTUMES_TAB_INDEX} from '../reducers/editor-tab';
 import {setReceivedBlocks} from '../reducers/hovered-target';
-
+import DragConstants from '../lib/drag-constants';
 import TargetPaneComponent from '../components/target-pane/target-pane.jsx';
 import spriteLibraryContent from '../lib/libraries/sprites.json';
 import {handleFileUpload, spriteUpload} from '../lib/file-uploader.js';
@@ -27,6 +27,7 @@ class TargetPane extends React.Component {
             'handleChangeSpriteX',
             'handleChangeSpriteY',
             'handleDeleteSprite',
+            'handleDrop',
             'handleDuplicateSprite',
             'handleNewSprite',
             'handleSelectSprite',
@@ -106,6 +107,14 @@ class TargetPane extends React.Component {
             this.props.onReceivedBlocks(true);
         }
     }
+
+    handleDrop (dragInfo) {
+        if (dragInfo.dragType === DragConstants.SPRITE) {
+            // Add one to both new and target index because we are not counting/moving the stage
+            this.props.vm.reorderTarget(dragInfo.index + 1, dragInfo.newIndex + 1);
+        }
+    }
+
     render () {
         const {
             onActivateTab, // eslint-disable-line no-unused-vars
@@ -123,6 +132,7 @@ class TargetPane extends React.Component {
                 onChangeSpriteX={this.handleChangeSpriteX}
                 onChangeSpriteY={this.handleChangeSpriteY}
                 onDeleteSprite={this.handleDeleteSprite}
+                onDrop={this.handleDrop}
                 onDuplicateSprite={this.handleDuplicateSprite}
                 onFileUploadClick={this.handleFileUploadClick}
                 onPaintSpriteClick={this.handlePaintSpriteClick}
diff --git a/src/css/z-index.css b/src/css/z-index.css
new file mode 100644
index 0000000000000000000000000000000000000000..b3368fc2bd638ef4763bf495171b41a7f41d7c53
--- /dev/null
+++ b/src/css/z-index.css
@@ -0,0 +1,28 @@
+/*
+    Contains constants for the z-index values of elements that are part of the global stack context.
+    In other words, z-index values that are "inside" a component are not added here.
+    This prevents conflicts between identical z-index values in different components.
+*/
+
+$z-index-extension-button: 50; /* Force extension button above the ScratchBlocks flyout */
+$z-index-menu-bar: 50; /* blocklyToolboxDiv is 40 */
+
+$z-index-monitor: 100;
+$z-index-coming-soon: 110;
+$z-index-add-button: 120;
+
+$z-index-card: 490;
+$z-index-loader: 500;
+$z-index-modal: 510;
+
+$z-index-drag-layer: 1000;
+$z-index-monitor-dragging: 1010;
+$z-index-dragging-sprite: 1020; /* so it is draggable into other panes */
+
+$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;
+
+/* in most interfaces, the context menu is always on top */
+$z-index-context-menu: 10000;
diff --git a/src/lib/drag-constants.js b/src/lib/drag-constants.js
new file mode 100644
index 0000000000000000000000000000000000000000..86f064da90e556b50b5653be17aa8d9f17708811
--- /dev/null
+++ b/src/lib/drag-constants.js
@@ -0,0 +1,5 @@
+export default {
+    SOUND: 'SOUND',
+    COSTUME: 'COSTUME',
+    SPRITE: 'SPRITE'
+};
diff --git a/src/lib/drag-utils.js b/src/lib/drag-utils.js
new file mode 100644
index 0000000000000000000000000000000000000000..e3f5c1bd0e65bd6fbade42ad9dda30b260e650a9
--- /dev/null
+++ b/src/lib/drag-utils.js
@@ -0,0 +1,47 @@
+/**
+ * @fileoverview
+ * Utility functions for drag interactions, e.g. sorting items in a grid/list.
+ */
+
+/**
+ * From an xy position and a list of boxes {top, left, bottom, right}, return there
+ * corresponding box index the position is over. The boxes are in a (possibly wrapped)
+ * list, the only requirement being all boxes are flush against the edges, that is,
+ * if they are along an outer edge, the position of that edge is identical.
+ * This functionality works for a single column of items, a wrapped list with
+ * many rows, or a single row of items.
+ * @param {{x: number, y: number}} position The xy coordinates to retreive the corresponding index of.
+ * @param {Array.<DOMRect>} boxes The rects of the items, returned from `getBoundingClientRect`
+ * @return {?number} index of the corresponding box, or null if one could not be found.
+ */
+const indexForPositionOnList = ({x, y}, boxes) => {
+    if (boxes.length === 0) return null;
+    let index = null;
+    const leftEdge = Math.min.apply(null, boxes.map(b => b.left));
+    const rightEdge = Math.max.apply(null, boxes.map(b => b.right));
+    const topEdge = Math.min.apply(null, boxes.map(b => b.top));
+    const bottomEdge = Math.max.apply(null, boxes.map(b => b.bottom));
+    for (let n = 0; n < boxes.length; n++) {
+        const box = boxes[n];
+        // Construct an "extended" box for each, extending out to infinity if
+        // the box is along a boundary.
+        const minX = box.left === leftEdge ? -Infinity : box.left;
+        const minY = box.top === topEdge ? -Infinity : box.top;
+        const maxY = box.bottom === bottomEdge ? Infinity : box.bottom;
+        // The last item in the wrapped list gets a right edge at infinity, even
+        // if it isn't the farthest right. Add this as an "or" condition for extension.
+        const maxX = (n === boxes.length - 1 || box.right === rightEdge) ?
+            Infinity : box.right;
+
+        // Check if the point is in the bounds.
+        if (x > minX && x <= maxX && y > minY && y <= maxY) {
+            index = n;
+            break; // No need to keep looking.
+        }
+    }
+    return index;
+};
+
+export {
+    indexForPositionOnList
+};
diff --git a/src/lib/layout-constants.js b/src/lib/layout-constants.js
index 67c2ae44cc0ecc3c8b979626d4ba51e85a176b63..89cc7f3d9b6e34a768108771cceb4426dc1116dc 100644
--- a/src/lib/layout-constants.js
+++ b/src/lib/layout-constants.js
@@ -1,8 +1,56 @@
+import keyMirror from 'keymirror';
+
+/**
+ * Names for each state of the stage size toggle
+ * @enum {string}
+ */
+const STAGE_SIZE_MODES = keyMirror({
+    /**
+     * The "large stage" button is pressed; the user would like a large stage.
+     */
+    large: null,
+
+    /**
+     * The "small stage" button is pressed; the user would like a small stage.
+     */
+    small: null
+});
+
+/**
+ * Names for each stage render size
+ * @enum {string}
+ */
+const STAGE_DISPLAY_SIZES = keyMirror({
+    /**
+     * Large stage with wide browser
+     */
+    large: null,
+
+    /**
+     * Large stage with narrow browser
+     */
+    largeConstrained: null,
+
+    /**
+     * Small stage (ignores browser width)
+     */
+    small: null
+});
+
+const STAGE_DISPLAY_SCALES = {};
+STAGE_DISPLAY_SCALES[STAGE_DISPLAY_SIZES.large] = 1; // large mode, wide browser (standard)
+STAGE_DISPLAY_SCALES[STAGE_DISPLAY_SIZES.largeConstrained] = 0.85; // large mode but narrow browser
+STAGE_DISPLAY_SCALES[STAGE_DISPLAY_SIZES.small] = 0.5; // small mode, regardless of browser size
+
 export default {
-    fullStageWidth: 480,
-    fullStageHeight: 360,
-    smallerStageWidth: 480 * 0.85,
-    smallerStageHeight: 360 * 0.85,
+    standardStageWidth: 480,
+    standardStageHeight: 360,
     fullSizeMinWidth: 1096,
     fullSizePaintMinWidth: 1250
 };
+
+export {
+    STAGE_DISPLAY_SCALES,
+    STAGE_DISPLAY_SIZES,
+    STAGE_SIZE_MODES
+};
diff --git a/src/lib/screen-utils.js b/src/lib/screen-utils.js
index 866014913c11f8a37884d1abc5610eadd9e7ff3d..e233993e26155a8f493363c7a6424422d2e1e054 100644
--- a/src/lib/screen-utils.js
+++ b/src/lib/screen-utils.js
@@ -1,39 +1,73 @@
-const STAGE_SIZE_DEFAULTS = {
-    heightSmall: 360,
-    widthSmall: 480,
+import layout, {STAGE_DISPLAY_SCALES, STAGE_SIZE_MODES, STAGE_DISPLAY_SIZES} from '../lib/layout-constants';
+
+/**
+ * @typedef {object} StageDimensions
+ * @property {int} height - the height to be used for the stage in the current situation.
+ * @property {int} width - the width to be used for the stage in the current situation.
+ * @property {number} scale - the scale factor from the stage's default size to its current size.
+ * @property {int} heightDefault - the height of the stage in its default (large) size.
+ * @property {int} widthDefault - the width of the stage in its default (large) size.
+ */
+
+const STAGE_DIMENSION_DEFAULTS = {
     spacingBorderAdjustment: 9,
     menuHeightAdjustment: 40
 };
 
-const getStageSize = (
-    isFullScreen = false,
-    height = STAGE_SIZE_DEFAULTS.heightSmall,
-    width = STAGE_SIZE_DEFAULTS.widthSmall) => {
+/**
+ * Resolve the current GUI and browser state to an actual stage size enum value.
+ * @param {STAGE_SIZE_MODES} stageSizeMode - the state of the stage size toggle button.
+ * @param {boolean} isFullSize - true if the window is large enough for the large stage at its full size.
+ * @return {STAGE_DISPLAY_SIZES} - the stage size enum value we should use in this situation.
+ */
+const resolveStageSize = (stageSizeMode, isFullSize) => {
+    if (stageSizeMode === STAGE_SIZE_MODES.small) {
+        return STAGE_DISPLAY_SIZES.small;
+    }
+    if (isFullSize) {
+        return STAGE_DISPLAY_SIZES.large;
+    }
+    return STAGE_DISPLAY_SIZES.largeConstrained;
+};
 
-    const stageSize = {
-        heightDefault: height,
-        widthDefault: width,
-        height: height,
-        width: width
+/**
+ * Retrieve info used to determine the actual stage size based on the current GUI and browser state.
+ * @param {STAGE_DISPLAY_SIZES} stageSize - the current fully-resolved stage size.
+ * @param {boolean} isFullScreen - true if full-screen mode is enabled.
+ * @return {StageDimensions} - an object describing the dimensions of the stage.
+ */
+const getStageDimensions = (stageSize, isFullScreen) => {
+    const stageDimensions = {
+        heightDefault: layout.standardStageHeight,
+        widthDefault: layout.standardStageWidth,
+        height: 0,
+        width: 0,
+        scale: 0
     };
 
     if (isFullScreen) {
-        stageSize.height = window.innerHeight -
-                           STAGE_SIZE_DEFAULTS.menuHeightAdjustment -
-                           STAGE_SIZE_DEFAULTS.spacingBorderAdjustment;
+        stageDimensions.height = window.innerHeight -
+            STAGE_DIMENSION_DEFAULTS.menuHeightAdjustment -
+            STAGE_DIMENSION_DEFAULTS.spacingBorderAdjustment;
 
-        stageSize.width = stageSize.height + (stageSize.height / 3);
+        stageDimensions.width = stageDimensions.height + (stageDimensions.height / 3);
 
-        if (stageSize.width > window.innerWidth) {
-            stageSize.width = window.innerWidth;
-            stageSize.height = stageSize.width * .75;
+        if (stageDimensions.width > window.innerWidth) {
+            stageDimensions.width = window.innerWidth;
+            stageDimensions.height = stageDimensions.width * .75;
         }
+
+        stageDimensions.scale = stageDimensions.width / stageDimensions.widthDefault;
+    } else {
+        stageDimensions.scale = STAGE_DISPLAY_SCALES[stageSize];
+        stageDimensions.height = stageDimensions.scale * stageDimensions.heightDefault;
+        stageDimensions.width = stageDimensions.scale * stageDimensions.widthDefault;
     }
 
-    return stageSize;
+    return stageDimensions;
 };
 
 export {
-    getStageSize,
-    STAGE_SIZE_DEFAULTS
+    getStageDimensions,
+    resolveStageSize
 };
diff --git a/src/lib/sortable-hoc.jsx b/src/lib/sortable-hoc.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..665c5bc43a64fb055217786e4e96551de3c496ba
--- /dev/null
+++ b/src/lib/sortable-hoc.jsx
@@ -0,0 +1,120 @@
+import bindAll from 'lodash.bindall';
+import PropTypes from 'prop-types';
+import React from 'react';
+import {connect} from 'react-redux';
+import {indexForPositionOnList} from './drag-utils';
+
+const SortableHOC = function (WrappedComponent) {
+    class SortableWrapper extends React.Component {
+        constructor (props) {
+            super(props);
+            bindAll(this, [
+                'handleAddSortable',
+                'handleRemoveSortable'
+            ]);
+
+            this.sortableRefs = [];
+            this.boxes = null;
+        }
+
+        componentWillReceiveProps (newProps) {
+            if (newProps.dragInfo.dragging && !this.props.dragInfo.dragging) {
+                // Drag just started, snapshot the sorted bounding boxes for sortables.
+                this.boxes = this.sortableRefs.map(el => el && el.getBoundingClientRect());
+                this.boxes.sort((a, b) => { // Sort top-to-bottom, left-to-right.
+                    if (a.top === b.top) return a.left - b.left;
+                    return a.top - b.top;
+                });
+            } else if (!newProps.dragInfo.dragging && this.props.dragInfo.dragging) {
+                this.props.onDrop(Object.assign({},
+                    this.props.dragInfo, {newIndex: this.getMouseOverIndex()}));
+            }
+        }
+
+        handleAddSortable (node) {
+            this.sortableRefs.push(node);
+        }
+
+        handleRemoveSortable (node) {
+            const index = this.sortableRefs.indexOf(node);
+            this.sortableRefs = this.sortableRefs.slice(0, index)
+                .concat(this.sortableRefs.slice(index + 1));
+        }
+
+        getOrdering (items, draggingIndex, newIndex) {
+            // An "Ordering" is an array of indices, where the position array value corresponds
+            // to the position of the item in props.items, and the index of the value
+            // is the index at which the item should appear.
+            // That is, if props.items is ['a', 'b', 'c', 'd'], and we want the GUI to display
+            // ['b', 'c', 'a, 'd'], the value of "ordering" would be [1, 2, 0, 3].
+            // This mapping is used because it is easy to translate to flexbox ordering,
+            // the `order` property for item N is ordering.indexOf(N).
+            // If the user-facing order matches props.items, the ordering is just [0, 1, 2, ...]
+            let ordering = Array(this.props.items.length).fill(0)
+                .map((_, i) => i);
+            const isNumber = v => typeof v === 'number' && !isNaN(v);
+            if (isNumber(draggingIndex) && isNumber(newIndex)) {
+                ordering = ordering.slice(0, draggingIndex).concat(ordering.slice(draggingIndex + 1));
+                ordering.splice(newIndex, 0, draggingIndex);
+            }
+            return ordering;
+        }
+        getMouseOverIndex () {
+            // MouseOverIndex is the index that the current drag wants to place the
+            // the dragging object. Obviously only exists if there is a drag (i.e. currentOffset).
+            let mouseOverIndex = null;
+            if (this.props.dragInfo.currentOffset) {
+                mouseOverIndex = indexForPositionOnList(
+                    this.props.dragInfo.currentOffset, this.boxes);
+            }
+            return mouseOverIndex;
+        }
+        render () {
+            const {dragInfo: {index: dragIndex, dragType}, items} = this.props;
+            const mouseOverIndex = this.getMouseOverIndex();
+            const ordering = this.getOrdering(items, dragIndex, mouseOverIndex);
+            return (
+                <WrappedComponent
+                    draggingIndex={dragIndex}
+                    draggingType={dragType}
+                    mouseOverIndex={mouseOverIndex}
+                    ordering={ordering}
+                    onAddSortable={this.handleAddSortable}
+                    onRemoveSortable={this.handleRemoveSortable}
+                    {...this.props}
+                />
+            );
+        }
+    }
+
+    SortableWrapper.propTypes = {
+        dragInfo: PropTypes.shape({
+            currentOffset: PropTypes.shape({
+                x: PropTypes.number,
+                y: PropTypes.number
+            }),
+            dragType: PropTypes.string,
+            dragging: PropTypes.bool,
+            index: PropTypes.number
+        }),
+        items: PropTypes.arrayOf(PropTypes.shape({
+            url: PropTypes.string,
+            name: PropTypes.string.isRequired
+        })),
+        onClose: PropTypes.func,
+        onDrop: PropTypes.func
+    };
+
+    const mapStateToProps = state => ({
+        dragInfo: state.scratchGui.assetDrag
+    });
+
+    const mapDispatchToProps = () => ({});
+
+    return connect(
+        mapStateToProps,
+        mapDispatchToProps
+    )(SortableWrapper);
+};
+
+export default SortableHOC;
diff --git a/src/reducers/menus.js b/src/reducers/menus.js
index 9d9ab0be9882e9001c5bd8f63195d9755a82631d..6693160c177fc988232fbd505f5a6c1e5af8d134 100644
--- a/src/reducers/menus.js
+++ b/src/reducers/menus.js
@@ -3,11 +3,13 @@ const CLOSE_MENU = 'scratch-gui/menus/CLOSE_MENU';
 
 const MENU_FILE = 'fileMenu';
 const MENU_EDIT = 'editMenu';
+const MENU_LANGUAGE = 'languageMenu';
 
 
 const initialState = {
     [MENU_FILE]: false,
-    [MENU_EDIT]: false
+    [MENU_EDIT]: false,
+    [MENU_LANGUAGE]: false
 };
 
 const reducer = function (state, action) {
@@ -39,6 +41,9 @@ const fileMenuOpen = state => state.scratchGui.menus[MENU_FILE];
 const openEditMenu = () => openMenu(MENU_EDIT);
 const closeEditMenu = () => closeMenu(MENU_EDIT);
 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];
 
 export {
     reducer as default,
@@ -47,6 +52,9 @@ export {
     closeFileMenu,
     openEditMenu,
     closeEditMenu,
+    openLanguageMenu,
+    closeLanguageMenu,
     fileMenuOpen,
-    editMenuOpen
+    editMenuOpen,
+    languageMenuOpen
 };
diff --git a/src/reducers/stage-size.js b/src/reducers/stage-size.js
index 74653f19e778169509ceeb898e6d87060742ca5a..44246ca43ae9decef5b27090fa2cccb697257ab2 100644
--- a/src/reducers/stage-size.js
+++ b/src/reducers/stage-size.js
@@ -1,13 +1,9 @@
+import {STAGE_DISPLAY_SIZES} from '../lib/layout-constants.js';
+
 const SET_STAGE_SIZE = 'scratch-gui/StageSize/SET_STAGE_SIZE';
 
 const initialState = {
-    stageSize: 'large'
-};
-
-// stage size constants
-const STAGE_SIZES = {
-    small: 'small',
-    large: 'large'
+    stageSize: STAGE_DISPLAY_SIZES.large
 };
 
 const reducer = function (state, action) {
@@ -32,6 +28,5 @@ const setStageSize = function (stageSize) {
 export {
     reducer as default,
     initialState as stageSizeInitialState,
-    setStageSize,
-    STAGE_SIZES
+    setStageSize
 };
diff --git a/test/unit/util/drag-utils.test.js b/test/unit/util/drag-utils.test.js
new file mode 100644
index 0000000000000000000000000000000000000000..73029564eb584bf87d55745be9f9883fab472dbf
--- /dev/null
+++ b/test/unit/util/drag-utils.test.js
@@ -0,0 +1,41 @@
+import {indexForPositionOnList} from '../../../src/lib/drag-utils';
+
+const box = (top, right, bottom, left) => ({top, right, bottom, left});
+
+describe('indexForPositionOnList', () => {
+    test('returns null when not given any boxes', () => {
+        expect(indexForPositionOnList({x: 0, y: 0}, [])).toEqual(null);
+    });
+
+    test('wrapped list with incomplete last row', () => {
+        const boxes = [
+            box(0, 100, 100, 0), // index: 0
+            box(0, 200, 100, 100), // index: 1
+            box(0, 300, 100, 200), // index: 2
+            box(100, 100, 200, 0), // index: 3 (second row)
+            box(100, 200, 200, 100) // index: 4 (second row, left incomplete intentionally)
+        ];
+
+        // Inside the second box.
+        expect(indexForPositionOnList({x: 150, y: 50}, boxes)).toEqual(1);
+
+        // On the border edge of the first and second box. Given to the first box.
+        expect(indexForPositionOnList({x: 100, y: 50}, boxes)).toEqual(0);
+
+        // Off the top/left edge.
+        expect(indexForPositionOnList({x: -100, y: -100}, boxes)).toEqual(0);
+
+        // Off the left edge, in the second row.
+        expect(indexForPositionOnList({x: -100, y: 175}, boxes)).toEqual(3);
+
+        // Off the right edge, in the first row.
+        expect(indexForPositionOnList({x: 400, y: 75}, boxes)).toEqual(2);
+
+        // Off the top edge, middle of second item.
+        expect(indexForPositionOnList({x: 150, y: -75}, boxes)).toEqual(1);
+
+        // Within the right edge bounds, but on the second (incomplete) row.
+        // This tests that wrapped lists with incomplete final rows work correctly.
+        expect(indexForPositionOnList({x: 375, y: 175}, boxes)).toEqual(4);
+    });
+});