diff --git a/src/components/audio-trimmer/audio-selector.css b/src/components/audio-trimmer/audio-selector.css
new file mode 100644
index 0000000000000000000000000000000000000000..f17f50e5109784f488cb04530ae82e0f3bc455b1
--- /dev/null
+++ b/src/components/audio-trimmer/audio-selector.css
@@ -0,0 +1,119 @@
+@import "../../css/colors.css";
+
+$border-radius: 4px;
+$trim-handle-width: 12px;
+$trim-handle-height: 14px;
+$stripe-size: 10px;
+$hover-scale: 2;
+
+.absolute {
+    position: absolute;
+    top: 0;
+    left: 0;
+    width: 100%;
+    height: 100%;
+    cursor: pointer;
+
+    /* Force the browser to paint separately to avoid composite cost with waveform */
+    transform: translateZ(0);
+}
+
+.trim-background {
+    cursor: pointer;
+    touch-action: none;
+}
+
+.trim-background-mask {
+    border: 1px solid $motion-tertiary;
+    opacity: 0.5;
+    background: $motion-primary;
+}
+
+.start-trim-background .trim-background-mask {
+    border-top-left-radius: $border-radius;
+    border-bottom-left-radius: $border-radius;
+}
+
+.end-trim-background .trim-background-mask {
+    border-top-right-radius: $border-radius;
+    border-bottom-right-radius: $border-radius;
+}
+
+.trim-line {
+    position: absolute;
+    top: 0;
+    width: 0px;
+    height: 100%;
+    border: 1px solid $motion-tertiary;
+}
+
+.playhead-container {
+    position: absolute;
+    top: 0;
+    left: 0;
+    height: 100%;
+    width: 100%;
+    overflow: hidden;
+}
+
+.playhead {
+    /*
+        Even though playhead is just a line, it is 100% width (the width of the waveform)
+        so that we can use transform: translateX() using percentages.
+    */
+    width: 100%;
+    border-left: 1px solid $motion-primary;
+    border-top: none;
+    border-bottom: none;
+    border-right: none;
+}
+
+.start-trim-line {
+    left: 0;
+}
+
+.end-trim-line {
+    right: 0;
+}
+
+.trim-handle {
+    position: absolute;
+    left: calc(-$trim-handle-width / 2);
+    width: $trim-handle-width;
+    height: $trim-handle-height;
+    filter: hue-rotate(180deg);
+}
+
+.trim-handle img {
+    position: absolute;
+    width: $trim-handle-width;
+    height: $trim-handle-height;
+
+    /* Make sure image dragging isn't triggered */
+    user-select: none;
+    user-drag: none;
+    -webkit-user-drag: none; /* Autoprefixer doesn't seem to work for this */
+
+    transition: 0.2s;
+}
+
+.top-trim-handle {
+    top: -$trim-handle-height;
+}
+
+.bottom-trim-handle {
+    bottom: -$trim-handle-height;
+}
+
+.top-trim-handle img {
+    transform: rotate(180deg);
+}
+
+/* Increase handle size when anywhere on draggable area is hovered */
+.trim-background:hover img {
+    transform: scale($hover-scale);
+}
+
+.trim-background:hover .top-trim-handle img {
+    transform: rotate(180deg) scale($hover-scale);
+}
diff --git a/src/components/audio-trimmer/audio-selector.jsx b/src/components/audio-trimmer/audio-selector.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..1243edf113f2026924482f63d06145c93bb28e8a
--- /dev/null
+++ b/src/components/audio-trimmer/audio-selector.jsx
@@ -0,0 +1,73 @@
+import PropTypes from 'prop-types';
+import React from 'react';
+import classNames from 'classnames';
+import Box from '../box/box.jsx';
+import styles from './audio-selector.css';
+import handleIcon from './icon--handle.svg';
+
+const AudioSelector = props => (
+    <div
+        className={styles.absolute}
+        ref={props.containerRef}
+        onMouseDown={props.onNewSelectionMouseDown}
+        onTouchStart={props.onNewSelectionMouseDown}
+    >
+        {props.trimStart === null ? null : (
+            <Box
+                className={classNames(styles.absolute, styles.trimBackground, styles.startTrimBackground)}
+                style={{
+                    left: `${props.trimStart * 100}%`,
+                    width: `${100 * (props.trimEnd - props.trimStart)}%`
+                }}
+            >
+                <Box className={classNames(styles.absolute, styles.trimBackgroundMask)} />
+                <Box
+                    className={classNames(styles.trimLine, styles.startTrimLine)}
+                    onMouseDown={props.onTrimStartMouseDown}
+                    onTouchStart={props.onTrimStartMouseDown}
+                >
+                    <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
+                    className={classNames(styles.trimLine, styles.endTrimLine)}
+                    onMouseDown={props.onTrimEndMouseDown}
+                    onTouchStart={props.onTrimEndMouseDown}
+                >
+                    <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>
+        )}
+        {props.playhead ? (
+            <div className={styles.playheadContainer}>
+                <div
+                    className={classNames(styles.trimLine, styles.playhead)}
+                    style={{
+                        transform: `translateX(${100 * props.playhead}%)`
+                    }}
+                />
+            </div>
+        ) : null}
+    </div>
+);
+
+AudioSelector.propTypes = {
+    containerRef: PropTypes.func,
+    onNewSelectionMouseDown: PropTypes.func.isRequired,
+    onTrimEndMouseDown: PropTypes.func.isRequired,
+    onTrimStartMouseDown: PropTypes.func.isRequired,
+    playhead: PropTypes.number,
+    trimEnd: PropTypes.number,
+    trimStart: PropTypes.number
+};
+
+export default AudioSelector;
diff --git a/src/components/sound-editor/sound-editor.jsx b/src/components/sound-editor/sound-editor.jsx
index f97338e06a55bf75e0bceda2ddc33d8a9be1070b..ce25dc50b2ed84485e423a22f724a28e3c53bcc7 100644
--- a/src/components/sound-editor/sound-editor.jsx
+++ b/src/components/sound-editor/sound-editor.jsx
@@ -8,15 +8,13 @@ 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 AudioSelector from '../../containers/audio-selector.jsx';
 import IconButton from '../icon-button/icon-button.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';
