Search code examples
javascriptgoogle-chrometext-to-speechweb-audio-apispeech-synthesis

Resuming a paused speech using speechSynthesis.resume() on chrome android doesn't work


using the browser api speechSynthesis.resume() I'm trying to resume a paused speech on android chrome

I've tested the code below on chrome desktop version 78.0.3904.97 on mac os mojave and it resumes a speech without any problem after the speech is paused. But the same code can not resume a speech on android chrome version 77.x and 78.x

steps to reproduce

  1. run the code below
  2. press play to hear a speech
  3. pause the speech midway
  4. press resume
  5. speech is resumed on desktop chrome but not on android chrome

is this a bug in chrome?

<!DOCTYPE html>
<html>
  <head>
    <title>Parcel Sandbox</title>
    <meta charset="UTF-8" />
  </head>

  <body>
    <button id="play">Play</button>
    <button id="pause">Pause</button>
    <button id="resume">Resume</button>

    <div id="data"></div>

    <script>
      const play = document.getElementById("play");
      const pause = document.getElementById("pause");
      const resume = document.getElementById("resume");

      play.addEventListener("click", function() {
        document.getElementById("data").innerText = "play";
        var utterance = new SpeechSynthesisUtterance(
          "1 2 3 4 5 6 7 8 9 10 11 12 13 14 15"
        );

        utterance.lang = "en-US";
        speechSynthesis.cancel();
        speechSynthesis.speak(utterance);
      });

      pause.addEventListener("click", function() {
        document.getElementById("data").innerText = "pause";
        speechSynthesis.pause();
      });
      
      resume.addEventListener("click", function() {
        document.getElementById("data").innerText = "resume";
        speechSynthesis.resume();
      });
    </script>
  </body>
</html>


Solution

  • Yes it's a bug: https://github.com/mdn/browser-compat-data/issues/4500

    The status is fixed so maybe it'll work in a beta or development version, or Chronium.

    As it stands, when you pause speechSynthesis its pause status is not updated and all further speak commands are put into the pending queue, giving the impression that speechSynthesis has stopped working altogether. Your code avoids this with the 'cancel' line before each 'speak' command.

    Incidentally, do you want this code to work in Firefox?

    EDIT: Hope you don't mind, I changed your code a bit and added a 'status' button to demonstrate this bug. The Status button shows the speaking, paused and pending status.

    Click 'Play' then 'Status': speaking is true, paused is false and pending is false. Then click 'Pause' and 'Status'. In Windows speaking is true and paused true, with pending false. However in Andriod the statuses are true, false, false respectively. So resume cannot do anything because paused is false.

    Thereafter, if you clicked 'Play' again the utterance just goes into the pending queue, so the statuses are true, false, true.

    <!DOCTYPE html>
    <html>
      <head>
        <title>Parcel Sandbox</title>
        <meta charset="UTF-8" />
      </head>
    
      <body>
        <button id="play">Play</button>
        <button id="pause">Pause</button>
        <button id="resume">Resume</button>
        <button id="status">Status</button>
        <button id="ssCancel">Cancel</button>
    
        <div id="data"></div>
    
        <script>
          const play = document.getElementById("play");
          const pause = document.getElementById("pause");
          const resume = document.getElementById("resume");
          const status = document.getElementById("status");
          const ssCancel = document.getElementById("ssCancel");
          const data = document.getElementById("data");
    
          play.addEventListener("click", () => {
            data.innerText = "play";
            var utterance = new SpeechSynthesisUtterance(
              "1 2 3 4 5 6 7 8 9 10 11 12 13 14 15"
            );
            utterance.lang = "en-US";
            //speechSynthesis.cancel();
            speechSynthesis.speak(utterance);
          });
    
          pause.addEventListener("click", () => {
            data.innerText = 'pause';
            speechSynthesis.pause();
          });
          
          resume.addEventListener("click", () => {
            data.innerText = "resume";
            speechSynthesis.resume();
          });
          
          status.addEventListener('click', () => {
            data.innerText = `
              Speaking: ${speechSynthesis.speaking},
              Paused: ${speechSynthesis.paused},
              Pending: ${speechSynthesis.pending}.`;
          });
          
          ssCancel.addEventListener('click', () => {
              speechSynthesis.cancel();
          });
        </script>
      </body>
    </html>

    (To better show the bug I commented out your cancel line and added a separate cancel button)