diff --git a/src/components/audio-trimmer/audio-trimmer.jsx b/src/components/audio-trimmer/audio-trimmer.jsx
index d414f3ae25d6350e86f4e5e230e1a76f5ccaa7e4..fc216063a89091523735081e9c3ab407739e316a 100644
--- a/src/components/audio-trimmer/audio-trimmer.jsx
+++ b/src/components/audio-trimmer/audio-trimmer.jsx
@@ -10,23 +10,25 @@ const AudioTrimmer = props => (
         className={styles.absolute}
         ref={props.containerRef}
     >
-        <Box
-            className={classNames(styles.absolute, styles.trimBackground, styles.startTrimBackground)}
-            style={{
-                width: `${100 * props.trimStart}%`
-            }}
-            onMouseDown={props.onTrimStartMouseDown}
-        >
-            <Box className={classNames(styles.absolute, styles.trimBackgroundMask)} />
-            <Box className={classNames(styles.trimLine, styles.startTrimLine)}>
-                <Box className={classNames(styles.trimHandle, styles.topTrimHandle, styles.startTrimHandle)}>
-                    <img src={handleIcon} />
-                </Box>
-                <Box className={classNames(styles.trimHandle, styles.bottomTrimHandle, styles.startTrimHandle)}>
-                    <img src={handleIcon} />
+        {props.trimStart !== null ? (
+            <Box
+                className={classNames(styles.absolute, styles.trimBackground, styles.startTrimBackground)}
+                style={{
+                    width: `${100 * props.trimStart}%`
+                }}
+                onMouseDown={props.onTrimStartMouseDown}
+            >
+                <Box className={classNames(styles.absolute, styles.trimBackgroundMask)} />
+                <Box className={classNames(styles.trimLine, styles.startTrimLine)}>
+                    <Box className={classNames(styles.trimHandle, styles.topTrimHandle, styles.startTrimHandle)}>
+                        <img src={handleIcon} />
+                    </Box>
+                    <Box className={classNames(styles.trimHandle, styles.bottomTrimHandle, styles.startTrimHandle)}>
+                        <img src={handleIcon} />
+                    </Box>
                 </Box>
             </Box>
-        </Box>
+        ) : null}
 
         {props.playhead ? (
             <Box
@@ -37,24 +39,26 @@ const AudioTrimmer = props => (
             />
         ) : null}
 
-        <Box
-            className={classNames(styles.absolute, styles.trimBackground, styles.endTrimBackground)}
-            style={{
-                left: `${100 * props.trimEnd}%`,
-                width: `${100 - 100 * props.trimEnd}%`
-            }}
-            onMouseDown={props.onTrimEndMouseDown}
-        >
-            <Box className={classNames(styles.absolute, styles.trimBackgroundMask)} />
-            <Box className={classNames(styles.trimLine, styles.endTrimLine)}>
-                <Box className={classNames(styles.trimHandle, styles.topTrimHandle, styles.endTrimHandle)}>
-                    <img src={handleIcon} />
-                </Box>
-                <Box className={classNames(styles.trimHandle, styles.bottomTrimHandle, styles.endTrimHandle)}>
-                    <img src={handleIcon} />
+        {props.trimEnd !== null ? (
+            <Box
+                className={classNames(styles.absolute, styles.trimBackground, styles.endTrimBackground)}
+                style={{
+                    left: `${100 * props.trimEnd}%`,
+                    width: `${100 - 100 * props.trimEnd}%`
+                }}
+                onMouseDown={props.onTrimEndMouseDown}
+            >
+                <Box className={classNames(styles.absolute, styles.trimBackgroundMask)} />
+                <Box className={classNames(styles.trimLine, styles.endTrimLine)}>
+                    <Box className={classNames(styles.trimHandle, styles.topTrimHandle, styles.endTrimHandle)}>
+                        <img src={handleIcon} />
+                    </Box>
+                    <Box className={classNames(styles.trimHandle, styles.bottomTrimHandle, styles.endTrimHandle)}>
+                        <img src={handleIcon} />
+                    </Box>
                 </Box>
             </Box>
-        </Box>
+        ) : null }
     </div>
 );
 
@@ -63,8 +67,8 @@ AudioTrimmer.propTypes = {
     onTrimEndMouseDown: PropTypes.func.isRequired,
     onTrimStartMouseDown: PropTypes.func.isRequired,
     playhead: PropTypes.number,
-    trimEnd: PropTypes.number.isRequired,
-    trimStart: PropTypes.number.isRequired
+    trimEnd: PropTypes.number,
+    trimStart: PropTypes.number
 };
 
 export default AudioTrimmer;
diff --git a/src/components/sound-editor/icon--trim.svg b/src/components/sound-editor/icon--trim.svg
new file mode 100644
index 0000000000000000000000000000000000000000..cf64941cab62829199fa7d895442c4c17f506c57
Binary files /dev/null and b/src/components/sound-editor/icon--trim.svg differ
diff --git a/src/components/sound-editor/sound-editor.css b/src/components/sound-editor/sound-editor.css
index 0bb44f115c65dfabf9e3e899e40aca4b0322fb68..1d67886da3674e57240139e4fdaede6d581ab443 100644
--- a/src/components/sound-editor/sound-editor.css
+++ b/src/components/sound-editor/sound-editor.css
@@ -36,17 +36,36 @@
 }
 
 .button {
-    width: 2rem;
     height: 2rem;
     padding: 0.25rem;
     outline: none;
     background: white;
     border-radius: 0.25rem;
     border: 1px solid #ddd;
+    cursor: pointer;
+    font-size: 0.85rem;
+    transition: 0.2s;
 }
 
 .button img {
     flex-grow: 1;
     max-width: 100%;
     max-height: 100%;
+    min-width: 1.5rem;
+}
+
+.trim-button {
+    display: flex;
+    align-items: center;
+    padding-right: 10px; /* To equalize with empty whitespace from image on left */
+}
+
+.trim-button-active {
+    filter: hue-rotate(155deg); /* @todo replace blue -> red with real submit icon */
+}
+
+.input-group-right {
+    flex-grow: 1;
+    display: flex;
+    flex-direction: row-reverse;
 }