-import trimConfirmIcon from './icon--trim-confirm.svg';
 import redoIcon from './icon--redo.svg';
 import undoIcon from './icon--undo.svg';
 import echoIcon from './icon--echo.svg';
@@ -103,7 +101,11 @@ const messages = defineMessages({
 });
 
 const SoundEditor = props => (
-    <div className={styles.editorContainer}>
+    <div
+        className={styles.editorContainer}
+        ref={props.setRef}
+        onMouseDown={props.onContainerClick}
+    >
         <div className={styles.row}>
             <div className={styles.inputGroup}>
                 <Label text={props.intl.formatMessage(messages.sound)}>
@@ -161,12 +163,13 @@ const SoundEditor = props => (
                     height={160}
                     width={600}
                 />
-                <AudioTrimmer
+                <AudioSelector
                     playhead={props.playhead}
                     trimEnd={props.trimEnd}
                     trimStart={props.trimStart}
-                    onSetTrimEnd={props.onSetTrimEnd}
-                    onSetTrimStart={props.onSetTrimStart}
+                    onPlay={props.onPlay}
+                    onSetTrim={props.onSetTrim}
+                    onStop={props.onStop}
                 />
             </div>
         </div>
@@ -248,8 +251,8 @@ SoundEditor.propTypes = {
     chunkLevels: PropTypes.arrayOf(PropTypes.number).isRequired,
     intl: intlShape,
     name: PropTypes.string.isRequired,
-    onActivateTrim: PropTypes.func,
     onChangeName: PropTypes.func.isRequired,
+    onContainerClick: PropTypes.func.isRequired,
     onEcho: PropTypes.func.isRequired,
     onFaster: PropTypes.func.isRequired,
     onLouder: PropTypes.func.isRequired,
@@ -257,13 +260,13 @@ SoundEditor.propTypes = {
     onRedo: PropTypes.func.isRequired,
     onReverse: PropTypes.func.isRequired,
     onRobot: PropTypes.func.isRequired,
-    onSetTrimEnd: PropTypes.func,
-    onSetTrimStart: PropTypes.func,
+    onSetTrim: PropTypes.func,
     onSlower: PropTypes.func.isRequired,
     onSofter: PropTypes.func.isRequired,
     onStop: PropTypes.func.isRequired,
     onUndo: PropTypes.func.isRequired,
     playhead: PropTypes.number,
+    setRef: PropTypes.func,
     trimEnd: PropTypes.number,
     trimStart: PropTypes.number
 };
diff --git a/src/containers/audio-selector.jsx b/src/containers/audio-selector.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..30ef003a96b1edbd9e25cca848e8ad2b5985b25c
--- /dev/null
+++ b/src/containers/audio-selector.jsx
@@ -0,0 +1,156 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import bindAll from 'lodash.bindall';
+import AudioSelectorComponent from '../components/audio-trimmer/audio-selector.jsx';
+import {getEventXY} from '../lib/touch-utils';
+
+const MIN_LENGTH = 0.01;
+const MIN_DURATION = 500;
+
+class AudioSelector extends React.Component {
+    constructor (props) {
+        super(props);
+        bindAll(this, [
+            'handleNewSelectionMouseDown',
+            'handleTrimStartMouseDown',
+            'handleTrimEndMouseDown',
+            'handleTrimStartMouseMove',
+            'handleTrimEndMouseMove',
+            'handleTrimStartMouseUp',
+            'handleTrimEndMouseUp',
+            'storeRef'
+        ]);
+
+        this.state = {
+            trimStart: props.trimStart,
+            trimEnd: props.trimEnd
+        };
+
+        this.clickStartTime = 0;
+    }
+    componentWillReceiveProps (newProps) {
+        if (newProps.trimStart === this.props.trimStart) return;
+        this.setState({
+            trimStart: newProps.trimStart,
+            trimEnd: newProps.trimEnd
+        });
+    }
+    clearSelection () {
+        this.props.onSetTrim(null, null);
+    }
+    handleNewSelectionMouseDown (e) {
+        this.initialX = getEventXY(e).x;
+        const {width, left} = this.containerElement.getBoundingClientRect();
+        this.initialTrimEnd = (this.initialX - left) / width;
+        this.initialTrimStart = this.initialTrimEnd;
+        this.props.onSetTrim(this.initialTrimStart, this.initialTrimEnd);
+
+        this.clickStartTime = Date.now();
+
+        window.addEventListener('mousemove', this.handleTrimEndMouseMove);
+        window.addEventListener('mouseup', this.handleTrimEndMouseUp);
+        window.addEventListener('touchmove', this.handleTrimEndMouseMove);
+        window.addEventListener('touchend', this.handleTrimEndMouseUp);
+    }
+    handleTrimStartMouseMove (e) {
+        const containerSize = this.containerElement.getBoundingClientRect().width;
+        const dx = (getEventXY(e).x - this.initialX) / containerSize;
+        const newTrim = Math.max(0, Math.min(1, this.initialTrimStart + dx));
+        if (newTrim > this.initialTrimEnd) {
+            this.setState({
+                trimStart: this.initialTrimEnd,
+                trimEnd: newTrim
+            });
+        } else {
+            this.setState({
+                trimStart: newTrim
+            });
+        }
+        e.preventDefault();
+    }
+    handleTrimEndMouseMove (e) {
+        const containerSize = this.containerElement.getBoundingClientRect().width;
+        const dx = (getEventXY(e).x - this.initialX) / containerSize;
+        const newTrim = Math.min(1, Math.max(0, this.initialTrimEnd + dx));
+        if (newTrim < this.initialTrimStart) {
+            this.setState({
+                trimStart: newTrim,
+                trimEnd: this.initialTrimStart
+            });
+        } else {
+            this.setState({
+                trimEnd: newTrim
+            });
+        }
+        e.preventDefault();
+    }
+    handleTrimStartMouseUp () {
+        window.removeEventListener('mousemove', this.handleTrimStartMouseMove);
+        window.removeEventListener('mouseup', this.handleTrimStartMouseUp);
+        window.removeEventListener('touchmove', this.handleTrimStartMouseMove);
+        window.removeEventListener('touchend', this.handleTrimStartMouseUp);
+        this.props.onSetTrim(this.state.trimStart, this.state.trimEnd);
+    }
+    handleTrimEndMouseUp () {
+        window.removeEventListener('mousemove', this.handleTrimEndMouseMove);
+        window.removeEventListener('mouseup', this.handleTrimEndMouseUp);
+        window.removeEventListener('touchmove', this.handleTrimEndMouseMove);
+        window.removeEventListener('touchend', this.handleTrimEndMouseUp);
+        // If the selection was made quickly (tooFast) and is small (tooShort),
+        // deselect instead. This allows click-to-deselect even if you drag
+        // a little bit by accident. It also allows very quickly making a
+        // selection, as long as it is above a minimum length.
+        const tooFast = (Date.now() - this.clickStartTime) < MIN_DURATION;
+        const tooShort = (this.state.trimEnd - this.state.trimStart) < MIN_LENGTH;
+        if (tooFast && tooShort) {
+            this.clearSelection();
+        } else {
+            this.props.onSetTrim(this.state.trimStart, this.state.trimEnd);
+        }
+    }
+    handleTrimStartMouseDown (e) {
+        this.initialX = getEventXY(e).x;
+        this.initialTrimStart = this.props.trimStart;
+        this.initialTrimEnd = this.props.trimEnd;
+        window.addEventListener('mousemove', this.handleTrimStartMouseMove);
+        window.addEventListener('mouseup', this.handleTrimStartMouseUp);
+        window.addEventListener('touchmove', this.handleTrimStartMouseMove);
+        window.addEventListener('touchend', this.handleTrimStartMouseUp);
+        e.stopPropagation();
+    }
+    handleTrimEndMouseDown (e) {
+        this.initialX = getEventXY(e).x;
+        this.initialTrimEnd = this.props.trimEnd;
+        this.initialTrimStart = this.props.trimStart;
+        window.addEventListener('mousemove', this.handleTrimEndMouseMove);
+        window.addEventListener('mouseup', this.handleTrimEndMouseUp);
+        window.addEventListener('touchmove', this.handleTrimEndMouseMove);
+        window.addEventListener('touchend', this.handleTrimEndMouseUp);
+        e.stopPropagation();
+    }
+    storeRef (el) {
+        this.containerElement = el;
+    }
+    render () {
+        return (
+            <AudioSelectorComponent
+                containerRef={this.storeRef}
+                playhead={this.props.playhead}
+                trimEnd={this.state.trimEnd}
+                trimStart={this.state.trimStart}
+                onNewSelectionMouseDown={this.handleNewSelectionMouseDown}
+                onTrimEndMouseDown={this.handleTrimEndMouseDown}
+                onTrimStartMouseDown={this.handleTrimStartMouseDown}
+            />
+        );
+    }
+}
+
+AudioSelector.propTypes = {
+    onSetTrim: PropTypes.func,
+    playhead: PropTypes.number,
+    trimEnd: PropTypes.number,
+    trimStart: PropTypes.number
+};
+
+export default AudioSelector;
diff --git a/src/containers/sound-editor.jsx b/src/containers/sound-editor.jsx
index a3cdaaf674fee46aeddcaaac66fcfbf168deb58a..d3c58f811f4ac816f645f30948e49969265c0786 100644
--- a/src/containers/sound-editor.jsx
+++ b/src/containers/sound-editor.jsx
@@ -23,13 +23,14 @@ class SoundEditor extends React.Component {
             'handlePlay',
             'handleStopPlaying',
             'handleUpdatePlayhead',
-            'handleActivateTrim',
-            'handleUpdateTrimEnd',
-            'handleUpdateTrimStart',
+            'handleDelete',
+            'handleUpdateTrim',
             'handleEffect',
             'handleUndo',
             'handleRedo',
             'submitNewSamples'
+            'handleContainerClick',
+            'setRef'
         ]);
         this.state = {
             chunkLevels: computeChunkedRMS(this.props.samples),
@@ -40,6 +41,8 @@ class SoundEditor extends React.Component {
 
         this.redoStack = [];
         this.undoStack = [];
+
+        this.ref = null;
     }
     componentDidMount () {
         this.audioBufferPlayer = new AudioBufferPlayer(this.props.samples, this.props.sampleRate);
@@ -49,6 +52,10 @@ class SoundEditor extends React.Component {
             this.redoStack = [];
             this.undoStack = [];
             this.resetState(newProps.samples, newProps.sampleRate);
+            this.setState({
+                trimStart: null,
+                trimEnd: null
+            });
         }
     }
     componentWillUnmount () {
@@ -59,9 +66,7 @@ class SoundEditor extends React.Component {
         this.audioBufferPlayer = new AudioBufferPlayer(samples, sampleRate);
         this.setState({
             chunkLevels: computeChunkedRMS(samples),
-            playhead: null,
-            trimStart: null,
-            trimEnd: null
+            playhead: null
         });
     }
     submitNewSamples (samples, sampleRate, skipUndo) {
@@ -106,6 +111,7 @@ class SoundEditor extends React.Component {
         return false; // Update failed
     }
     handlePlay () {
+        this.audioBufferPlayer.stop();
         this.audioBufferPlayer.play(
             this.state.trimStart || 0,
             this.state.trimEnd || 1,
@@ -125,31 +131,36 @@ class SoundEditor extends React.Component {
     handleChangeName (name) {
         this.props.vm.renameSound(this.props.soundIndex, name);
     }
-    handleActivateTrim () {
-        if (this.state.trimStart === null && this.state.trimEnd === null) {
-            this.setState({trimEnd: 0.95, trimStart: 0.05});
+    handleDelete () {
+        const {samples, sampleRate} = this.copyCurrentBuffer();
+        const sampleCount = samples.length;
+        const startIndex = Math.floor(this.state.trimStart * sampleCount);
+        const endIndex = Math.floor(this.state.trimEnd * sampleCount);
+        const firstPart = samples.slice(0, startIndex);
+        const secondPart = samples.slice(endIndex, sampleCount);
+        const newLength = firstPart.length + secondPart.length;
+        let newSamples;
+        if (newLength === 0) {
+            newSamples = new Float32Array(1);
         } else {
-            const {samples, sampleRate} = this.copyCurrentBuffer();
-            const sampleCount = samples.length;
-            const startIndex = Math.floor(this.state.trimStart * sampleCount);
-            const endIndex = Math.floor(this.state.trimEnd * sampleCount);
-            if (endIndex > startIndex) { // Strictly greater to prevent 0 sample sounds
-                const clippedSamples = samples.slice(startIndex, endIndex);
-                this.submitNewSamples(clippedSamples, sampleRate);
-            } else {
-                // Just clear the trim state, it cannot be completed
-                this.setState({
-                    trimStart: null,
-                    trimEnd: null
-                });
-            }
+            newSamples = new Float32Array(newLength);
+            newSamples.set(firstPart, 0);
+            newSamples.set(secondPart, firstPart.length);
         }
+        this.submitNewSamples(newSamples, sampleRate);
+        this.setState({
+            trimStart: null,
+            trimEnd: null
+        });
     }
     handleUpdateTrimEnd (trimEnd) {
         this.setState({trimEnd});
     }
     handleUpdateTrimStart (trimStart) {
         this.setState({trimStart});
+    handleUpdateTrim (trimStart, trimEnd) {
+        this.setState({trimStart, trimEnd});
+        this.handleStopPlaying();
     }
     effectFactory (name) {
         return () => this.handleEffect(name);
@@ -184,6 +195,13 @@ class SoundEditor extends React.Component {
             this.undoStack.push(this.copyCurrentBuffer());
             this.submitNewSamples(samples, sampleRate, true);
             this.handlePlay();
+    setRef (element) {
+        this.ref = element;
+    }
+    handleContainerClick (e) {
+        // If the click is on the sound editor's div (and not any other element), delesect
+        if (e.target === this.ref) {
+            this.handleUpdateTrim(null, null);
         }
     }
     render () {
@@ -195,10 +213,12 @@ class SoundEditor extends React.Component {
                 chunkLevels={this.state.chunkLevels}
                 name={this.props.name}
                 playhead={this.state.playhead}
+                setRef={this.setRef}
                 trimEnd={this.state.trimEnd}
                 trimStart={this.state.trimStart}
-                onActivateTrim={this.handleActivateTrim}
                 onChangeName={this.handleChangeName}
+                onContainerClick={this.handleContainerClick}
+                onDelete={this.handleDelete}
                 onEcho={this.effectFactory(effectTypes.ECHO)}
                 onFaster={this.effectFactory(effectTypes.FASTER)}
                 onLouder={this.effectFactory(effectTypes.LOUDER)}
@@ -206,8 +226,7 @@ class SoundEditor extends React.Component {
                 onRedo={this.handleRedo}
                 onReverse={this.effectFactory(effectTypes.REVERSE)}
                 onRobot={this.effectFactory(effectTypes.ROBOT)}
-                onSetTrimEnd={this.handleUpdateTrimEnd}
-                onSetTrimStart={this.handleUpdateTrimStart}
+                onSetTrim={this.handleUpdateTrim}
                 onSlower={this.effectFactory(effectTypes.SLOWER)}
                 onSofter={this.effectFactory(effectTypes.SOFTER)}
                 onStop={this.handleStopPlaying}