diff --git a/package.json b/package.json
index 41888d82b5e5fbebb8f9351850a995efb5eb155f..5a2fe29f397b79f2893c7ddffc320e38a340a033 100644
--- a/package.json
+++ b/package.json
@@ -52,6 +52,7 @@
     "react-modal": "1.7.2",
     "react-redux": "5.0.3",
     "react-style-proptype": "2.0.1",
+    "react-tabs": "0.8.2",
     "redux": "3.6.0",
     "redux-throttle": "0.1.1",
     "scratch-audio": "latest",
diff --git a/src/components/asset-panel/asset-panel.css b/src/components/asset-panel/asset-panel.css
new file mode 100644
index 0000000000000000000000000000000000000000..8ff5ca4cb3d41a9f37e87a15f0ef3b2f63a749f7
--- /dev/null
+++ b/src/components/asset-panel/asset-panel.css
@@ -0,0 +1,18 @@
+@import "../../css/units.css";
+@import "../../css/colors.css";
+
+.wrapper {
+    display: flex;
+    flex-grow: 1;
+    border: 1px solid $ui-pane-border;
+    border-top-right-radius: $space;
+    background: $ui-pane-gray;
+    font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
+    font-size: 0.85rem;
+}
+
+.detail-area {
+    flex-grow: 1;
+    flex-shrink: 0;
+    border-left: 1px solid $ui-pane-border;
+}
diff --git a/src/components/asset-panel/asset-panel.jsx b/src/components/asset-panel/asset-panel.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..2d6ea710b0294da15989db524ee126ce405f4141
--- /dev/null
+++ b/src/components/asset-panel/asset-panel.jsx
@@ -0,0 +1,23 @@
+const React = require('react');
+
+const Box = require('../box/box.jsx');
+const Selector = require('./selector.jsx');
+const styles = require('./asset-panel.css');
+
+const AssetPanel = props => (
+    <Box className={styles.wrapper}>
+        <Selector
+            className={styles.selector}
+            {...props}
+        />
+        <Box className={styles.detailArea}>
+            {/* @todo editor area */}
+        </Box>
+    </Box>
+);
+
+AssetPanel.propTypes = {
+    ...Selector.propTypes
+};
+
+module.exports = AssetPanel;
diff --git a/src/components/target-pane/icon--sound-dark.svg b/src/components/asset-panel/icon--sound.svg
similarity index 100%
rename from src/components/target-pane/icon--sound-dark.svg
rename to src/components/asset-panel/icon--sound.svg
diff --git a/src/components/asset-panel/selector.css b/src/components/asset-panel/selector.css
new file mode 100644
index 0000000000000000000000000000000000000000..8964c9b13923e9782544fabfaf090867f2c30ebe
--- /dev/null
+++ b/src/components/asset-panel/selector.css
@@ -0,0 +1,38 @@
+/* Need to use a fixed height for the new container to make list scrollable */
+$new-height: 60px;
+
+.wrapper {
+    width: 200px;
+    position: relative;
+}
+
+.new-item {
+    background: white;
+    border-bottom: 1px solid #ddd;
+    height: $new-height;
+    display: flex;
+    align-items: center;
+    padding-left: 1.75rem;
+    cursor: pointer;
+    font-size: 0.85rem;
+}
+
+.list-area {
+    position: absolute;
+    width: 100%;
+    top: 60px;
+    height: calc(100% - $new-height);
+    overflow-y: scroll;
+}
+
+.list-item {
+    width: 5rem;
+    min-height: 5rem;
+    margin: 1rem auto;
+}
+
+.delete-button {
+    position: absolute;
+    top: 2px;
+    right: 2px;
+}
diff --git a/src/components/asset-panel/selector.jsx b/src/components/asset-panel/selector.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..ef92e7696821d42b380d9a42e301b46fd509cacc
--- /dev/null
+++ b/src/components/asset-panel/selector.jsx
@@ -0,0 +1,56 @@
+const React = require('react');
+
+const SpriteSelectorItem = require('../../containers/sprite-selector-item.jsx');
+
+const Box = require('../box/box.jsx');
+const styles = require('./selector.css');
+
+const Selector = props => {
+    const {
+        items,
+        newText,
+        selectedItemIndex,
+        onDeleteClick,
+        onItemClick,
+        onNewClick
+    } = props;
+
+    return (
+        <Box className={styles.wrapper}>
+            <Box
+                className={styles.newItem}
+                onClick={onNewClick}
+            >
+                {newText}
+            </Box>
+            <Box className={styles.listArea}>
+                {items.map((item, index) => (
+                    <SpriteSelectorItem
+                        className={styles.listItem}
+                        costumeURL={item.image}
+                        id={index}
+                        key={`asset-${index}`}
+                        name={item.name}
+                        selected={index === selectedItemIndex}
+                        onClick={onItemClick}
+                        onDeleteButtonClick={onDeleteClick}
+                    />
+                ))}
+            </Box>
+        </Box>
+    );
+};
+
+Selector.propTypes = {
+    items: React.PropTypes.arrayOf(React.PropTypes.shape({
+        image: React.PropTypes.string,
+        name: React.PropTypes.string
+    })),
+    newText: React.PropTypes.string,
+    onDeleteClick: React.PropTypes.func,
+    onItemClick: React.PropTypes.func,
+    onNewClick: React.PropTypes.func,
+    selectedItemIndex: React.PropTypes.number
+};
+
+module.exports = Selector;
diff --git a/src/components/blocks/blocks.css b/src/components/blocks/blocks.css
index 137a8f88664dc0851555d389cdc6c1981f8775ed..80689e48c3d9cafbc2f7882dac12a17ffd5d6853 100644
--- a/src/components/blocks/blocks.css
+++ b/src/components/blocks/blocks.css
@@ -1,4 +1,7 @@
-$border-style: 1px solid #dbdbdb;
+@import "../../css/units.css";
+@import "../../css/colors.css";
+
+$border-style: 1px solid $ui-pane-border;
 
 .blocks :global(.injectionDiv){
     position: absolute;
@@ -7,12 +10,7 @@ $border-style: 1px solid #dbdbdb;
     bottom: 0;
     left: 0;
     border: $border-style;
-
-    /* 
-        @todo: using _space doesn't compute to the right amount? 
-        Related to `scratch-blocks` 
-    */
-    border-top-right-radius: 0.75rem;
+    border-top-right-radius: $space;
 }
 
 .blocks :global(.blocklyMainBackground) {
diff --git a/src/components/gui/gui.css b/src/components/gui/gui.css
index e587122a7c7bdc8131b7e9fbe99de0df03fdbfdb..e2fe4db4333437657c6c3009fdde3c8df478b158 100644
--- a/src/components/gui/gui.css
+++ b/src/components/gui/gui.css
@@ -21,24 +21,71 @@
     height: 100%;
 }
 