diff --git a/src/components/sound-editor/sound-editor.jsx b/src/components/sound-editor/sound-editor.jsx
index 3e379b3f5372e57c977438246a8690768eac66d9..7ac6811abfb4c6219608f84ab541bbc533123278 100644
--- a/src/components/sound-editor/sound-editor.jsx
+++ b/src/components/sound-editor/sound-editor.jsx
@@ -1,26 +1,58 @@
 import PropTypes from 'prop-types';
 import React from 'react';
 import classNames from 'classnames';
-import Box from '../box/box.jsx';
+import {defineMessages, FormattedMessage, injectIntl, intlShape} from 'react-intl';
+
 import Waveform from '../waveform/waveform.jsx';
 import Label from '../forms/label.jsx';
 import Input from '../forms/input.jsx';
 import BufferedInputHOC from '../forms/buffered-input-hoc.jsx';
+import AudioTrimmer from '../../containers/audio-trimmer.jsx';
 
 import styles from './sound-editor.css';
 
 import playIcon from '../record-modal/icon--play.svg';
 import stopIcon from '../record-modal/icon--stop-playback.svg';
+import trimIcon from './icon--trim.svg';
 
 const BufferedInput = BufferedInputHOC(Input);
 
+const messages = defineMessages({
+    sound: {
+        id: 'soundEditor.sound',
+        description: 'Lable for the name of the sound',
+        defaultMessage: 'Sound'
+    },
+    play: {
+        id: 'soundEditor.play',
+        description: 'Title of the button to start playing the sound',
+        defaultMessage: 'Play'
+    },
+    stop: {
+        id: 'soundEditor.stop',
+        description: 'Title of the button to stop the sound',
+        defaultMessage: 'Stop'
+    },
+    trim: {
+        id: 'soundEditor.trim',
+        description: 'Title of the button to start trimminging the sound',
+        defaultMessage: 'Trim'
+    },
+    save: {
+        id: 'soundEditor.save',
+        description: 'Title of the button to save trimmed sound',
+        defaultMessage: 'Save'
+    }
+});
+
 const SoundEditor = props => (
-    <Box className={styles.editorContainer}>
-        <Box className={styles.row}>
-            <Box className={styles.inputGroup}>
+    <div className={styles.editorContainer}>
+        <div className={styles.row}>
+            <div className={styles.inputGroup}>
                 {props.playhead ? (
                     <button
                         className={classNames(styles.button, styles.stopButtonn)}
+                        title={props.intl.formatMessage(messages.stop)}
                         onClick={props.onStop}
                     >
                         <img src={stopIcon} />
@@ -28,14 +60,15 @@ const SoundEditor = props => (
                 ) : (
                     <button
                         className={classNames(styles.button, styles.playButton)}
+                        title={props.intl.formatMessage(messages.play)}
                         onClick={props.onPlay}
                     >
                         <img src={playIcon} />
                     </button>
                 )}
-            </Box>
-            <Box className={styles.inputGroup}>
-                <Label text="Sound">
+            </div>
+            <div className={styles.inputGroup}>
+                <Label text={props.intl.formatMessage(messages.sound)}>
                     <BufferedInput
                         tabIndex="1"
                         type="text"
@@ -43,27 +76,60 @@ const SoundEditor = props => (
                         onSubmit={props.onChangeName}
                     />
                 </Label>
-            </Box>
-        </Box>
-        <Box className={styles.row}>
-            <Box className={styles.waveformContainer}>
+            </div>
+            <div className={styles.inputGroupRight}>
+                <button
+                    className={classNames(styles.button, styles.trimButton, {
+                        [styles.trimButtonActive]: props.trimStart !== null
+                    })}
+                    title={props.trimStart === null ? (
+                        props.intl.formatMessage(messages.trim)
+                    ) : (
+                        props.intl.formatMessage(messages.save)
+                    )}
+                    onClick={props.onActivateTrim}
+                >
+                    <img src={trimIcon} />
+                    {props.trimStart === null ? (
+                        <FormattedMessage {...messages.trim} />
+                    ) : (
+                        <FormattedMessage {...messages.save} />
+                    )}
+                </button>
+            </div>
+        </div>
+        <div className={styles.row}>
+            <div className={styles.waveformContainer}>
                 <Waveform
                     data={props.chunkLevels}
                     height={180}
                     width={600}
                 />
-            </Box>
-        </Box>
-    </Box>
+                <AudioTrimmer
+                    playhead={props.playhead}
+                    trimEnd={props.trimEnd}
+                    trimStart={props.trimStart}
+                    onSetTrimEnd={props.onSetTrimEnd}
+                    onSetTrimStart={props.onSetTrimStart}
+                />
+            </div>
+        </div>
+    </div>
 );
 
 SoundEditor.propTypes = {
     chunkLevels: PropTypes.arrayOf(PropTypes.number).isRequired,
+    intl: intlShape,
     name: PropTypes.string.isRequired,
+    onActivateTrim: PropTypes.func,
     onChangeName: PropTypes.func.isRequired,
     onPlay: PropTypes.func.isRequired,
+    onSetTrimEnd: PropTypes.func,
+    onSetTrimStart: PropTypes.func,
     onStop: PropTypes.func.isRequired,
-    playhead: PropTypes.number
+    playhead: PropTypes.number,
+    trimEnd: PropTypes.number,
+    trimStart: PropTypes.number
 };
 
-export default SoundEditor;
+export default injectIntl(SoundEditor);
diff --git a/src/containers/audio-trimmer.jsx b/src/containers/audio-trimmer.jsx
index 07ba2154dbecd3dd8905198226c2f91f18cfb8ac..03e91443eb32f538640ee9a1fd17ac2a66aa2375 100644
--- a/src/containers/audio-trimmer.jsx
+++ b/src/containers/audio-trimmer.jsx
@@ -20,12 +20,14 @@ class AudioTrimmer extends React.Component {
         const dx = (e.clientX - this.initialX) / containerSize;
         const newTrim = Math.max(0, Math.min(this.props.trimEnd, this.initialTrim + dx));
         this.props.onSetTrimStart(newTrim);
+        e.preventDefault();
     }
     handleTrimEndMouseMove (e) {
         const containerSize = this.containerElement.getBoundingClientRect().width;
         const dx = (e.clientX - this.initialX) / containerSize;
         const newTrim = Math.min(1, Math.max(this.props.trimStart, this.initialTrim + dx));
         this.props.onSetTrimEnd(newTrim);
+        e.preventDefault();
     }
     handleTrimStartMouseUp () {
         window.removeEventListener('mousemove', this.handleTrimStartMouseMove);
diff --git a/src/containers/sound-editor.jsx b/src/containers/sound-editor.jsx
index 7706b1e98c984fcfebfe4c206acb313ec6b878d1..d18678273bf183ab1eea7298a3a82b9c41938e63 100644
--- a/src/containers/sound-editor.jsx
+++ b/src/containers/sound-editor.jsx
@@ -17,30 +17,50 @@ class SoundEditor extends React.Component {
             'handleChangeName',
             'handlePlay',
             'handleStopPlaying',
-            'handleUpdatePlayhead'
+            'handleUpdatePlayhead',
+            'handleActivateTrim',
+            'handleUpdateTrimEnd',
+            'handleUpdateTrimStart'
         ]);
         this.state = {
+            chunkLevels: computeChunkedRMS(this.props.samples),
             playhead: null, // null is not playing, [0 -> 1] is playing percent
-            chunkLevels: computeChunkedRMS(this.props.samples)
+            trimStart: null,
+            trimEnd: null
         };
     }
     componentDidMount () {
         this.audioBufferPlayer = new AudioBufferPlayer(this.props.samples, this.props.sampleRate);
     }
     componentWillReceiveProps (newProps) {
-        if (newProps.soundIndex !== this.props.soundIndex) {
-            this.audioBufferPlayer.stop();
-            this.audioBufferPlayer = new AudioBufferPlayer(newProps.samples, newProps.sampleRate);
-            this.setState({chunkLevels: computeChunkedRMS(newProps.samples)});
+        if (newProps.soundId !== this.props.soundId) { // A different sound has been selected
+            this.resetState(newProps.samples, newProps.sampleRate);
         }
     }
     componentWillUnmount () {
         this.audioBufferPlayer.stop();
     }
+    resetState (samples, sampleRate) {
+        this.audioBufferPlayer.stop();
+        this.audioBufferPlayer = new AudioBufferPlayer(samples, sampleRate);
+        this.setState({
+            chunkLevels: computeChunkedRMS(samples),
+            playhead: null,
+            trimStart: null,
+            trimEnd: null
+        });
+    }
+    submitNewSamples (samples, sampleRate) {
+        this.resetState(samples, sampleRate);
+        this.props.onUpdateSoundBuffer(
+            this.props.soundIndex,
+            this.audioBufferPlayer.buffer
+        );
+    }
     handlePlay () {
         this.audioBufferPlayer.play(
-            0,
-            1,
+            this.state.trimStart || 0,
+            this.state.trimEnd || 1,
             this.handleUpdatePlayhead,
             this.handleStoppedPlaying);
     }
@@ -57,6 +77,23 @@ class SoundEditor extends React.Component {
     handleChangeName (name) {
         this.props.onRenameSound(this.props.soundIndex, name);
     }
+    handleActivateTrim () {
+        if (this.state.trimStart === null && this.state.trimEnd === null) {
+            this.setState({trimEnd: 0.95, trimStart: 0.05});
+        } else {
+            const sampleCount = this.props.samples.length;
+            const startIndex = Math.floor(this.state.trimStart * sampleCount);
+            const endIndex = Math.floor(this.state.trimEnd * sampleCount);
+            const clippedSamples = this.props.samples.slice(startIndex, endIndex);
+            this.submitNewSamples(clippedSamples, this.props.sampleRate);
+        }
+    }
+    handleUpdateTrimEnd (trimEnd) {
+        this.setState({trimEnd});
+    }
+    handleUpdateTrimStart (trimStart) {
+        this.setState({trimStart});
+    }
     render () {
         return (
             <SoundEditorComponent
@@ -65,12 +102,12 @@ class SoundEditor extends React.Component {
                 playhead={this.state.playhead}
                 trimEnd={this.state.trimEnd}
                 trimStart={this.state.trimStart}
+                onActivateTrim={this.handleActivateTrim}
                 onChangeName={this.handleChangeName}
                 onPlay={this.handlePlay}
                 onSetTrimEnd={this.handleUpdateTrimEnd}
                 onSetTrimStart={this.handleUpdateTrimStart}
                 onStop={this.handleStopPlaying}
-                onTrim={this.handleActivateTrim}
             />
         );
     }
@@ -79,8 +116,10 @@ class SoundEditor extends React.Component {
 SoundEditor.propTypes = {
     name: PropTypes.string.isRequired,
     onRenameSound: PropTypes.func.isRequired,
+    onUpdateSoundBuffer: PropTypes.func.isRequired,
     sampleRate: PropTypes.number,
     samples: PropTypes.instanceOf(Float32Array),
+    soundId: PropTypes.string,
     soundIndex: PropTypes.number
 };
 
@@ -88,10 +127,12 @@ const mapStateToProps = (state, {soundIndex}) => {
     const sound = state.vm.editingTarget.sprite.sounds[soundIndex];
     const audioBuffer = state.vm.getSoundBuffer(soundIndex);
     return {
+        soundId: sound.soundId,
         sampleRate: audioBuffer.sampleRate,
         samples: audioBuffer.getChannelData(0),
         name: sound.name,
-        onRenameSound: state.vm.renameSound.bind(state.vm)
+        onRenameSound: state.vm.renameSound.bind(state.vm),
+        onUpdateSoundBuffer: state.vm.updateSoundBuffer.bind(state.vm)
     };
 };
 
diff --git a/test/__mocks__/audio-buffer-player.js b/test/__mocks__/audio-buffer-player.js
new file mode 100644
index 0000000000000000000000000000000000000000..c36092be339f166b2376a5340c1e82abed6c9ca9
--- /dev/null
+++ b/test/__mocks__/audio-buffer-player.js
@@ -0,0 +1,12 @@
+/* eslint-env jest */
+export default class MockAudioBufferPlayer {
+    constructor (samples, sampleRate) {
+        this.samples = samples;
+        this.sampleRate = sampleRate;
+        this.play = jest.fn((trimStart, trimEnd, onUpdate) => {
+            this.onUpdate = onUpdate;
+        });
+        this.stop = jest.fn();
+        MockAudioBufferPlayer.instance = this;
+    }
+}
diff --git a/test/helpers/intl-helpers.js b/test/helpers/intl-helpers.js
new file mode 100644
index 0000000000000000000000000000000000000000..d658aeae0981adc70d8de4f57d3ec196a06f2df1
--- /dev/null
+++ b/test/helpers/intl-helpers.js
@@ -0,0 +1,45 @@
+/*
+ * Helpers for using enzyme and react-test-renderer with react-intl
+ * Directly from https://github.com/yahoo/react-intl/wiki/Testing-with-React-Intl
+ */
+import React from 'react';
+import renderer from 'react-test-renderer';
+import {IntlProvider, intlShape} from 'react-intl';
+import {mount, shallow} from 'enzyme';
+
+const intlProvider = new IntlProvider({locale: 'en'}, {});
+const {intl} = intlProvider.getChildContext();
+
+const nodeWithIntlProp = node => {
+    return React.cloneElement(node, {intl});
+};
+
+const shallowWithIntl = (node, {context} = {}) => {
+    return shallow(
+        nodeWithIntlProp(node),
+        {
+            context: Object.assign({}, context, {intl})
+        }
+    );
+};
+
+const mountWithIntl = (node, {context, childContextTypes} = {}) => {
+    return mount(
+        nodeWithIntlProp(node),
+        {
+            context: Object.assign({}, context, {intl}),
+            childContextTypes: Object.assign({}, {intl: intlShape}, childContextTypes)
+        }
+    );
+};
+
+// react-test-renderer component for use with snapshot testing
+const componentWithIntl = (children, props = {locale: 'en'}) => {
+    return renderer.create(<IntlProvider {...props}>{children}</IntlProvider>);
+};
+
+export {
+    componentWithIntl,
+    shallowWithIntl,
+    mountWithIntl
+};
diff --git a/test/unit/components/__snapshots__/sound-editor.test.jsx.snap b/test/unit/components/__snapshots__/sound-editor.test.jsx.snap
new file mode 100644
index 0000000000000000000000000000000000000000..c757bc5d5679537c6125e4e7ed3b5a97be4d88ae
--- /dev/null
+++ b/test/unit/components/__snapshots__/sound-editor.test.jsx.snap
@@ -0,0 +1,313 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Sound Editor Component matches snapshot 1`] = `
+<div
+  className={undefined}
+>
+  <div
+    className={undefined}
+  >
+    <div
+      className={undefined}
+    >
+      <button
+        className=""
+        onClick={[Function]}
+        title="Stop"
+      >
+        <img
+          src="test-file-stub"
+        />
+      </button>
+    </div>
+    <div
+      className={undefined}
+    >
+      <label
+        className={undefined}
+      >
+        <span
+          className={undefined}
+        >
+          Sound
+        </span>
+        <input
+          className=""
+          onBlur={[Function]}
+          onChange={[Function]}
+          onKeyPress={[Function]}
+          onSubmit={[Function]}
+          tabIndex="1"
+          type="text"
+          value="sound name"
+        />
+      </label>
+    </div>
+    <div
+      className={undefined}
+    >
+      <button
+        className="undefined"
+        onClick={[Function]}
+        title="Save"
+      >
+        <img
+          src="test-file-stub"
+        />
+        <span>
+          Save
+        </span>
+      </button>
+    </div>
+  </div>
+  <div
+    className={undefined}
+  >
+    <div
+      className={undefined}
+    >
+      <svg
+        className={undefined}
+        viewBox="0 0 600 180"
+      >
+        <g
+          transform="scale(1, -1) translate(0, -90)"
+        >
+          <path
+            className={undefined}
+            d="M0 0Q0 0 60 45,Q120 90 180 135,Q240 180 300 225,Q360 270 420 135,Q480 0 480 0,Q480 0 420 -135,Q360 -270 300 -225,Q240 -180 180 -135,Q120 -90 60 -45,Q0 0 0 0Z"
+            strokeLinejoin="round"
+            strokeWidth={2}
+          />
+        </g>
+      </svg>
+      <div
+        className={undefined}
+      >
+        <div
+          className=""
+          onMouseDown={[Function]}
+          style={
+            Object {
+              "alignContent": undefined,
+              "alignItems": undefined,
+              "alignSelf": undefined,
+              "flexBasis": undefined,
+              "flexDirection": undefined,
+              "flexGrow": undefined,
+              "flexShrink": undefined,
+              "flexWrap": undefined,
+              "height": undefined,
+              "justifyContent": undefined,
+              "width": "20%",
+            }
+          }
+        >
+          <div
+            className=""
+            style={
+              Object {
+                "alignContent": undefined,
+                "alignItems": undefined,
+                "alignSelf": undefined,
+                "flexBasis": undefined,
+                "flexDirection": undefined,
+                "flexGrow": undefined,
+                "flexShrink": undefined,
+                "flexWrap": undefined,
+                "height": undefined,
+                "justifyContent": undefined,
+                "width": undefined,
+              }
+            }
+          />
+          <div
+            className=""
+            style={
+              Object {
+                "alignContent": undefined,
+                "alignItems": undefined,
+                "alignSelf": undefined,
+                "flexBasis": undefined,
+                "flexDirection": undefined,
+                "flexGrow": undefined,
+                "flexShrink": undefined,
+                "flexWrap": undefined,
+                "height": undefined,
+                "justifyContent": undefined,
+                "width": undefined,
+              }
+            }
+          >
+            <div
+              className=""
+              style={
+                Object {
+                  "alignContent": undefined,
+                  "alignItems": undefined,
+                  "alignSelf": undefined,
+                  "flexBasis": undefined,
+                  "flexDirection": undefined,
+                  "flexGrow": undefined,
+                  "flexShrink": undefined,
+                  "flexWrap": undefined,
+                  "height": undefined,
+                  "justifyContent": undefined,
+                  "width": undefined,
+                }
+              }
+            >
+              <img
+                src="test-file-stub"
+              />
+            </div>
+            <div
+              className=""
+              style={
+                Object {
+                  "alignContent": undefined,
+                  "alignItems": undefined,
+                  "alignSelf": undefined,
+                  "flexBasis": undefined,
+                  "flexDirection": undefined,
+                  "flexGrow": undefined,
+                  "flexShrink": undefined,
+                  "flexWrap": undefined,
+                  "height": undefined,
+                  "justifyContent": undefined,
+                  "width": undefined,
+                }
+              }
+            >
+              <img
+                src="test-file-stub"
+              />
+            </div>
+          </div>
+        </div>
+        <div
+          className=""
+          style={
+            Object {
+              "alignContent": undefined,
+              "alignItems": undefined,
+              "alignSelf": undefined,
+              "flexBasis": undefined,
+              "flexDirection": undefined,
+              "flexGrow": undefined,
+              "flexShrink": undefined,
+              "flexWrap": undefined,
+              "height": undefined,
+              "justifyContent": undefined,
+              "left": "50%",
+              "width": undefined,
+            }
+          }
+        />
+        <div
+          className=""
+          onMouseDown={[Function]}
+          style={
+            Object {
+              "alignContent": undefined,
+              "alignItems": undefined,
+              "alignSelf": undefined,
+              "flexBasis": undefined,
+              "flexDirection": undefined,
+              "flexGrow": undefined,
+              "flexShrink": undefined,
+              "flexWrap": undefined,
+              "height": undefined,
+              "justifyContent": undefined,
+              "left": "80%",
+              "width": "20%",
+            }
+          }
+        >
+          <div
+            className=""
+            style={
+              Object {
+                "alignContent": undefined,
+                "alignItems": undefined,
+                "alignSelf": undefined,
+                "flexBasis": undefined,
+                "flexDirection": undefined,
+                "flexGrow": undefined,
+                "flexShrink": undefined,
+                "flexWrap": undefined,
+                "height": undefined,
+                "justifyContent": undefined,
+                "width": undefined,
+              }
+            }
+          />
+          <div
+            className=""
+            style={
+              Object {
+                "alignContent": undefined,
+                "alignItems": undefined,
+                "alignSelf": undefined,
+                "flexBasis": undefined,
+                "flexDirection": undefined,
+                "flexGrow": undefined,
+                "flexShrink": undefined,
+                "flexWrap": undefined,
+                "height": undefined,
+                "justifyContent": undefined,
+                "width": undefined,
+              }
+            }
+          >
+            <div
+              className=""
+              style={
+                Object {
+                  "alignContent": undefined,
+                  "alignItems": undefined,
+                  "alignSelf": undefined,
+                  "flexBasis": undefined,
+                  "flexDirection": undefined,
+                  "flexGrow": undefined,
+                  "flexShrink": undefined,
+                  "flexWrap": undefined,
+                  "height": undefined,
+                  "justifyContent": undefined,
+                  "width": undefined,
+                }
+              }
+            >
+              <img
+                src="test-file-stub"
+              />
+            </div>
+            <div
+              className=""
+              style={
+                Object {
+                  "alignContent": undefined,
+                  "alignItems": undefined,
+                  "alignSelf": undefined,
+                  "flexBasis": undefined,
+                  "flexDirection": undefined,
+                  "flexGrow": undefined,
+                  "flexShrink": undefined,
+                  "flexWrap": undefined,
+                  "height": undefined,
+                  "justifyContent": undefined,
+                  "width": undefined,
+                }
+              }
+            >
+              <img
+                src="test-file-stub"
+              />
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+</div>
+`;
diff --git a/test/unit/components/sound-editor.test.jsx b/test/unit/components/sound-editor.test.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..9c98edda9ba2632ad95e1760bc4fdafd12ed1289
--- /dev/null
+++ b/test/unit/components/sound-editor.test.jsx
@@ -0,0 +1,60 @@
+/* eslint-env jest */
+import React from 'react'; // eslint-disable-line no-unused-vars
+import {mountWithIntl, componentWithIntl} from '../../helpers/intl-helpers';
+import SoundEditor from '../../../src/components/sound-editor/sound-editor'; // eslint-disable-line no-unused-vars
+
+describe('Sound Editor Component', () => {
+    let props;
+    beforeEach(() => {
+        props = {
+            chunkLevels: [1, 2, 3],
+            name: 'sound name',
+            playhead: 0.5,
+            trimStart: 0.2,
+            trimEnd: 0.8,
+            onActivateTrim: jest.fn(),
+            onChangeName: jest.fn(),
+            onPlay: jest.fn(),
+            onSetTrimEnd: jest.fn(),
+            onSetTrimStart: jest.fn(),
+            onStop: jest.fn()
+        };
+    });
+
+    test('matches snapshot', () => {
+        const component = componentWithIntl(<SoundEditor {...props} />);
+        expect(component.toJSON()).toMatchSnapshot();
+    });
+
+    test('trim button appears when trims are null', () => {
+        const wrapper = mountWithIntl(<SoundEditor {...props} trimStart={null} trimEnd={null} />);
+        wrapper.find('button[title="Trim"]').simulate('click');
+        expect(props.onActivateTrim).toHaveBeenCalled();
+    });
+
+    test('save button appears when trims are not null', () => {
+        const wrapper = mountWithIntl(<SoundEditor {...props} trimStart={0.25} trimEnd={0.75} />);
+        wrapper.find('button[title="Save"]').simulate('click');
+        expect(props.onActivateTrim).toHaveBeenCalled();
+    });
+
+    test('play button appears when playhead is null', () => {
+        const wrapper = mountWithIntl(<SoundEditor {...props} playhead={null} />);
+        wrapper.find('button[title="Play"]').simulate('click');
+        expect(props.onPlay).toHaveBeenCalled();
+    });
+
+    test('stop button appears when playhead is not null', () => {
+        const wrapper = mountWithIntl(<SoundEditor {...props} playhead={0.5} />);
+        wrapper.find('button[title="Stop"]').simulate('click');
+        expect(props.onStop).toHaveBeenCalled();
+    });
+
+    test('submitting name calls the callback', () => {
+        const wrapper = mountWithIntl(<SoundEditor {...props} />);
+        wrapper.find('input')
+            .simulate('change', {target: {value: 'hello'}})
+            .simulate('blur');
+        expect(props.onChangeName).toHaveBeenCalled();
+    });
+});
diff --git a/test/unit/containers/sound-editor.test.jsx b/test/unit/containers/sound-editor.test.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..39f1e19b07b2f3a9a24aee00136fdb7a2eefa0c3
--- /dev/null
+++ b/test/unit/containers/sound-editor.test.jsx
@@ -0,0 +1,93 @@
+/* eslint-env jest */
+import React from 'react'; // eslint-disable-line no-unused-vars
+import {mountWithIntl} from '../../helpers/intl-helpers';
+import configureStore from 'redux-mock-store';
+import mockAudioBufferPlayer from '../../__mocks__/audio-buffer-player.js';
+
+import SoundEditor from '../../../src/containers/sound-editor'; // eslint-disable-line no-unused-vars
+// eslint-disable-next-line no-unused-vars
+import SoundEditorComponent from '../../../src/components/sound-editor/sound-editor';
+
+jest.mock('../../../src/lib/audio/audio-buffer-player', () => mockAudioBufferPlayer);
+
+describe('Sound Editor Container', () => {
+    const mockStore = configureStore();
+    let store;
+    let soundIndex;
+    let soundBuffer;
+    let samples = new Float32Array([0, 0, 0]); // eslint-disable-line no-undef
+    let vm;
+
+    beforeEach(() => {
+        soundIndex = 0;
+        soundBuffer = {
+            sampleRate: 0,
+            getChannelData: jest.fn(() => samples)
+        };
+        vm = {
+            getSoundBuffer: jest.fn(() => soundBuffer),
+            renameSound: jest.fn(),
+            updateSoundBuffer: jest.fn(),
+            editingTarget: {
+                sprite: {
+                    sounds: [{name: 'first name', id: 'first id'}]
+                }
+            }
+        };
+        store = mockStore({vm});
+    });
+
+    test('should pass the correct data to the component from the store', () => {
+        const wrapper = mountWithIntl(<SoundEditor store={store} soundIndex={soundIndex} />);
+        const componentProps = wrapper.find(SoundEditorComponent).props();
+        // Data retreived and processed by the `connect` with the store
+        expect(componentProps.name).toEqual('first name');
+        expect(componentProps.chunkLevels).toEqual([0]);
+        expect(mockAudioBufferPlayer.instance.samples).toEqual(samples);
+        // Initial data
+        expect(componentProps.playhead).toEqual(null);
+        expect(componentProps.trimStart).toEqual(null);
+        expect(componentProps.trimEnd).toEqual(null);
+
+    });
+
+    test('it plays when clicked and stops when clicked again', () => {
+        const wrapper = mountWithIntl(<SoundEditor store={store} soundIndex={soundIndex} />);
+        const component = wrapper.find(SoundEditorComponent);
+        // Ensure rendering doesn't start playing any sounds
+        expect(mockAudioBufferPlayer.instance.play.mock.calls).toEqual([]);
+        expect(mockAudioBufferPlayer.instance.stop.mock.calls).toEqual([]);
+
+        component.props().onPlay();
+        expect(mockAudioBufferPlayer.instance.play).toHaveBeenCalled();
+
+        // Mock the audio buffer player calling onUpdate
+        mockAudioBufferPlayer.instance.onUpdate(0.5);
+        expect(component.props().playhead).toEqual(0.5);
+
+        component.props().onStop();
+        expect(mockAudioBufferPlayer.instance.stop).toHaveBeenCalled();
+        expect(component.props().playhead).toEqual(null);
+    });
+
+    test('it sets the component props for trimming and submits to the vm', () => {
+        const wrapper = mountWithIntl(<SoundEditor store={store} soundIndex={soundIndex} />);
+        const component = wrapper.find(SoundEditorComponent);
+
+        component.props().onActivateTrim();
+        expect(component.props().trimStart).not.toEqual(null);
+        expect(component.props().trimEnd).not.toEqual(null);
+
+        component.props().onActivateTrim();
+        expect(vm.updateSoundBuffer).toHaveBeenCalled();
+        expect(component.props().trimStart).toEqual(null);
+        expect(component.props().trimEnd).toEqual(null);
+    });
+
+    test('it submits name changes to the vm', () => {
+        const wrapper = mountWithIntl(<SoundEditor store={store} soundIndex={soundIndex} />);
+        const component = wrapper.find(SoundEditorComponent);
+        component.props().onChangeName('hello');
+        expect(vm.renameSound).toHaveBeenCalledWith(soundIndex, 'hello');
+    });
+});