diff --git a/package.json b/package.json
index d8b102fe61a9c7694a77aab92711a489b2203046..38bd0168678c7b649c705dee4695825b3dd1943d 100644
--- a/package.json
+++ b/package.json
@@ -77,7 +77,7 @@
     "react-ga": "2.4.1",
     "react-intl": "2.4.0",
     "react-intl-redux": "0.7.0",
-    "react-modal": "3.3.1",
+    "react-modal": "3.3.2",
     "react-redux": "5.0.7",
     "react-responsive": "4.0.5",
     "react-style-proptype": "3.2.1",
@@ -89,9 +89,9 @@
     "redux-throttle": "0.1.1",
     "rimraf": "^2.6.1",
     "scratch-audio": "0.1.0-prerelease.1516198804",
-    "scratch-blocks": "0.1.0-prerelease.1520519504",
+    "scratch-blocks": "0.1.0-prerelease.1521811349",
     "scratch-l10n": "2.0.20180108132626",
-    "scratch-paint": "0.2.0-prerelease.20180309170325",
+    "scratch-paint": "0.2.0-prerelease.20180320181154",
     "scratch-render": "0.1.0-prerelease.1516837442",
     "scratch-storage": "0.4.0",
     "scratch-vm": "0.1.0-prerelease.1521814818-prerelease.1521814838",
