title: javascript oscillator not responding randomly in firefox
body: <p>So I tried to write a simple alarm clock in javascript. The goal was a website that would simply sound a tone repeatedly at specified time and that could be loaded while online and would continue to work even if the internet connection breaks after the page is loaded. Easy, right?</p>
  
  <p>The problem is generating the sound.</p>
  
  <p>My first attempt was this:</p>
  
  <p><div class="snippet" data-lang="js" data-hide="false">
  <div class="snippet-code">
  <pre class="snippet-code-js lang-js prettyprint-override"><code>function beep() {
    var sound = new Audio( "&lt;base64 encoded waw&gt;" );
    sound.play();
  }
  
  var beeping;
  
  function startBeeping() {
    beeping = setInterval(beep, 500);
  }
  
  function stopBeeping() {
    clearInterval(beeping);
  }</code></pre>
  </div>
  </div>
  </p>
  
  <p>This works great in Firefox (Linux, 44.0), but it beeps exactly once after loading the page in Chrome (Linux, 48.0.2564.109). I tried to search StackOverflow and other sources and tried to play with the code for quite a while (e.g. declaring the <code>sound</code> variable outside of the function, reseting it after every beep again, calling <code>sound.load()</code>, setting <code>sound.currentTime=0</code>, declaring an <code>&lt;audio&gt;</code> HTML5 element cloning it after every beep...), but no matter what I do, Chrome just won't play the sound more than once per page load. Apparently, there has been some issues with this in the past, but they are marked as resolved for my version of Chrome.</p>
  
  <p>My second attempt was this:</p>
  
  <p><div class="snippet" data-lang="js" data-hide="false">
  <div class="snippet-code">
  <pre class="snippet-code-js lang-js prettyprint-override"><code>var audioCtx = new (window.AudioContext || window.webkitAudioContext || window.audioContext);
  
  var oscillator = audioCtx.createOscillator();
  var gainNode = audioCtx.createGain();
  gainNode.connect(audioCtx.destination);
  oscillator.connect(gainNode);
  
  gainNode.gain.value = 50;
  oscillator.frequency.value = 880;
  oscillator.type = "sawtooth";
  oscillator.start();
  oscillator.disconnect(gainNode);
  
  function startTone() {
    oscillator.connect(gainNode);
  };
  
  function stopTone() {
    oscillator.disconnect(gainNode);
  }
  
  function beep() {
    startTone();
    setTimeout(stopTone, 200);
  }</code></pre>
  </div>
  </div>
  </p>
  
  <p>Together with the same <code>startBeeping()</code> and <code>stopBeeping()</code> functions this works great in Chrome, but RANDOMLY doesn't work in Firefox (I'll explain, I promise).</p>
  
  <p>I also tried using <code>oscillator.start()</code> and <code>oscillator.stop()</code>, while declaring the <code>oscillator</code> and <code>gainNode</code> variables again after every beep, using <code>oscillator.detune()</code> and <code>oscillator.start()</code> instead, setting <code>oscillator.frequency.value=0</code> and back to 880Hz for turning it on and off... the problem is, that while all of these things work perfectly in Chrome, the <code>beep()</code> function works only every second time I enter it into the console on Firefox. That means that I load the page, type <code>beep()</code> into the console and nothing happens. Then I enter <code>beep()</code> into the console again and it beeps. Then again and nothing happens and so on. Every second time it beeps. Even more interestingly, this is true even if I enter <code>beep(); beep()</code> into the console. Nothing happens the first time, but it works the second time (beeping only once).</p>
  
  <p>Different versions of this code also produced some complaints about the oscillator object "not being" or "being in the wrong state" (that's literally what the message said), or even about the syntax error "missing ]", which was totally bizarre, since there really were no syntax errors, no square brackets anywhere and the function still worked every second time...</p>
  
  <p>My last version (bellow, I used it saved in a single <code>alarm.html</code> file and opened locally) always works in Chrome, but in Firefox works, or doesn't work totally at random. With no changes to the code, no new tabs being opened, just reloading and/or trying to use the page multiple times). That means, that I load the page once, and it may or may not work in the beginning (you can know weather it works based on weather the beep sound can be heard when you start the alarm), then it works one or more times and then again not. The number of times when it works or not changes all the time and I didn't figure out any circumstances that affect it... except maybe moving around some of my DOM manipulating code makes it work more or less reliably (for example, moving the <code>setCurrentTime()</code> call into the <code>beep()</code> breaks it almost every time for me).</p>
  
  <p><div class="snippet" data-lang="js" data-hide="false">
  <div class="snippet-code">
  <pre class="snippet-code-html lang-html prettyprint-override"><code>&lt;html&gt;
  &lt;head&gt;
  &lt;script&gt;
  function setCurrentTime() {
    yyyy = document.getElementById("YYYY");
    mm = document.getElementById("MM");
    dd = document.getElementById("DD");
    h = document.getElementById("h");
    m = document.getElementById("m");
    s = document.getElementById("s");
  
    now = new Date();
    yyyy.value = now.getFullYear();
    mm.value = now.getMonth();
    dd.value = now.getDate();
    h.value = now.getHours();
    m.value = now.getMinutes();
    s.value = now.getSeconds();
  }
  
  var audioCtx = new (window.AudioContext || window.webkitAudioContext || window.audioContext);
  
  var oscillator = audioCtx.createOscillator();
  var gainNode = audioCtx.createGain();
  gainNode.connect(audioCtx.destination);
  oscillator.connect(gainNode);
  
  gainNode.gain.value = 50;
  oscillator.frequency.value = 880;
  oscillator.type = "sawtooth";
  oscillator.start();
  oscillator.disconnect(gainNode);
  
  function startTone() {
    oscillator.connect(gainNode);
  };
  
  function stopTone() {
    oscillator.disconnect(gainNode);
  }
  
  function beep() {
    startTone();
    setTimeout(stopTone, 200);
  }
  
  var beeping;
  
  function startBeeping() {
    beeping = setInterval(beep, 500);
  
    document.getElementById("stopBeepingButton").disabled = false;
  
    document.getElementById("clearAlarmButton").disabled = true;
  
    document.getElementById("YYYY").disabled = false;
    document.getElementById("MM").disabled = false;
    document.getElementById("DD").disabled = false;
    document.getElementById("h").disabled = false;
    document.getElementById("m").disabled = false;
    document.getElementById("s").disabled = false;
  } 
  
  function stopBeeping() {
    clearInterval(beeping);
    setCurrentTime(); // into text fields
    document.getElementById("setAlarmButton").disabled = false;
    document.getElementById("stopBeepingButton").disabled = true;
  } 
  
  var alarm;
  
  function setAlarm() {
    beep();
  
    yyyy = document.getElementById("YYYY");
    mm = document.getElementById("MM");
    dd = document.getElementById("DD");
    h = document.getElementById("h");
    m = document.getElementById("m");
    s = document.getElementById("s");
  
    when = new Date(yyyy.value, mm.value, dd.value, h.value, m.value, s.value);
    now  = new Date();
    timeout = when.getTime() - now.getTime();
    if (timeout &gt; 0) {
      alarm = setTimeout(startBeeping, timeout);
  
      document.getElementById("clearAlarmButton").disabled = false;
      
      document.getElementById("setAlarmButton").disabled = true;
      document.getElementById("YYYY").disabled = true;
      document.getElementById("MM").disabled = true;
      document.getElementById("DD").disabled = true;
      document.getElementById("h").disabled = true;
      document.getElementById("m").disabled = true;
      document.getElementById("s").disabled = true;
    }
    else {
      alert("Alarm must be set in the future!");
    }
  }
  
  function clearAlarm() {
    clearTimeout(alarm);
  
    document.getElementById("clearAlarmButton").disabled = true;
  
    document.getElementById("setAlarmButton").disabled = false;
    document.getElementById("YYYY").disabled = false;
    document.getElementById("MM").disabled = false;
    document.getElementById("DD").disabled = false;
    document.getElementById("h").disabled = false;
    document.getElementById("m").disabled = false;
    document.getElementById("s").disabled = false;
  }
  &lt;/script&gt;
  &lt;/head&gt;
  &lt;body onload="setCurrentTime();"&gt;
  &lt;p&gt;
  &lt;input type="text" name="year" id="YYYY" size="4" maxlength="4"&gt;
  -
  &lt;input type="text" name="month" id="MM" size="2" maxlength="2"&gt;
  -
  &lt;input type="text" name="day" id="DD" size="2" maxlength="2"&gt;
  /
  &lt;input type="text" name="hour" id="h" size="2" maxlength="2"&gt;
  :
  &lt;input type="text" name="minute" id="m" size="2" maxlength="2"&gt;
  :
  &lt;input type="text" name="second" id="s" size="2" maxlength="2"&gt;
  &lt;/p&gt;
  
  &lt;button onclick="setAlarm();" id="setAlarmButton"&gt;Set Alarm&lt;/button&gt;
  &lt;button onclick="clearAlarm();" id="clearAlarmButton" disabled="true"&gt;Cancel Alarm&lt;/button&gt;
  &lt;button onclick="stopBeeping();" id="stopBeepingButton" disabled="true"&gt;Stop Beeping!&lt;/button&gt;
  &lt;/body&gt;
  &lt;/html&gt;</code></pre>
  </div>
  </div>
  </p>
  
  <p>What kind of woodoo is this?</p>
  
  <p><strong>Please, don't refer me to some jQuery thingie or that one JS library/web-app that will surely work in your browser - I am only interested in what is going on with this particular code. I do not really need to have a working alarm in JS.</strong></p>