-.blocks-wrapper {
-    /*
-        scratch-blocks is based on absolute positioning, which injects
-        inside this element and becomes the child
-    */
+.editor-wrapper {
+    flex-basis: 600px;
+    flex-grow: 1;
+    flex-shrink: 0;
     position: relative;
 
-    flex-basis: 600px;
+    display: flex;
+    flex-direction: column;
+}
+
+.tab-list {
+    height: $stage-menu-height;
+    width: 250px; /* Match width of the toolbox */
+    display: flex;
+    align-items: flex-end;
+    flex-shrink: 0;
+
+    font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
+    font-weight: 500;
+    font-size: 0.80rem;
+
+    /* Overrides for react-tabs styling */
+    margin: 0 !important;
+    border-bottom: 0 !important;
+}
+
+.tab-list .tab {
+    flex-grow: 1;
+    height: 80%;
+    margin-left: 1px;
+
+    border-radius: $space $space 0 0;
+    border: none;
+
+    background-color: #F6F8FA;
+    color: #9AA1B5;
+
+    display: flex;
+    justify-content: center;
+    align-items: center;
+}
+
+
+.tab-list .tab[aria-selected="true"] {
+    color: #40B9F5;
+}
+
+.tabs {
+    position: relative;
     flex-grow: 1;
     flex-shrink: 0;
+    display: flex;
+    flex-direction: column;
+}
 
-    /*
-        Normally we'd use padding, but the absolute positioning ignores it,
-        so use margin instead. Temporary, until tabs are inserted.
-    */
-    margin-top: $stage-menu-height;
+.tab-panel {
+    position: relative;
+    flex-grow: 1;
+    flex-shrink: 0;
+    display: flex;
+}
 
-    background: #e8edf1;
+.blocks-wrapper {
+    flex-grow: 1;
+    position: relative;
 }
 
 .stage-and-target-wrapper {
@@ -66,7 +113,7 @@
     padding-right: $space;
 
     /* Hides negative space between edge of rounded corners + container, when selected */
-    user-select: none; 
+    user-select: none;
 }
 
 .target-wrapper {
diff --git a/src/components/gui/gui.jsx b/src/components/gui/gui.jsx
index 36dbec0e2a0e77c04b9b9391c6d18637d238a1d3..0a7c2704dd892d66cd859287cf049cac0ba240d3 100644
--- a/src/components/gui/gui.jsx
+++ b/src/components/gui/gui.jsx
@@ -1,12 +1,14 @@
 const React = require('react');
 const VM = require('scratch-vm');
-
 const Blocks = require('../../containers/blocks.jsx');
+const CostumeTab = require('../../containers/costume-tab.jsx');
 const GreenFlag = require('../../containers/green-flag.jsx');
 const TargetPane = require('../../containers/target-pane.jsx');
+const SoundTab = require('../../containers/sound-tab.jsx');
 const Stage = require('../../containers/stage.jsx');
 const StopAll = require('../../containers/stop-all.jsx');
 const MenuBar = require('../menu-bar/menu-bar.jsx');
+const {Tab, Tabs, TabList, TabPanel} = require('react-tabs');
 
 const Box = require('../box/box.jsx');
 const styles = require('./gui.css');
@@ -25,6 +27,14 @@ const GUIComponent = props => {
             </Box>
         );
     }
+
+    // @todo hack to resize blockly manually in case resize happened while hidden
+    const handleTabSelect = tabIndex => {
+        if (tabIndex === 0) {
+            setTimeout(() => window.dispatchEvent(new Event('resize')));
+        }
+    };
+
     return (
         <Box
             className={styles.pageWrapper}
@@ -33,14 +43,35 @@ const GUIComponent = props => {
             <MenuBar />
             <Box className={styles.bodyWrapper}>
                 <Box className={styles.flexWrapper}>
-                    <Box className={styles.blocksWrapper}>
-                        <Blocks
-                            grow={1}
-                            options={{
-                                media: `${basePath}static/blocks-media/`
-                            }}
-                            vm={vm}
-                        />
+                    <Box className={styles.editorWrapper}>
+                        <Tabs
+                            className={styles.tabs}
+                            forceRenderTabPanel={true} // eslint-disable-line react/jsx-boolean-value
+                            onSelect={handleTabSelect}
+                        >
+                            <TabList className={styles.tabList}>
+                                <Tab className={styles.tab}>Scripts</Tab>
+                                <Tab className={styles.tab}>Costumes</Tab>
+                                <Tab className={styles.tab}>Sounds</Tab>
+                            </TabList>
+                            <TabPanel className={styles.tabPanel}>
+                                <Box className={styles.blocksWrapper}>
+                                    <Blocks
+                                        grow={1}
+                                        options={{
+                                            media: `${basePath}static/blocks-media/`
+                                        }}
+                                        vm={vm}
+                                    />
+                                </Box>
+                            </TabPanel>
+                            <TabPanel className={styles.tabPanel}>
+                                <CostumeTab vm={vm} />
+                            </TabPanel>
+                            <TabPanel className={styles.tabPanel}>
+                                <SoundTab vm={vm} />
+                            </TabPanel>
+                        </Tabs>
                     </Box>
 
                     <Box className={styles.stageAndTargetWrapper} >
@@ -48,7 +79,7 @@ const GUIComponent = props => {
                             <GreenFlag vm={vm} />
                             <StopAll vm={vm} />
                         </Box>
-                        
+
                         <Box className={styles.stageWrapper} >
                             <Stage
                                 shrink={0}
diff --git a/src/components/target-pane/target-pane.jsx b/src/components/target-pane/target-pane.jsx
index e4e86e4de306d82d521d101a87590519cc4ab587..d8b3ec3df3776fb47f5cad516549ccd05f8f2ec2 100644
--- a/src/components/target-pane/target-pane.jsx
+++ b/src/components/target-pane/target-pane.jsx
@@ -162,7 +162,6 @@ TargetPane.propTypes = {
     onChangeSpriteY: React.PropTypes.func,
     onDeleteSprite: React.PropTypes.func,
     onNewBackdropClick: React.PropTypes.func,
-    onNewSoundClick: React.PropTypes.func,
     onNewSpriteClick: React.PropTypes.func,
     onRequestCloseBackdropLibrary: React.PropTypes.func,
     onRequestCloseCostumeLibrary: React.PropTypes.func,
diff --git a/src/containers/costume-tab.jsx b/src/containers/costume-tab.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..539e364307967c5b662a244120987532ac0abdca
--- /dev/null
+++ b/src/containers/costume-tab.jsx
@@ -0,0 +1,97 @@
+const React = require('react');
+const bindAll = require('lodash.bindall');
+
+const VM = require('scratch-vm');
+
+const AssetPanel = require('../components/asset-panel/asset-panel.jsx');
+
+const {connect} = require('react-redux');
+
+const {
+    openCostumeLibrary
+} = require('../reducers/modals');
+
+class CostumeTab extends React.Component {
+    constructor (props) {
+        super(props);
+        bindAll(this, [
+            'handleSelectCostume',
+            'handleDeleteCostume'
+        ]);
+        this.state = {selectedCostumeIndex: 0};
+    }
+
+    handleSelectCostume (costumeIndex) {
+        this.setState({selectedCostumeIndex: costumeIndex});
+    }
+
+    handleDeleteCostume (costumeIndex) {
+        // @todo the VM should handle all of this logic
+        const {editingTarget} = this.props.vm;
+
+        if (costumeIndex === editingTarget.currentCostume) {
+            editingTarget.setCostume(costumeIndex - 1);
+        }
+
+        editingTarget.sprite.costumes = editingTarget.sprite.costumes
+            .slice(0, costumeIndex)
+            .concat(editingTarget.sprite.costumes.slice(costumeIndex + 1));
+        this.props.vm.emitTargetsUpdate();
+        // @todo not sure if this is getting redrawn correctly
+        this.props.vm.runtime.requestRedraw();
+
+        this.setState({
+            selectedCostumeIndex: this.state.selectedCostumeIndex % editingTarget.sprite.costumes.length
+        });
+    }
+
+    render () {
+        const {
+            vm,
+            onNewCostumeClick
+        } = this.props;
+
+        const costumes = vm.editingTarget ? vm.editingTarget.sprite.costumes.map(costume => (
+            {
+                image: costume.skin,
+                name: costume.name
+            }
+        )) : [];
+
+        const addText = vm.editingTarget && vm.editingTarget.isStage ? 'Add Backdrop' : 'Add Costume';
+
+        return (
+            <AssetPanel
+                items={costumes}
+                newText={addText}
+                selectedItemIndex={this.state.selectedCostumeIndex}
+                onDeleteClick={this.handleDeleteCostume}
+                onItemClick={this.handleSelectCostume}
+                onNewClick={onNewCostumeClick}
+            />
+        );
+    }
+}
+
+CostumeTab.propTypes = {
+    ...AssetPanel.propTypes,
+    vm: React.PropTypes.instanceOf(VM)
+};
+
+const mapStateToProps = state => ({
+    editingTarget: state.targets.editingTarget,
+    sprites: state.targets.sprites,
+    costumeLibraryVisible: state.modals.costumeLibrary
+});
+
+const mapDispatchToProps = dispatch => ({
+    onNewCostumeClick: e => {
+        e.preventDefault();
+        dispatch(openCostumeLibrary());
+    }
+});
+
+module.exports = connect(
+    mapStateToProps,
+    mapDispatchToProps
+)(CostumeTab);
diff --git a/src/containers/sound-library.jsx b/src/containers/sound-library.jsx
index 010f8e23e5ddcd9a322462e01ee40b05d5805ed6..4db5ee3ea9ee2f9fce60b5df5edb1d739744420f 100644
--- a/src/containers/sound-library.jsx
+++ b/src/containers/sound-library.jsx
@@ -4,7 +4,8 @@ const VM = require('scratch-vm');
 const AudioEngine = require('scratch-audio');
 
 const LibaryComponent = require('../components/library/library.jsx');
-const soundIcon = require('../components/target-pane/icon--sound-dark.svg');
+
+const soundIcon = require('../components/asset-panel/icon--sound.svg');
 
 const soundLibraryContent = require('../lib/libraries/sounds.json');
 
diff --git a/src/containers/sound-tab.jsx b/src/containers/sound-tab.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..960e93ffa8de55a5d7283abd281635ac7559424e
--- /dev/null
+++ b/src/containers/sound-tab.jsx
@@ -0,0 +1,93 @@
+const React = require('react');
+const bindAll = require('lodash.bindall');
+
+const VM = require('scratch-vm');
+
+const AssetPanel = require('../components/asset-panel/asset-panel.jsx');
+const soundIcon = require('../components/asset-panel/icon--sound.svg');
+
+const {connect} = require('react-redux');
+
+const {
+    openSoundLibrary
+} = require('../reducers/modals');
+
+class SoundTab extends React.Component {
+    constructor (props) {
+        super(props);
+        bindAll(this, [
+            'handleSelectSound',
+            'handleDeleteSound'
+        ]);
+        this.state = {selectedSoundIndex: 0};
+    }
+
+    handleSelectSound (soundIndex) {
+        const sound = this.props.vm.editingTarget.sprite.sounds[soundIndex];
+        this.props.vm.editingTarget.audioPlayer.playSound(sound.md5);
+        this.setState({selectedSoundIndex: soundIndex});
+    }
+
+    handleDeleteSound (soundIndex) {
+        // @todo the VM should handle all of this logic
+        const {editingTarget} = this.props.vm;
+        editingTarget.sprite.sounds = editingTarget.sprite.sounds
+            .slice(0, soundIndex)
+            .concat(editingTarget.sprite.sounds.slice(soundIndex + 1));
+        this.props.vm.emitTargetsUpdate();
+        this.props.vm.runtime.requestRedraw();
+
+        this.setState({
+            selectedSoundIndex: this.state.selectedSoundIndex % editingTarget.sprite.sounds.length
+        });
+    }
+
+    render () {
+        const {
+            vm,
+            onNewSoundClick
+        } = this.props;
+
+        const sounds = vm.editingTarget ? vm.editingTarget.sprite.sounds.map(sound => (
+            {
+                image: soundIcon,
+                name: sound.name
+            }
+        )) : [];
+
+
+        return (
+            <AssetPanel
+                items={sounds}
+                newText={'Add Sound'}
+                selectedItemIndex={this.state.selectedSoundIndex}
+                onDeleteClick={this.handleDeleteSound}
+                onItemClick={this.handleSelectSound}
+                onNewClick={onNewSoundClick}
+            />
+        );
+    }
+}
+
+SoundTab.propTypes = {
+    ...AssetPanel.propTypes,
+    vm: React.PropTypes.instanceOf(VM)
+};
+
+const mapStateToProps = state => ({
+    editingTarget: state.targets.editingTarget,
+    sprites: state.targets.sprites,
+    soundLibraryVisible: state.modals.soundLibrary
+});
+
+const mapDispatchToProps = dispatch => ({
+    onNewSoundClick: e => {
+        e.preventDefault();
+        dispatch(openSoundLibrary());
+    }
+});
+
+module.exports = connect(
+    mapStateToProps,
+    mapDispatchToProps
+)(SoundTab);
diff --git a/src/containers/sprite-selector-item.jsx b/src/containers/sprite-selector-item.jsx
index a9e538f30d4f11a3d134d3ef6348dc4ed9f2b554..c2620071585b882c1164284a306ff850bbd8ca8a 100644
--- a/src/containers/sprite-selector-item.jsx
+++ b/src/containers/sprite-selector-item.jsx
@@ -40,7 +40,7 @@ class SpriteSelectorItem extends React.Component {
 
 SpriteSelectorItem.propTypes = {
     costumeURL: React.PropTypes.string,
-    id: React.PropTypes.string,
+    id: React.PropTypes.oneOfType([React.PropTypes.string, React.PropTypes.number]),
     name: React.PropTypes.string,
     onClick: React.PropTypes.func,
     onDeleteButtonClick: React.PropTypes.func,
diff --git a/src/containers/target-pane.jsx b/src/containers/target-pane.jsx
index 9a66cbb779265d2c13c35bc3e3302a7932a01c6e..41feed8e5215c546e464c9e0d073871a8dc302c8 100644
--- a/src/containers/target-pane.jsx
+++ b/src/containers/target-pane.jsx
@@ -5,7 +5,6 @@ const {connect} = require('react-redux');
 
 const {
     openBackdropLibrary,
-    openSoundLibrary,
     openSpriteLibrary,
     closeBackdropLibrary,
     closeCostumeLibrary,
@@ -99,10 +98,6 @@ const mapDispatchToProps = dispatch => ({
         e.preventDefault();
         dispatch(openBackdropLibrary());
     },
-    onNewSoundClick: e => {
-        e.preventDefault();
-        dispatch(openSoundLibrary());
-    },
     onNewSpriteClick: e => {
         e.preventDefault();
         dispatch(openSpriteLibrary());
diff --git a/src/css/colors.css b/src/css/colors.css
index 03bf83a7a0e0ab7ceda4cf04a5c6030aabe0dc5d..c223ea39e92a23c741da43d08ce3a59b691eb356 100644
--- a/src/css/colors.css
+++ b/src/css/colors.css
@@ -1,2 +1,3 @@
-$ui-pane-gray: #f9f9f9;
-$blue: #4c97ff;
+$ui-pane-border: #D9D9D9;
+$ui-pane-gray: #F9F9F9;
+$blue: #4C97FF;