diff --git a/src/components/library-item/lib-icon--sound-rtl.svg b/src/components/library-item/lib-icon--sound-rtl.svg
new file mode 100644
index 0000000000000000000000000000000000000000..db193366a3f7c592381ca6382f49de2ba2abc5a2
Binary files /dev/null and b/src/components/library-item/lib-icon--sound-rtl.svg differ
diff --git a/src/components/library-item/lib-icon--sound.svg b/src/components/library-item/lib-icon--sound.svg
new file mode 100644
index 0000000000000000000000000000000000000000..2ffc760131cccf23d7e0346985252c5e765ccefa
Binary files /dev/null and b/src/components/library-item/lib-icon--sound.svg differ
diff --git a/src/components/library-item/library-item.css b/src/components/library-item/library-item.css
index 5e726a363950e9acd3237461c138f9adc700d5d3..79aebf79693435b0183591f64c6797967f1fb07d 100644
--- a/src/components/library-item/library-item.css
+++ b/src/components/library-item/library-item.css
@@ -7,6 +7,7 @@
     align-items: center;
     justify-content: flex-start;
     flex-basis: 160px;
+    position: relative;
     height: 160px;
     max-width: 160px;
     margin: $space;
diff --git a/src/components/library-item/library-item.jsx b/src/components/library-item/library-item.jsx
index 5af6fb15956cac258263186d819a0d2bba3e375e..81eac4801c22b7f554c31fcebbc48b9a857c24e8 100644
--- a/src/components/library-item/library-item.jsx
+++ b/src/components/library-item/library-item.jsx
@@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
 import React from 'react';
 
 import Box from '../box/box.jsx';
+import PlayButton from '../../containers/play-button.jsx';
 import styles from './library-item.css';
 import classNames from 'classnames';
 
