diff --git a/playback.js b/playback.js
index 1d89719435ef01c1fa9338251931a2d2171d5881..f10a07144df7e887d966d46d9399f4d141db8270 100644
--- a/playback.js
+++ b/playback.js
@@ -89,15 +89,6 @@ SpicePlaybackConn.prototype.process_channel_message = function(msg)
     {
         var data = new SpiceMsgPlaybackData(msg.data);
 
-        // If this packet has the same time as the last, just bump up by one.
-        if (this.last_data_time && data.time <= this.last_data_time)
-        {
-            // FIXME - this is arguably wrong.  But delaying the transmission was worse,
-            //          in initial testing.  Could use more research.
-            PLAYBACK_DEBUG > 1 && console.log("Hacking time of " + data.time + " to " + this.last_data_time + 1);
-            data.time = this.last_data_time + 1;
-        }
-
         if (! this.source_buffer)
             return true;
 
@@ -110,8 +101,35 @@ SpicePlaybackConn.prototype.process_channel_message = function(msg)
             this.audio.currentTime = this.audio.buffered.start(this.audio.buffered.length - 1);
         }
 
-        this.last_data_time = data.time;
+        /* Around version 45, Firefox started being very particular about the
+           time stamps put into the Opus stream.  The time stamps from the Spice server are
+           somewhat irregular.  They mostly arrive every 10 ms, but sometimes it is 11, or sometimes
+           with two time stamps the same in a row.  The previous logic resulted in fuzzy and/or
+           distorted audio streams in Firefox in a row.
+
+           In theory, the sequence mode should be appropriate for us, but as of 09/27/2016,
+           I was unable to make sequence mode work with Firefox.
 
+           Thus, we end up with an inelegant hack.  Essentially, we force every packet to have
+           a 10ms time delta, unless there is an obvious gap in time stream, in which case we
+           will resync.
+        */
+
+        if (this.start_time != 0 && data.time != (this.last_data_time + EXPECTED_PACKET_DURATION))
+        {
+            if (Math.abs(data.time - (EXPECTED_PACKET_DURATION + this.last_data_time)) < MAX_CLUSTER_TIME)
+            {
+                PLAYBACK_DEBUG > 1 && console.log("Hacking time of " + data.time + " to " +
+                                      (this.last_data_time + EXPECTED_PACKET_DURATION));
+                data.time = this.last_data_time + EXPECTED_PACKET_DURATION;
+            }
+            else
+            {
+                PLAYBACK_DEBUG > 1 && console.log("Apparent gap in audio time; now is " + data.time + " last was " + this.last_data_time);
+            }
+        }
+
+        this.last_data_time = data.time;
 
         PLAYBACK_DEBUG > 1 && console.log("PlaybackData; time " + data.time + "; length " + data.data.byteLength);
 
diff --git a/webm.js b/webm.js
index 8faa8e7edc5e77c3b7bfbde28784d16d7ae803d0..789da1441647a7c40aedbf1c32b83b9e503d5391 100644
--- a/webm.js
+++ b/webm.js
@@ -84,6 +84,7 @@ var OPUS_CHANNELS                           = 2;
 var SPICE_PLAYBACK_CODEC                    = 'audio/webm; codecs="opus"';
 var MAX_CLUSTER_TIME                        = 1000;
 
+var EXPECTED_PACKET_DURATION                = 10;
 var GAP_DETECTION_THRESHOLD                 = 50;
 
 var SPICE_VP8_CODEC                         = 'video/webm; codecs="vp8"';