diff --git a/src/components/asset-panel/selector.jsx b/src/components/asset-panel/selector.jsx
index f2ab58cae0dc4c084cc525ffc50252eaad777b8e..cc5abed4d55d1cb4e1af2b441dfefc90e40aecd5 100644
--- a/src/components/asset-panel/selector.jsx
+++ b/src/components/asset-panel/selector.jsx
@@ -59,9 +59,9 @@ const Selector = props => {
 
 Selector.propTypes = {
     buttons: PropTypes.arrayOf(PropTypes.shape({
-        message: PropTypes.string.isRequired,
+        title: PropTypes.string.isRequired,
         img: PropTypes.string.isRequired,
-        onClick: PropTypes.func.isRequired
+        onClick: PropTypes.func
     })),
     items: PropTypes.arrayOf(PropTypes.shape({
         url: PropTypes.string,
diff --git a/src/components/browser-modal/browser-modal.jsx b/src/components/browser-modal/browser-modal.jsx
index c410f7f62d26e359a300e2bb80072e8ed9bcf248..9052d5f57075796500df46e8f172cca3405e0f8f 100644
--- a/src/components/browser-modal/browser-modal.jsx
+++ b/src/components/browser-modal/browser-modal.jsx
@@ -9,7 +9,7 @@ import styles from './browser-modal.css';
 const messages = defineMessages({
     label: {
         id: 'gui.unsupportedBrowser.label',
-        defaultMessage: 'Internet Explorer is not supported',
+        defaultMessage: 'Browser is not supported',
         description: ''
     }
 });
@@ -31,7 +31,7 @@ const BrowserModal = ({intl, ...props}) => (
             <p>
                 { /* eslint-disable max-len */ }
                 <FormattedMessage
-                    defaultMessage="We're very sorry, but Scratch 3.0 does not support Internet Explorer. We recommend trying a newer browser such as Google Chrome, Mozilla Firefox, or Microsoft Edge."
+                    defaultMessage="We're very sorry, but Scratch 3.0 does not support Internet Explorer, Opera or Silk. We recommend trying a newer browser such as Google Chrome, Mozilla Firefox, or Microsoft Edge."
                     description="Unsupported browser description"
                     id="gui.unsupportedBrowser.description"
                 />
diff --git a/src/containers/backdrop-library.jsx b/src/containers/backdrop-library.jsx
index 68f373ece16c7181c259a7b0c35db5d2442752b1..8f796225e81a8e8740b4100b408269fbbc22b2b2 100644
--- a/src/containers/backdrop-library.jsx
+++ b/src/containers/backdrop-library.jsx
@@ -23,11 +23,7 @@ class BackdropLibrary extends React.Component {
             bitmapResolution: item.info.length > 2 ? item.info[2] : 1,
             skinId: null
         };
-        this.props.vm.addBackdrop(item.md5, vmBackdrop).then(() => {
-            if (this.props.onNewBackdrop) {
-                this.props.onNewBackdrop();
-            }
-        });
+        this.props.vm.addBackdrop(item.md5, vmBackdrop);
         analytics.event({
             category: 'library',
             action: 'Select Backdrop',
@@ -47,7 +43,6 @@ class BackdropLibrary extends React.Component {
 }
 
 BackdropLibrary.propTypes = {
-    onNewBackdrop: PropTypes.func.isRequired,
     onRequestClose: PropTypes.func,
     vm: PropTypes.instanceOf(VM).isRequired
 };
diff --git a/src/containers/blocks.jsx b/src/containers/blocks.jsx
index bedbbdbade90c98fe17572495521ca28478cdef9..504291036f35d8f99d61c062b53c31d7e0315dbe 100644
--- a/src/containers/blocks.jsx
+++ b/src/containers/blocks.jsx
@@ -95,9 +95,11 @@ class Blocks extends React.Component {
         }
 
         if (prevProps.toolboxXML !== this.props.toolboxXML) {
-            const selectedCategoryName = this.workspace.toolbox_.getSelectedItem().name_;
+            const categoryName = this.workspace.toolbox_.getSelectedCategoryName();
+            const offset = this.workspace.toolbox_.getCategoryScrollOffset();
             this.workspace.updateToolbox(this.props.toolboxXML);
-            this.workspace.toolbox_.setSelectedCategoryByName(selectedCategoryName);
+            const currentCategoryPos = this.workspace.toolbox_.getCategoryPositionByName(categoryName);
+            this.workspace.toolbox_.setFlyoutScrollPos(currentCategoryPos + offset);
         }
         if (this.props.isVisible === prevProps.isVisible) {
             return;
diff --git a/src/containers/costume-library.jsx b/src/containers/costume-library.jsx
index 2e631bd95e4339344eac40cbfc3f691f4c2cd87b..afdd56a5d292a714fa81d954f6ee27fede66ab29 100644
--- a/src/containers/costume-library.jsx
+++ b/src/containers/costume-library.jsx
@@ -23,9 +23,7 @@ class CostumeLibrary extends React.PureComponent {
             bitmapResolution: item.info.length > 2 ? item.info[2] : 1,
             skinId: null
         };
-        this.props.vm.addCostume(item.md5, vmCostume).then(() => {
-            this.props.onNewCostume();
-        });
+        this.props.vm.addCostume(item.md5, vmCostume);
         analytics.event({
             category: 'library',
             action: 'Select Costume',
@@ -45,7 +43,6 @@ class CostumeLibrary extends React.PureComponent {
 }
 
 CostumeLibrary.propTypes = {
-    onNewCostume: PropTypes.func.isRequired,
     onRequestClose: PropTypes.func,
     vm: PropTypes.instanceOf(VM).isRequired
 };
diff --git a/src/containers/costume-tab.jsx b/src/containers/costume-tab.jsx
index 3668a94fb1bc57fc86f19beb97f3f2bd9af86371..37fd8a12e1e490933ec5f843eb12e667c2316b10 100644
--- a/src/containers/costume-tab.jsx
+++ b/src/containers/costume-tab.jsx
@@ -67,7 +67,6 @@ class CostumeTab extends React.Component {
             'handleSelectCostume',
             'handleDeleteCostume',
             'handleDuplicateCostume',
-            'handleNewCostume',
             'handleNewBlankCostume',
             'handleSurpriseCostume',
             'handleSurpriseBackdrop'
@@ -96,11 +95,20 @@ class CostumeTab extends React.Component {
             return;
         }
 
-        // If switching editing targets, update the costume index
-        if (this.props.editingTarget !== editingTarget) {
+        if (this.props.editingTarget === editingTarget) {
+            // If costumes have been added or removed, change costumes to the editing target's
+            // current costume.
+            const oldTarget = this.props.sprites[editingTarget] ?
+                this.props.sprites[editingTarget] : this.props.stage;
+            // @todo: Find and switch to the index of the costume that is new. This is blocked by
+            // https://github.com/LLK/scratch-vm/issues/967
+            // Right now, you can land on the wrong costume if a costume changing script is running.
+            if (oldTarget.costumeCount !== target.costumeCount) {
+                this.setState({selectedCostumeIndex: target.currentCostume});
+            }
+        } else {
+            // If switching editing targets, update the costume index
             this.setState({selectedCostumeIndex: target.currentCostume});
-        } else if (this.state.selectedCostumeIndex > target.costumes.length - 1) {
-            this.setState({selectedCostumeIndex: target.costumes.length - 1});
         }
     }
     handleSelectCostume (costumeIndex) {
@@ -111,14 +119,7 @@ class CostumeTab extends React.Component {
         this.props.vm.deleteCostume(costumeIndex);
     }
     handleDuplicateCostume (costumeIndex) {
-        this.props.vm.duplicateCostume(costumeIndex).then(() => {
-            this.setState({selectedCostumeIndex: costumeIndex + 1});
-        });
-    }
-    handleNewCostume () {
-        if (!this.props.vm.editingTarget) return;
-        const costumes = this.props.vm.editingTarget.getCostumes() || [];
-        this.setState({selectedCostumeIndex: Math.max(costumes.length - 1, 0)});
+        this.props.vm.duplicateCostume(costumeIndex);
     }
     handleNewBlankCostume () {
         const emptyItem = costumeLibraryContent.find(item => (
@@ -133,9 +134,7 @@ class CostumeTab extends React.Component {
             skinId: null
         };
 
-        this.props.vm.addCostume(emptyItem.md5, vmCostume).then(() => {
-            this.handleNewCostume();
-        });
+        this.props.vm.addCostume(emptyItem.md5, vmCostume);
     }
     handleSurpriseCostume () {
         const item = costumeLibraryContent[Math.floor(Math.random() * costumeLibraryContent.length)];
@@ -146,9 +145,7 @@ class CostumeTab extends React.Component {
             bitmapResolution: item.info.length > 2 ? item.info[2] : 1,
             skinId: null
         };
-        this.props.vm.addCostume(item.md5, vmCostume).then(() => {
-            this.handleNewCostume();
-        });
+        this.props.vm.addCostume(item.md5, vmCostume);
     }
     handleSurpriseBackdrop () {
         const item = backdropLibraryContent[Math.floor(Math.random() * backdropLibraryContent.length)];
@@ -159,12 +156,9 @@ class CostumeTab extends React.Component {
             bitmapResolution: item.info.length > 2 ? item.info[2] : 1,
             skinId: null
         };
-        this.props.vm.addCostume(item.md5, vmCostume).then(() => {
-            this.handleNewCostume();
-        });
+        this.props.vm.addCostume(item.md5, vmCostume);
     }
     render () {
-        // For paint wrapper
         const {
             intl,
             onNewLibraryBackdropClick,
@@ -173,15 +167,11 @@ class CostumeTab extends React.Component {
             costumeLibraryVisible,
             onRequestCloseBackdropLibrary,
             onRequestCloseCostumeLibrary,
-            ...props
-        } = this.props;
-
-        const {
             editingTarget,
             sprites,
             stage,
             vm
-        } = props;
+        } = this.props;
 
         const target = editingTarget && sprites[editingTarget] ? sprites[editingTarget] : stage;
 
@@ -236,14 +226,12 @@ class CostumeTab extends React.Component {
                 {costumeLibraryVisible ? (
                     <CostumeLibrary
                         vm={vm}
-                        onNewCostume={this.handleNewCostume}
                         onRequestClose={onRequestCloseCostumeLibrary}
                     />
                 ) : null}
                 {backdropLibraryVisible ? (
                     <BackdropLibrary
                         vm={vm}
-                        onNewBackdrop={this.handleNewCostume}
                         onRequestClose={onRequestCloseBackdropLibrary}
                     />
                 ) : null}
diff --git a/src/containers/error-boundary.jsx b/src/containers/error-boundary.jsx
index d7083f05273c2b1003551f5059995e2a448ecb05..4c473a7374a05e14b4f99772a499e72a1f8f8eb3 100644
--- a/src/containers/error-boundary.jsx
+++ b/src/containers/error-boundary.jsx
@@ -35,7 +35,12 @@ class ErrorBoundary extends React.Component {
 
     render () {
         if (this.state.hasError) {
-            if (platform.name === 'IE') {
+            // don't use array.includes because that's something that causes IE to crash.
+            if (
+                platform.name === 'IE' ||
+                platform.name === 'Opera' ||
+                platform.name === 'Opera Mini' ||
+                platform.name === 'Silk') {
                 return <BrowserModalComponent onBack={this.handleBack} />;
             }
             return <CrashMessageComponent onReload={this.handleReload} />;
diff --git a/src/containers/preview-modal.jsx b/src/containers/preview-modal.jsx
index 1209f251f0445da6a5aa0d65f8fa6d343ce5654e..3fa6c8f03f97eea04d8e1ed3b69c50ca2e070b0a 100644
--- a/src/containers/preview-modal.jsx
+++ b/src/containers/preview-modal.jsx
@@ -36,10 +36,7 @@ class PreviewModal extends React.Component {
         this.props.onViewProject();
     }
     supportedBrowser () {
-        if (platform.name === 'IE') {
-            return false;
-        }
-        return true;
+        return !['IE', 'Opera', 'Opera Mini', 'Silk', 'Vivaldi'].includes(platform.name);
     }
     render () {
         return (this.supportedBrowser() ?
diff --git a/src/containers/target-pane.jsx b/src/containers/target-pane.jsx
index 317cfcbb32b5d0dfff2403ee406381036477f8cf..97d6dffaa31e4c89e9f09a02d0e7acda5bb8fe76 100644
--- a/src/containers/target-pane.jsx
+++ b/src/containers/target-pane.jsx
@@ -89,6 +89,7 @@ class TargetPane extends React.Component {
     render () {
         const {
             onActivateTab, // eslint-disable-line no-unused-vars
+            onReceivedBlocks, // eslint-disable-line no-unused-vars
             ...componentProps
         } = this.props;
         return (
diff --git a/src/lib/make-toolbox-xml.js b/src/lib/make-toolbox-xml.js
index 677dc2efad0b1c2d3b7ddb650d998ca9db441258..4bfb8b2e5f93e287bfd35c9fbe63a7ec8a328b06 100644
--- a/src/lib/make-toolbox-xml.js
+++ b/src/lib/make-toolbox-xml.js
@@ -310,13 +310,17 @@ const sound = function () {
     `;
 };
 
-const events = function () {
+const events = function (isStage) {
     return `
     <category name="Events" colour="#FFD500" secondaryColour="#CC9900">
         <block type="event_whenflagclicked"/>
         <block type="event_whenkeypressed">
         </block>
-        <block type="event_whenthisspriteclicked"/>
+        ${isStage ? `
+            <block type="event_whenstageclicked"/>
+        ` : `
+            <block type="event_whenthisspriteclicked"/>
+        `}
         <block type="event_whenbackdropswitchesto">
         </block>
         ${blockSeparator}
@@ -430,7 +434,11 @@ const sensing = function (isStage) {
         </block>
         <block id="answer" type="sensing_answer"/>
         ${blockSeparator}
-        <block type="sensing_keypressed"/>
+        <block type="sensing_keypressed">
+            <value name="KEY_OPTION">
+                <shadow type="sensing_keyoptions"/>
+            </value>
+        </block>
         <block type="sensing_mousedown"/>
         <block type="sensing_mousex"/>
         <block type="sensing_mousey"/>
diff --git a/src/lib/vm-listener-hoc.jsx b/src/lib/vm-listener-hoc.jsx
index 8e7a0ca17d1062bb41491e2bbac5cb1b63dffeab..3f1453b548daf4aa77493a0330fbf349d1397c28 100644
--- a/src/lib/vm-listener-hoc.jsx
+++ b/src/lib/vm-listener-hoc.jsx
@@ -52,11 +52,6 @@ const vmListenerHOC = function (WrappedComponent) {
                 keyCode: e.keyCode,
                 isDown: true
             });
-
-            // Don't stop browser keyboard shortcuts
-            if (e.metaKey || e.altKey || e.ctrlKey) return;
-
-            e.preventDefault();
         }
         handleKeyUp (e) {
             // Always capture up events,
@@ -99,9 +94,7 @@ const vmListenerHOC = function (WrappedComponent) {
         attachKeyboardEvents: true
     };
     const mapStateToProps = state => ({
-        vm: state.vm,
-        hoveredSprite: state.hoveredTarget.sprite,
-        editingTarget: state.targets.editingTarget
+        vm: state.vm
     });
     const mapDispatchToProps = dispatch => ({
         onTargetsUpdate: data => {