@@ -106,8 +107,9 @@ class LibraryItemComponent extends React.PureComponent {
         ) : (
             <Box
                 className={classNames(
-                    styles.libraryItem,
-                    this.props.hidden ? styles.hidden : null
+                    styles.libraryItem, {
+                        [styles.hidden]: this.props.hidden
+                    }
                 )}
                 role="button"
                 tabIndex="0"
@@ -115,12 +117,16 @@ class LibraryItemComponent extends React.PureComponent {
                 onClick={this.props.onClick}
                 onFocus={this.props.onFocus}
                 onKeyPress={this.props.onKeyPress}
-                onMouseEnter={this.props.onMouseEnter}
-                onMouseLeave={this.props.onMouseLeave}
+                onMouseEnter={this.props.showPlayButton ? null : this.props.onMouseEnter}
+                onMouseLeave={this.props.showPlayButton ? null : this.props.onMouseLeave}
             >
                 {/* Layers of wrapping is to prevent layout thrashing on animation */}
                 <Box className={styles.libraryItemImageContainerWrapper}>
-                    <Box className={styles.libraryItemImageContainer}>
+                    <Box
+                        className={styles.libraryItemImageContainer}
+                        onMouseEnter={this.props.showPlayButton ? this.props.onMouseEnter : null}
+                        onMouseLeave={this.props.showPlayButton ? this.props.onMouseLeave : null}
+                    >
                         <img
                             className={styles.libraryItemImage}
                             src={this.props.iconURL}
@@ -128,6 +134,13 @@ class LibraryItemComponent extends React.PureComponent {
                     </Box>
                 </Box>
                 <span className={styles.libraryItemName}>{this.props.name}</span>
+                {this.props.showPlayButton ? (
+                    <PlayButton
+                        isPlaying={this.props.isPlaying}
+                        onPlay={this.props.onPlay}
+                        onStop={this.props.onStop}
+                    />
+                ) : null}
             </Box>
         );
     }
@@ -149,6 +162,7 @@ LibraryItemComponent.propTypes = {
     iconURL: PropTypes.string,
     insetIconURL: PropTypes.string,
     internetConnectionRequired: PropTypes.bool,
+    isPlaying: PropTypes.bool,
     name: PropTypes.oneOfType([
         PropTypes.string,
         PropTypes.node
@@ -158,11 +172,15 @@ LibraryItemComponent.propTypes = {
     onFocus: PropTypes.func.isRequired,
     onKeyPress: PropTypes.func.isRequired,
     onMouseEnter: PropTypes.func.isRequired,
-    onMouseLeave: PropTypes.func.isRequired
+    onMouseLeave: PropTypes.func.isRequired,
+    onPlay: PropTypes.func.isRequired,
+    onStop: PropTypes.func.isRequired,
+    showPlayButton: PropTypes.bool
 };
 
 LibraryItemComponent.defaultProps = {
-    disabled: false
+    disabled: false,
+    showPlayButton: false
 };
 
 export default LibraryItemComponent;
diff --git a/src/components/library/library.jsx b/src/components/library/library.jsx
index 13736d60f3a32045a020929c0f95560d4c689917..1272cb4a17659f0649c5495b005c6e1b255fac36 100644
--- a/src/components/library/library.jsx
+++ b/src/components/library/library.jsx
@@ -38,12 +38,13 @@ class LibraryComponent extends React.Component {
             'handleFilterClear',
             'handleMouseEnter',
             'handleMouseLeave',
+            'handlePlayingEnd',
             'handleSelect',
             'handleTagClick',
             'setFilteredDataRef'
         ]);
         this.state = {
-            selectedItem: null,
+            playingItem: null,
             filterQuery: '',
             selectedTag: ALL_TAG.tag,
             loaded: false
@@ -54,6 +55,7 @@ class LibraryComponent extends React.Component {
         setTimeout(() => {
             this.setState({loaded: true});
         });
+        if (this.props.setStopHandler) this.props.setStopHandler(this.handlePlayingEnd);
     }
     componentDidUpdate (prevProps, prevState) {
         if (prevState.filterQuery !== this.state.filterQuery ||
@@ -69,22 +71,58 @@ class LibraryComponent extends React.Component {
         this.props.onRequestClose();
     }
     handleTagClick (tag) {
-        this.setState({
-            filterQuery: '',
-            selectedTag: tag.toLowerCase()
-        });
+        if (this.state.playingItem === null) {
+            this.setState({
+                filterQuery: '',
+                selectedTag: tag.toLowerCase()
+            });
+        } else {
+            this.props.onItemMouseLeave(this.getFilteredData()[[this.state.playingItem]]);
+            this.setState({
+                filterQuery: '',
+                playingItem: null,
+                selectedTag: tag.toLowerCase()
+            });
+        }
     }
     handleMouseEnter (id) {
-        if (this.props.onItemMouseEnter) this.props.onItemMouseEnter(this.getFilteredData()[id]);
+        // don't restart if mouse over already playing item
+        if (this.props.onItemMouseEnter && this.state.playingItem !== id) {
+            this.props.onItemMouseEnter(this.getFilteredData()[id]);
+            this.setState({
+                playingItem: id
+            });
+        }
     }
     handleMouseLeave (id) {
-        if (this.props.onItemMouseLeave) this.props.onItemMouseLeave(this.getFilteredData()[id]);
+        if (this.props.onItemMouseLeave) {
+            this.props.onItemMouseLeave(this.getFilteredData()[id]);
+            this.setState({
+                playingItem: null
+            });
+        }
+    }
+    handlePlayingEnd () {
+        if (this.state.playingItem !== null) {
+            this.setState({
+                playingItem: null
+            });
+        }
     }
     handleFilterChange (event) {
-        this.setState({
-            filterQuery: event.target.value,
-            selectedTag: ALL_TAG.tag
-        });
+        if (this.state.playingItem === null) {
+            this.setState({
+                filterQuery: event.target.value,
+                selectedTag: ALL_TAG.tag
+            });
+        } else {
+            this.props.onItemMouseLeave(this.getFilteredData()[[this.state.playingItem]]);
+            this.setState({
+                filterQuery: event.target.value,
+                playingItem: null,
+                selectedTag: ALL_TAG.tag
+            });
+        }
     }
     handleFilterClear () {
         this.setState({filterQuery: ''});
@@ -185,8 +223,10 @@ class LibraryComponent extends React.Component {
                             id={index}
                             insetIconURL={dataItem.insetIconURL}
                             internetConnectionRequired={dataItem.internetConnectionRequired}
+                            isPlaying={this.state.playingItem === index}
                             key={typeof dataItem.name === 'string' ? dataItem.name : dataItem.rawURL}
                             name={dataItem.name}
+                            showPlayButton={this.props.showPlayButton}
                             onMouseEnter={this.handleMouseEnter}
                             onMouseLeave={this.handleMouseLeave}
                             onSelect={this.handleSelect}
@@ -227,12 +267,15 @@ LibraryComponent.propTypes = {
     onItemMouseLeave: PropTypes.func,
     onItemSelected: PropTypes.func,
     onRequestClose: PropTypes.func,
+    setStopHandler: PropTypes.func,
+    showPlayButton: PropTypes.bool,
     tags: PropTypes.arrayOf(PropTypes.shape(TagButton.propTypes)),
     title: PropTypes.string.isRequired
 };
 
 LibraryComponent.defaultProps = {
-    filterable: true
+    filterable: true,
+    showPlayButton: false
 };
 
 export default injectIntl(LibraryComponent);
diff --git a/src/components/play-button/icon--play.svg b/src/components/play-button/icon--play.svg
new file mode 100644
index 0000000000000000000000000000000000000000..7c4f45df771c93f5487fb64479c751600fd001ba
Binary files /dev/null and b/src/components/play-button/icon--play.svg differ
diff --git a/src/components/play-button/icon--stop.svg b/src/components/play-button/icon--stop.svg
new file mode 100644
index 0000000000000000000000000000000000000000..53837e30c9f0e93279855f364db975e17ae9386f
Binary files /dev/null and b/src/components/play-button/icon--stop.svg differ
diff --git a/src/components/play-button/play-button.css b/src/components/play-button/play-button.css
new file mode 100644
index 0000000000000000000000000000000000000000..17d2b6317730319d842127b1c4a14610dc15f8c5
--- /dev/null
+++ b/src/components/play-button/play-button.css
@@ -0,0 +1,42 @@
+
+@import "../../css/colors.css";
+@import "../../css/units.css";
+
+.play-button {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+
+    overflow: hidden;  /* Mask the icon animation */
+    width: 2.5rem;
+    height: 2.5rem;
+    background-color: $sound-primary;
+    color: $ui-white;
+    border-radius: 50%;
+    font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
+    user-select: none;
+    cursor: pointer;
+    transition: all 0.15s ease-out;
+}
+
+.play-button {
+    position: absolute;
+    top: .5rem;
+    z-index: auto;
+}
+
+.play-button:focus {
+    outline: none;
+}
+
+.play-icon {
+  width: 50%;
+}
+
+[dir="ltr"] .play-button {
+    right: .5rem;
+}
+
+[dir="rtl"] .play-button {
+    left: .5rem;
+}
diff --git a/src/components/play-button/play-button.jsx b/src/components/play-button/play-button.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..15f8226ac1d9c5cea3516ac48a90c3f2a653e25d
--- /dev/null
+++ b/src/components/play-button/play-button.jsx
@@ -0,0 +1,73 @@
+import PropTypes from 'prop-types';
+import React from 'react';
+import classNames from 'classnames';
+
+import {defineMessages, injectIntl, intlShape} from 'react-intl';
+
+import styles from './play-button.css';
+
+import playIcon from './icon--play.svg';
+import stopIcon from './icon--stop.svg';
+
+const messages = defineMessages({
+    play: {
+        id: 'gui.playButton.play',
+        description: 'Title of the button to start playing the sound',
+        defaultMessage: 'Play'
+    },
+    stop: {
+        id: 'gui.playButton.stop',
+        description: 'Title of the button to stop the sound',
+        defaultMessage: 'Stop'
+    }
+});
+
+const PlayButtonComponent = ({
+    className,
+    intl,
+    isPlaying,
+    onClick,
+    onMouseDown,
+    onMouseEnter,
+    onMouseLeave,
+    setButtonRef,
+    ...props
+}) => {
+    const label = isPlaying ?
+        intl.formatMessage(messages.stop) :
+        intl.formatMessage(messages.play);
+
+    return (
+        <div
+            aria-label={label}
+            className={classNames(styles.playButton, className, {
+                [styles.playing]: isPlaying
+            })}
+            onClick={onClick}
+            onMouseDown={onMouseDown}
+            onMouseEnter={onMouseEnter}
+            onMouseLeave={onMouseLeave}
+            ref={setButtonRef}
+            {...props}
+        >
+            <img
+                className={styles.playIcon}
+                draggable={false}
+                src={isPlaying ? stopIcon : playIcon}
+            />
+        </div>
+    );
+};
+
+PlayButtonComponent.propTypes = {
+    className: PropTypes.string,
+    intl: intlShape,
+    isPlaying: PropTypes.bool.isRequired,
+    onClick: PropTypes.func.isRequired,
+    onMouseDown: PropTypes.func.isRequired,
+    onMouseEnter: PropTypes.func.isRequired,
+    onMouseLeave: PropTypes.func.isRequired,
+    setButtonRef: PropTypes.func.isRequired
+};
+
+export default injectIntl(PlayButtonComponent);
diff --git a/src/containers/library-item.jsx b/src/containers/library-item.jsx
index 6177b916fd69c00107decb03b1d0de13073194ae..8881a57cc0a77eedd12613469dab3a4b4621c704 100644
--- a/src/containers/library-item.jsx
+++ b/src/containers/library-item.jsx
@@ -15,6 +15,8 @@ class LibraryItem extends React.PureComponent {
             'handleKeyPress',
             'handleMouseEnter',
             'handleMouseLeave',
+            'handlePlay',
+            'handleStop',
             'rotateIcon',
             'startRotatingIcons',
             'stopRotatingIcons'
@@ -37,7 +39,9 @@ class LibraryItem extends React.PureComponent {
         e.preventDefault();
     }
     handleFocus (id) {
-        this.handleMouseEnter(id);
+        if (!this.props.showPlayButton) {
+            this.handleMouseEnter(id);
+        }
     }
     handleKeyPress (e) {
         if (e.key === ' ' || e.key === 'Enter') {
@@ -46,22 +50,34 @@ class LibraryItem extends React.PureComponent {
         }
     }
     handleMouseEnter () {
-        this.props.onMouseEnter(this.props.id);
-        if (this.props.icons && this.props.icons.length) {
-            this.stopRotatingIcons();
-            this.setState({
-                isRotatingIcon: true
-            }, this.startRotatingIcons);
+        // only show hover effects on the item if not showing a play button
+        if (!this.props.showPlayButton) {
+            this.props.onMouseEnter(this.props.id);
+            if (this.props.icons && this.props.icons.length) {
+                this.stopRotatingIcons();
+                this.setState({
+                    isRotatingIcon: true
+                }, this.startRotatingIcons);
+            }
         }
     }
     handleMouseLeave () {
-        this.props.onMouseLeave(this.props.id);
-        if (this.props.icons && this.props.icons.length) {
-            this.setState({
-                isRotatingIcon: false
-            }, this.stopRotatingIcons);
+        // only show hover effects on the item if not showing a play button
+        if (!this.props.showPlayButton) {
+            this.props.onMouseLeave(this.props.id);
+            if (this.props.icons && this.props.icons.length) {
+                this.setState({
+                    isRotatingIcon: false
+                }, this.stopRotatingIcons);
+            }
         }
     }
+    handlePlay () {
+        this.props.onMouseEnter(this.props.id);
+    }
+    handleStop () {
+        this.props.onMouseLeave(this.props.id);
+    }
     startRotatingIcons () {
         this.rotateIcon();
         this.intervalId = setInterval(this.rotateIcon, 300);
@@ -104,13 +120,17 @@ class LibraryItem extends React.PureComponent {
                 id={this.props.id}
                 insetIconURL={this.props.insetIconURL}
                 internetConnectionRequired={this.props.internetConnectionRequired}
+                isPlaying={this.props.isPlaying}
                 name={this.props.name}
+                showPlayButton={this.props.showPlayButton}
                 onBlur={this.handleBlur}
                 onClick={this.handleClick}
                 onFocus={this.handleFocus}
                 onKeyPress={this.handleKeyPress}
                 onMouseEnter={this.handleMouseEnter}
                 onMouseLeave={this.handleMouseLeave}
+                onPlay={this.handlePlay}
+                onStop={this.handleStop}
             />
         );
     }
@@ -137,13 +157,15 @@ LibraryItem.propTypes = {
     id: PropTypes.number.isRequired,
     insetIconURL: PropTypes.string,
     internetConnectionRequired: PropTypes.bool,
+    isPlaying: PropTypes.bool,
     name: PropTypes.oneOfType([
         PropTypes.string,
         PropTypes.node
     ]),
     onMouseEnter: PropTypes.func.isRequired,
     onMouseLeave: PropTypes.func.isRequired,
-    onSelect: PropTypes.func.isRequired
+    onSelect: PropTypes.func.isRequired,
+    showPlayButton: PropTypes.bool
 };
 
 export default injectIntl(LibraryItem);
diff --git a/src/containers/play-button.jsx b/src/containers/play-button.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..f0ed8b6a2026fdd57d11718f69001a7885e44667
--- /dev/null
+++ b/src/containers/play-button.jsx
@@ -0,0 +1,115 @@
+import PropTypes from 'prop-types';
+import React from 'react';
+import bindAll from 'lodash.bindall';
+
+import PlayButtonComponent from '../components/play-button/play-button.jsx';
+
+class PlayButton extends React.Component {
+    constructor (props) {
+        super(props);
+        bindAll(this, [
+            'handleClick',
+            'handleMouseDown',
+            'handleMouseEnter',
+            'handleMouseLeave',
+            'handleTouchStart',
+            'setButtonRef'
+        ]);
+        this.state = {
+            touchStarted: false
+        };
+    }
+    getDerivedStateFromProps (props, state) {
+        // if touchStarted is true and it's not playing, the sound must have ended.
+        // reset the touchStarted state to allow the sound to be replayed
+        if (state.touchStarted && !props.isPlaying) {
+            return {
+                touchStarted: false
+            };
+        }
+        return null; // nothing changed
+    }
+    componentDidMount () {
+        // Touch start
+        this.buttonRef.addEventListener('touchstart', this.handleTouchStart);
+    }
+    componentWillUnmount () {
+        this.buttonRef.removeEventListener('touchstart', this.handleTouchStart);
+    }
+    handleClick (e) {
+        //  stop the click from propagating out of the button
+        e.stopPropagation();
+    }
+    handleMouseDown (e) {
+        // prevent default (focus) on mouseDown
+        e.preventDefault();
+        if (this.props.isPlaying) {
+            // stop sound and reset touch state
+            this.props.onStop();
+            if (this.state.touchstarted) this.setState({touchStarted: false});
+        } else {
+            this.props.onPlay();
+            if (this.state.touchstarted) {
+                // started on touch, but now clicked mouse
+                this.setState({touchStarted: false});
+            }
+        }
+    }
+    handleTouchStart (e) {
+        if (this.props.isPlaying) {
+            // If playing, stop sound, and reset touch state
+            e.preventDefault();
+            this.setState({touchStarted: false});
+            this.props.onStop();
+        } else {
+            // otherwise start playing, and set touch state
+            e.preventDefault();
+            this.setState({touchStarted: true});
+            this.props.onPlay();
+        }
+    }
+    handleMouseEnter (e) {
+        // start the sound if it's not already playing
+        e.preventDefault();
+        if (!this.props.isPlaying) {
+            this.props.onPlay();
+        }
+    }
+    handleMouseLeave () {
+        // stop the sound unless it was started by touch
+        if (this.props.isPlaying && !this.state.touchstarted) {
+            this.props.onStop();
+        }
+    }
+    setButtonRef (ref) {
+        this.buttonRef = ref;
+    }
+    render () {
+        const {
+            className,
+            isPlaying,
+            onPlay, // eslint-disable-line no-unused-vars
+            onStop // eslint-disable-line no-unused-vars
+        } = this.props;
+        return (
+            <PlayButtonComponent
+                className={className}
+                isPlaying={isPlaying}
+                onClick={this.handleClick}
+                onMouseDown={this.handleMouseDown}
+                onMouseEnter={this.handleMouseEnter}
+                onMouseLeave={this.handleMouseLeave}
+                setButtonRef={this.setButtonRef}
+            />
+        );
+    }
+}
+
+PlayButton.propTypes = {
+    className: PropTypes.string,
+    isPlaying: PropTypes.bool.isRequired,
+    onPlay: PropTypes.func.isRequired,
+    onStop: PropTypes.func.isRequired
+};
+
+export default PlayButton;
diff --git a/src/containers/sound-library.jsx b/src/containers/sound-library.jsx
index e49f1057ab0194628029a02069ed758ff76bc4c6..cdb5c7c6a1f4fdaa693d4c756710e5149027128c 100644
--- a/src/containers/sound-library.jsx
+++ b/src/containers/sound-library.jsx
@@ -7,8 +7,8 @@ import AudioEngine from 'scratch-audio';
 
 import LibraryComponent from '../components/library/library.jsx';
 
-import soundIcon from '../components/asset-panel/icon--sound.svg';
-import soundIconRtl from '../components/asset-panel/icon--sound-rtl.svg';
+import soundIcon from '../components/library-item/lib-icon--sound.svg';
+import soundIconRtl from '../components/library-item/lib-icon--sound-rtl.svg';
 
 import soundLibraryContent from '../lib/libraries/sounds.json';
 import soundTags from '../lib/libraries/sound-tags';
@@ -29,7 +29,9 @@ class SoundLibrary extends React.PureComponent {
         bindAll(this, [
             'handleItemSelected',
             'handleItemMouseEnter',
-            'handleItemMouseLeave'
+            'handleItemMouseLeave',
+            'onStop',
+            'setStopHandler'
         ]);
 
         /**
@@ -43,6 +45,11 @@ class SoundLibrary extends React.PureComponent {
          * @type {Promise<SoundPlayer>}
          */
         this.playingSoundPromise = null;
+
+        /**
+         * function to call when the sound ends
+         */
+        this.handleStop = null;
     }
     componentDidMount () {
         this.audioEngine = new AudioEngine();
@@ -51,10 +58,22 @@ class SoundLibrary extends React.PureComponent {
     componentWillUnmount () {
         this.stopPlayingSound();
     }
+    onStop () {
+        if (this.playingSoundPromise !== null) {
+            this.playingSoundPromise.then(soundPlayer => soundPlayer.removeListener('stop', this.onStop));
+            if (this.handleStop) this.handleStop();
+        }
+
+    }
+    setStopHandler (func) {
+        this.handleStop = func;
+    }
     stopPlayingSound () {
         // Playback is queued, playing, or has played recently and finished
         // normally.
         if (this.playingSoundPromise !== null) {
+            // Forcing sound to stop, so stop listening for sound ending:
+            this.playingSoundPromise.then(soundPlayer => soundPlayer.removeListener('stop', this.onStop));
             // Queued playback began playing before this method.
             if (this.playingSoundPromise.isPlaying) {
                 // Fetch the player from the promise and stop playback soon.
@@ -102,6 +121,7 @@ class SoundLibrary extends React.PureComponent {
                 // Play the sound. Playing the sound will always come before a
                 // paired stop if the sound must stop early.
                 soundPlayer.play();
+                soundPlayer.addListener('stop', this.onStop);
                 // Set that the sound is playing. This affects the type of stop
                 // instruction given if the sound must stop early.
                 if (this.playingSoundPromise !== null) {
@@ -141,8 +161,10 @@ class SoundLibrary extends React.PureComponent {
 
         return (
             <LibraryComponent
+                showPlayButton
                 data={soundLibraryThumbnailData}
                 id="soundLibrary"
+                setStopHandler={this.setStopHandler}
                 tags={soundTags}
                 title={this.props.intl.formatMessage(messages.libraryTitle)}
                 onItemMouseEnter={this.handleItemMouseEnter}