diff --git a/src/components/backpack/backpack.jsx b/src/components/backpack/backpack.jsx
index 3711cafd412d0e52e17cf61c1abce4ee6e6c80ae..563ec84a3dc7bcb7d85bd9fdf1a105451a17ee9c 100644
--- a/src/components/backpack/backpack.jsx
+++ b/src/components/backpack/backpack.jsx
@@ -1,6 +1,7 @@
 import React from 'react';
 import PropTypes from 'prop-types';
 import {FormattedMessage} from 'react-intl';
+import DragConstants from '../../lib/drag-constants';
 import {ComingSoonTooltip} from '../coming-soon/coming-soon.jsx';
 import SpriteSelectorItem from '../../containers/sprite-selector-item.jsx';
 import styles from './backpack.css';
@@ -8,6 +9,13 @@ import styles from './backpack.css';
 // TODO make sprite selector item not require onClick
 const noop = () => {};
 
+const dragTypeMap = {
+    costume: DragConstants.BACKPACK_COSTUME,
+    sound: DragConstants.BACKPACK_SOUND,
+    code: DragConstants.BACKPACK_CODE,
+    sprite: DragConstants.BACKPACK_SPRITE
+};
+
 const Backpack = ({contents, error, expanded, loading, onToggle}) => (
     <div className={styles.backpackContainer}>
         <div
@@ -60,6 +68,8 @@ const Backpack = ({contents, error, expanded, loading, onToggle}) => (
                                         className={styles.backpackItem}
                                         costumeURL={item.thumbnailUrl}
                                         details={item.name}
+                                        dragPayload={item}
+                                        dragType={dragTypeMap[item.type]}
                                         key={item.id}
                                         name={item.type}
                                         selected={false}
diff --git a/src/containers/backpack.jsx b/src/containers/backpack.jsx
index 878a6cd5f26acd9e8d0669d9777635280f74d362..a489279b28c24ac60e335d86961dc9cfd5223679 100644
--- a/src/containers/backpack.jsx
+++ b/src/containers/backpack.jsx
@@ -4,6 +4,7 @@ import bindAll from 'lodash.bindall';
 import BackpackComponent from '../components/backpack/backpack.jsx';
 import {getBackpackContents} from '../lib/backpack-api';
 import {connect} from 'react-redux';
+import storage from '../lib/storage';
 
 class Backpack extends React.Component {
     constructor (props) {
@@ -20,6 +21,14 @@ class Backpack extends React.Component {
             expanded: false,
             contents: []
         };
+
+        // If a host is given, add it as a web source to the storage module
+        if (props.host) {
+            storage.addWebSource(
+                [storage.AssetType.ImageVector, storage.AssetType.ImageBitmap, storage.AssetType.Sound],
+                asset => `${props.host}/${asset.assetId}.${asset.dataFormat}`
+            );
+        }
     }
     handleToggle () {
         const newState = !this.state.expanded;
diff --git a/src/containers/costume-tab.jsx b/src/containers/costume-tab.jsx
index 965f81abef7b99417f7f521d932cd6460dbd80c9..9cf6051a252abe1f9a7baa9ce30cb62d41a8e321 100644
--- a/src/containers/costume-tab.jsx
+++ b/src/containers/costume-tab.jsx
@@ -19,6 +19,11 @@ import {
     openBackdropLibrary
 } from '../reducers/modals';
 
+import {
+    activateTab,
+    SOUNDS_TAB_INDEX
+} from '../reducers/editor-tab';
+
 import addLibraryBackdropIcon from '../components/asset-panel/icon--add-backdrop-lib.svg';
 import addLibraryCostumeIcon from '../components/asset-panel/icon--add-costume-lib.svg';
 import fileUploadIcon from '../components/action-menu/icon--file-upload.svg';
@@ -191,14 +196,22 @@ class CostumeTab extends React.Component {
         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)});
+        } else if (dropInfo.dragType === DragConstants.BACKPACK_COSTUME) {
+            this.props.vm.addCostume(dropInfo.payload.body, {
+                name: dropInfo.payload.name
+            });
+        } else if (dropInfo.dragType === DragConstants.BACKPACK_SOUND) {
+            this.props.onActivateSoundsTab();
+            this.props.vm.addSound({
+                md5: dropInfo.payload.body,
+                name: dropInfo.payload.name
+            });
         }
     }
     setFileInput (input) {
@@ -303,6 +316,7 @@ CostumeTab.propTypes = {
     cameraModalVisible: PropTypes.bool,
     editingTarget: PropTypes.string,
     intl: intlShape,
+    onActivateSoundsTab: PropTypes.func.isRequired,
     onNewCostumeFromCameraClick: PropTypes.func.isRequired,
     onNewLibraryBackdropClick: PropTypes.func.isRequired,
     onNewLibraryCostumeClick: PropTypes.func.isRequired,
@@ -333,6 +347,7 @@ const mapStateToProps = state => ({
 });
 
 const mapDispatchToProps = dispatch => ({
+    onActivateSoundsTab: () => dispatch(activateTab(SOUNDS_TAB_INDEX)),
     onNewLibraryBackdropClick: e => {
         e.preventDefault();
         dispatch(openBackdropLibrary());
diff --git a/src/containers/sound-tab.jsx b/src/containers/sound-tab.jsx
index 097457d8a45a0e0b28ce5eaf26712f2da1a8becb..74b1eb5d406e16a43efd6c88ba227e220096d241 100644
--- a/src/containers/sound-tab.jsx
+++ b/src/containers/sound-tab.jsx
@@ -28,6 +28,11 @@ import {
     openSoundRecorder
 } from '../reducers/modals';
 
+import {
+    activateTab,
+    COSTUMES_TAB_INDEX
+} from '../reducers/editor-tab';
+
 class SoundTab extends React.Component {
     constructor (props) {
         super(props);
@@ -120,8 +125,6 @@ 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];
@@ -130,6 +133,16 @@ class SoundTab extends React.Component {
                 dropInfo.index, dropInfo.newIndex);
 
             this.setState({selectedSoundIndex: sprite.sounds.indexOf(activeSound)});
+        } else if (dropInfo.dragType === DragConstants.BACKPACK_COSTUME) {
+            this.props.onActivateCostumesTab();
+            this.props.vm.addCostume(dropInfo.payload.body, {
+                name: dropInfo.payload.name
+            });
+        } else if (dropInfo.dragType === DragConstants.BACKPACK_SOUND) {
+            this.props.vm.addSound({
+                md5: dropInfo.payload.body,
+                name: dropInfo.payload.name
+            }).then(this.handleNewSound);
         }
     }
 
@@ -235,6 +248,7 @@ class SoundTab extends React.Component {
 SoundTab.propTypes = {
     editingTarget: PropTypes.string,
     intl: intlShape,
+    onActivateCostumesTab: PropTypes.func.isRequired,
     onNewSoundFromLibraryClick: PropTypes.func.isRequired,
     onNewSoundFromRecordingClick: PropTypes.func.isRequired,
     onRequestCloseSoundLibrary: PropTypes.func.isRequired,
@@ -264,6 +278,7 @@ const mapStateToProps = state => ({
 });
 
 const mapDispatchToProps = dispatch => ({
+    onActivateCostumesTab: () => dispatch(activateTab(COSTUMES_TAB_INDEX)),
     onNewSoundFromLibraryClick: e => {
         e.preventDefault();
         dispatch(openSoundLibrary());
diff --git a/src/containers/sprite-selector-item.jsx b/src/containers/sprite-selector-item.jsx
index 5efe494280f1fa004b55a4502f5027a37634e238..4c968f82731222227f9c5cbf6cdb2b15beed4287 100644
--- a/src/containers/sprite-selector-item.jsx
+++ b/src/containers/sprite-selector-item.jsx
@@ -52,7 +52,8 @@ class SpriteSelectorItem extends React.Component {
                 currentOffset: currentOffset,
                 dragging: true,
                 dragType: this.props.dragType,
-                index: this.props.index
+                index: this.props.index,
+                payload: this.props.dragPayload
             });
             this.noClick = true;
         }
@@ -98,6 +99,7 @@ class SpriteSelectorItem extends React.Component {
             onClick,
             onDeleteButtonClick,
             onDuplicateButtonClick,
+            dragPayload,
             receivedBlocks,
             /* eslint-enable no-unused-vars */
             ...props
@@ -120,6 +122,10 @@ SpriteSelectorItem.propTypes = {
     assetId: PropTypes.string,
     costumeURL: PropTypes.string,
     dispatchSetHoveredSprite: PropTypes.func.isRequired,
+    dragPayload: PropTypes.shape({
+        name: PropTypes.string,
+        body: PropTypes.string
+    }),
     dragType: PropTypes.string,
     id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
     index: PropTypes.number,
diff --git a/src/lib/drag-constants.js b/src/lib/drag-constants.js
index 86f064da90e556b50b5653be17aa8d9f17708811..ce4da5af2a8d362feb8ddc81ff171331e3487e63 100644
--- a/src/lib/drag-constants.js
+++ b/src/lib/drag-constants.js
@@ -1,5 +1,10 @@
 export default {
     SOUND: 'SOUND',
     COSTUME: 'COSTUME',
-    SPRITE: 'SPRITE'
+    SPRITE: 'SPRITE',
+
+    BACKPACK_SOUND: 'BACKPACK_SOUND',
+    BACKPACK_COSTUME: 'BACKPACK_COSTUME',
+    BACKPACK_SPRITE: 'BACKPACK_SPRITE',
+    BACKPACK_CODE: 'BACKPACK_CODE'
 };