diff --git a/src/containers/sound-editor.jsx b/src/containers/sound-editor.jsx
index 28fe3efb5087731ce706108ca8737aed3fcc8898..781b2cc5fe51f9c068c46a7ae7467d0c93e9a10f 100644
--- a/src/containers/sound-editor.jsx
+++ b/src/containers/sound-editor.jsx
@@ -16,7 +16,7 @@ class SoundEditor extends React.Component {
     constructor (props) {
         super(props);
         bindAll(this, [
-            'copyCurrentSamples',
+            'copyCurrentBuffer',
             'handleStoppedPlaying',
             'handleChangeName',
             'handlePlay',
@@ -70,7 +70,7 @@ class SoundEditor extends React.Component {
             if (this.undoStack.length >= UNDO_STACK_SIZE) {
                 this.undoStack.shift(); // Drop the first element off the array
             }
-            this.undoStack.push(this.copyCurrentSamples());
+            this.undoStack.push(this.copyCurrentBuffer());
         }
         this.resetState(samples, sampleRate);
         this.props.onUpdateSoundBuffer(
@@ -102,12 +102,12 @@ class SoundEditor extends React.Component {
         if (this.state.trimStart === null && this.state.trimEnd === null) {
             this.setState({trimEnd: 0.95, trimStart: 0.05});
         } else {
-            const samples = this.copyCurrentSamples();
+            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 clippedSamples = samples.slice(startIndex, endIndex);
-            this.submitNewSamples(clippedSamples, this.props.sampleRate);
+            this.submitNewSamples(clippedSamples, sampleRate);
         }
     }
     handleUpdateTrimEnd (trimEnd) {
@@ -119,9 +119,12 @@ class SoundEditor extends React.Component {
     effectFactory (name) {
         return () => this.handleEffect(name);
     }
-    copyCurrentSamples () {
+    copyCurrentBuffer () {
         // Cannot reliably use props.samples because it gets detached by Firefox
-        return this.audioBufferPlayer.buffer.getChannelData(0);
+        return {
+            samples: this.audioBufferPlayer.buffer.getChannelData(0),
+            sampleRate: this.audioBufferPlayer.buffer.sampleRate
+        };
     }
     handleEffect (name) {
         const effects = new AudioEffects(this.audioBufferPlayer.buffer, name);
@@ -133,18 +136,18 @@ class SoundEditor extends React.Component {
         });
     }
     handleUndo () {
-        this.redoStack.push(this.copyCurrentSamples());
-        const samples = this.undoStack.pop();
+        this.redoStack.push(this.copyCurrentBuffer());
+        const {samples, sampleRate} = this.undoStack.pop();
         if (samples) {
-            this.submitNewSamples(samples, this.props.sampleRate, true);
+            this.submitNewSamples(samples, sampleRate, true);
             this.handlePlay();
         }
     }
     handleRedo () {
-        const samples = this.redoStack.pop();
+        const {samples, sampleRate} = this.redoStack.pop();
         if (samples) {
-            this.undoStack.push(this.copyCurrentSamples());
-            this.submitNewSamples(samples, this.props.sampleRate, true);
+            this.undoStack.push(this.copyCurrentBuffer());
+            this.submitNewSamples(samples, sampleRate, true);
             this.handlePlay();
         }
     }
diff --git a/src/lib/audio/audio-buffer-player.js b/src/lib/audio/audio-buffer-player.js
index e9e89c787f9fae23eae39c4b3f08e8a6ce601b04..9814d9807503898b6f31d00960855cf43a1d8662 100644
--- a/src/lib/audio/audio-buffer-player.js
+++ b/src/lib/audio/audio-buffer-player.js
@@ -45,7 +45,13 @@ class AudioBufferPlayer {
     stop () {
         if (this.source) {
             this.source.onended = null; // Do not call onEnded callback if manually stopped
-            this.source.stop();
+            try {
+                this.source.stop();
+            } catch (e) {
+                // This is probably Safari, which dies when you call stop more than once
+                // which the spec says is allowed: https://developer.mozilla.org/en-US/docs/Web/API/AudioBufferSourceNode
+                console.log('Caught error while stopping buffer source node.'); // eslint-disable-line no-console
+            }
         }
     }
 }
diff --git a/src/lib/audio/audio-effects.js b/src/lib/audio/audio-effects.js
index ead04bf504550f85c9837f3acb1c38adf18835d6..067dcc6209397782d248a3a04ce8ad11f0f668d9 100644
--- a/src/lib/audio/audio-effects.js
+++ b/src/lib/audio/audio-effects.js
@@ -38,8 +38,14 @@ class AudioEffects {
             buffer.getChannelData(0).reverse();
             break;
         }
-        const OfflineAudioContext = window.OfflineAudioContext || window.webkitOfflineAudioContext;
-        this.audioContext = new OfflineAudioContext(1, sampleCount, buffer.sampleRate);
+        if (window.OfflineAudioContext) {
+            this.audioContext = new window.OfflineAudioContext(1, sampleCount, buffer.sampleRate);
+        } else {
+            // Need to use webkitOfflineAudioContext, which doesn't support all sample rates.
+            // Resample by adjusting sample count to make room and set offline context to desired sample rate.
+            const sampleScale = 44100 / buffer.sampleRate;
+            this.audioContext = new window.webkitOfflineAudioContext(1, sampleScale * sampleCount, 44100);
+        }
         this.buffer = buffer;
         this.source = this.audioContext.createBufferSource();
         this.source.buffer = this.buffer;