Plasticity and Online Tone Generator now work in Firefox and Chrome

I have just uploaded new versions of Plasticity – my audio training game which may also alleviate tinnitus – and my increasingly popular Online Tone Generator – a handy tool for those times when you need your speakers to produce a specific frequency, and nothing else. The current versions use the HTML5 Web Audio API and have been tested to work on Chrome 33 and Firefox 28, at least on Windows. (They might also work on recent versions of other browsers – it’s worth a try.) Enjoy!

Web Audio API – things I learned the hard way

Firefox recently dropped support for the Mozilla Audio Data API, so I started porting my two audio-enabled Web apps (Plasticity and Online Tone Generator) to the Web Audio API, which is the W3C-blessed standard way to work with audio in the browser.

In the process, I ran into a few problems, which I thought I’d share here for the benefit of other developers making their first steps with the Web Audio API.

AudioBufferSourceNodes and OscillatorNodes are single-use entities

Suppose we want to generate two 440 Hz tones, each with a duration of 1 second, separated with a 1-second pause. This is not the way to do it:

oscillator = context.createOscillator();
oscillator.frequency.value = 440;
currentTime = context.currentTime;
oscillator.stop(currentTime + 1); //stop after 1 second
oscillator.start(currentTime + 2); //resume after 2 seconds
oscillator.stop(currentTime + 3); //stop again after 3 seconds

What’s wrong? We cannot call .start() on an OscillatorNode or AudioBufferSourceNode more than once. The second call in the above code will result in an error. Both OscillatorNodes (which are used to generate simple tones) and AudioBufferSourceNodes (which are used to play back short samples like sound effects) are meant to be thrown away after each use.

Instead, we should create a separate node for every time we want to play a sound. Every time, we must also connect it to the audio graph:

oscillator = context.createOscillator();
oscillator.frequency.value = 440;
currentTime = context.currentTime;
oscillator.stop(currentTime + 1);

oscillator2 = context.createOscillator(); //create 2nd oscillator
oscillator2.frequency.value = 440;
oscillator2.start(currentTime + 2);
oscillator2.stop(currentTime + 3);

ChannelMergerNode inputs don’t map to channels

Warning: This will be fixed in the next Working Draft of the W3C spec; the below text will eventually become obsolete when browsers implement the new spec.

What do you do when you have two mono sources – like OscillatorNodes, which are always mono, or AudioBufferSourceNodes connected to a mono buffer – and you want to mix them into a stereo signal, for example, play one sample in the left channel, and the other in the right? You use a ChannelMergerNode.

A ChannelMergerNode has a number of inputs, but only one output. It takes the input audio signals and mixes them into a single multichannel signal. Sounds pretty simple, but it’s easy to fall into the trap of assuming that inputs correspond to channels in the output signal. For example, take a look at the following code, which tries to play a tone on the right channel only:

oscillatorR = context.createOscillator();
oscillatorR.frequency.value = 440;
mergerNode = context.createChannelMerger(2);
//create mergerNode with 2 inputs

oscillatorR.connect(mergerNode, 0, 1);
//connect output #0 of the oscillator to input #1 of the mergerNode
//we're leaving input #0 of the mergerNode empty
currentTime = context.currentTime;
oscillatorR.stop(currentTime + 2);

The result of running this code is a tone playing in both channels at the same time. Why? Because inputs of a ChannelMergerNode do not map to channels in the output signal. If an input is not connected, ChannelMergerNode will ignore it. In this case, the first input (numbered 0) is not connected. The only connected input is #1, and it has a mono signal. ChannelMerger merges all the channels on all the connected inputs into a single output. Here, it receives only a single mono signal, so it will output a mono signal, which you will hear coming from both speakers, as you always do with mono sounds.

The right way to have a sound playing only in one channel is to create a “dummy” source node and connect it to the ChannelMergerNode:

context = makeAudioContext();
oscillatorR = context.createOscillator();
oscillatorR.frequency.value = 440;
mergerNode = context.createChannelMerger(2); //create mergerNode with 2 inputs

silence = context.createBufferSource();
silence.connect(mergerNode, 0, 0);
//connect dummy source to input #0 of the mergerNode
oscillatorR.connect(mergerNode, 0, 1);
//connect output #0 of the oscillator to input #1 of the mergerNode
currentTime = context.currentTime;
oscillatorR.stop(currentTime + 2);

You create a silence node by creating an AudioBufferSourceNode, just like you would for any sample, and then not initializing the buffer property. The W3C spec guarantees that this produces a single channel of silence. (As of April 2014, this works in Chrome, but does not work in Firefox 28. In Firefox, the input is ignored and the result is the tone playing on both channels.)

Update: I’m happy to say that my feedback to the W3C Audio Working Group has resulted in changes to the spec. Per the latest Editor’s Draft, ChannelMergerNodes accept up to 6 mono inputs (other types of inputs will be downmixed to mono), with empty inputs treated as silence rather than omitted. Now the changes have to be published in the next Working Draft, and then the browsers have to implement them.

Unused nodes get removed automatically

You might think that if you want to have two sounds playing in different channels – one sound in left, another in right – you don’t need to create dummy nodes. After all, the ChannelMergerNode will have two input channels.

In the code below, we want to play a 440 Hz tone in the left channel for 2 seconds, and a 2400 Hz tone in the right channel for 4 seconds. Both tones start at the same time.

oscillatorL = context.createOscillator();
oscillatorL.frequency.value = 440;
oscillatorR = context.createOscillator();
oscillatorR.frequency.value = 2400;
mergerNode = context.createChannelMerger(2); //create mergerNode with 2 inputs

oscillatorL.connect(mergerNode, 0, 0);
//connect output #0 of the oscillator to input #0 of the mergerNode
oscillatorR.connect(mergerNode, 0, 1);
//connect output #0 of the oscillator to input #1 of the mergerNode
currentTime = context.currentTime;
oscillatorL.stop(currentTime + 2); //stop "left" tone after 2 s
oscillatorR.stop(currentTime + 4); //stop "right" tone after 4 s

This code works as expected for the first 2 seconds – each tone is audible only on one channel. But then the left tone stops playing, and the right tone starts playing on both channels. What’s going on?

  1. When oscillatorL stops playing, it gets disconnected from mergerNode and deleted. The browser is allowed to do this because – as you recall – an OscillatorNode or AudioBufferSourceNode can only be used once, so after we call oscillatorL.stop(), oscillatorL becomes unusable.
  2. The ChannelMergerNode notices that it is left with only one channel of input, and starts outputting a mono signal.

As you can see, the most stable solution, if you want to access individual audio channels, is to always have a dummy node (or several, if you’re dealing with 5.1 or 7.1 audio) connected to your ChannelMergerNode. What’s more, it’s probably best to make sure the dummy nodes remain referenceable for as long as you need them. If you assign them to local variables in a function, and that function returns, the browser may remove those nodes from the audio graph:

function playRight()
    var oscillatorR = context.createOscillator();
    oscillatorR.frequency.value = 440;
    var mergerNode = context.createChannelMerger(2);
    var silence = context.createBufferSource();
    silence.connect(mergerNode, 0, 0);
    oscillatorR.connect(mergerNode, 0, 1);
    currentTime = context.currentTime;
    oscillatorR.stop(currentTime + 2);    


Consider what happens at the time playRight() finishes. oscillatorR won’t get removed because it’s playing (scheduled to stop in 2 seconds). But the silence node is not doing anything and when the function exits, it won’t be referenceable, so the browser might decide to get rid of it. This would of course switch the output of the ChannelMergerNode into mono mode.

It’s worth noting that the above code currently works in Chrome, but it might not in the future. The W3C spec gives browsers a lot of leeway when it comes to removing AudioNodes:

An implementation may choose any method to avoid unnecessary resource usage and unbounded memory growth of unused/finished nodes. (source)

In Firefox, You cannot modify an AudioBuffer after you’ve assigned it to AudioBufferSourceNode.buffer

The following code attempts to generate and play a 440 Hz tone over a single channel:

SAMPLE_RATE = 44100;
buffer = context.createBuffer(1, 44100*2, SAMPLE_RATE);
//create a mono 44.1 kHz buffer, 2 seconds length
bufferSource = context.createBufferSource();
bufferSource.buffer = buffer;

soundData = buffer.getChannelData(0);
for (var i = 0; i < soundData.length; i++)
  soundData[i] = Math.sin(2*Math.PI*i*440/SAMPLE_RATE);

It works in Chrome, but fails in Firefox (28) without any error message. Why? The moment you assign buffer to bufferSource.buffer, Firefox makes the buffer immutable. Further attempts to write to the buffer are ignored.

This behavior is not covered by the W3C spec (at least I couldn’t find any relevant passage), but here’s a Mozilla dev explaining it on StackOverflow.

Windows bug: AltGr key stops working after you open a “Save As” or “Open File” window

While working on the next release of my Windows app for typing foreign characters and phonetic symbols, I stumbled on a pretty serious bug that affects the use of multiple keyboard layouts on Windows Vista, Windows 7 and (to a lesser extent) Windows 8.

What the bug looks like to the end user

Suppose your default Windows keyboard layout is US English, but you also want to use other keyboard layouts occasionally – maybe you’re an American learning French or a Pole who lives in the US, but wants to write in Polish every now and then. The Language Bar in Windows allows you to set a separate layout for each window, so let’s say you fire up Microsoft Word and change your layout to Polish.

Quick note: The Polish keyboard layout, like most non-English keyboard layouts, makes use of the right-hand Alt key, also known as “AltGr”, which is short for Alternate Graving. For example, to type the letter ł (pronounced like English w), you press AltGr+L.

After typing for a while, you decide to save your document. You bring up the Save As dialog box, type up a filename, and press OK. When you start typing again, you notice that you can no longer type any AltGr characters.

What’s going on? The act of opening the default Windows “Save As” (or “Open File”) dialog disables the AltGr key. The AltGr characters are not accessible in the dialog (you cannot use them in file names). More seriously, once you close the dialog, AltGr will remain broken in your application, even though the Language Bar will report that you are using the correct layout. At first, I thought it was lying, but it’s not – technically, the layout is still in effect. It’s just that the AltGr key is no longer working; it becomes a regular Alt key.

The same happens if your default Windows layout uses AltGr and you set just one window to a non-AltGr layout such as US English. If you open the “Save As” or “Open File” dialog, the right Alt key will turn into AltGr. This means, for example, that hitting right-Alt+F will no longer bring up the File menu.

Which versions of Windows are affected?

I’ve reproduced this bug on Windows Vista, Windows 7, and Windows 8. Windows XP seems immune.

Note that the default setting in Windows 8 is to have a single keyboard layout for all applications. This bug will affect you only if you specifically go to the language settings and choose the option to enable per-app keyboard layouts.

The right way to stress-test an overclocked PC

Consider the following situation: You’ve overclocked your CPU, set the core voltage (Vcore) to some reasonable number, and then it’s time for some stress testing to make sure your rig is stable. You follow all the standard recommendations that you can find on overclocking forums: you run Prime95 for 12-24 hours, do a few hours of FurMark, and a few hours of IntelBurnTest for good measure. All the tests complete without a hitch. Congratulations! Your system is now considered stable.

But then you run Crysis or Battlefield 3 and you get random lockups and reboots. What’s going on?

The problem is that you stress-tested the CPU and GPU separately. That doesn’t guarantee that your system will be stable when both the CPU and the GPU are under load. Interestingly, loading the GPU can make your CPU unstable!

In other words, to be stable under combined CPU+GPU loads, your CPU may need a higher Vcore than it does for isolated CPU loads. That’s why the Vcore you arrived at using Prime95 or IBT may be too low to guarantee CPU stability when the GPU is under stress.

Here’s the story of how I realized it:

I had overclocked an i5 3570K to 4.3 GHz at 1.25 Vcore. The system had successfully completed the following stress tests:

  1. Prime95 Blend (all cores) for 14 hours
  2. Prime95 Small FFT (all cores) for 8.5 hours
  3. FurMark for 3 hours
  4. IntelBurnTest on Standard for 2 hours
  5. IntelBurnTest on High for 1 hour

Most overclockers would agree that these results look rock-solid. However, when I ran IntelBurnTest and FurMark simultaneously, I was shocked to see FurMark fail almost immediately. I repeated the experiment several times and the time to failure was always between 5 and 30 minutes. I started coming up with hypotheses:

Hypothesis #1: The PSU can’t supply enough power for the combined CPU+GPU load. The Corsair HX520W supplies 480 watts on the 12V line; an overclocked 3570K + HD7850 shouldn’t draw more than 300 W, but there’s also the motherboard, and the PSU is kind of old – who knows, maybe the capacitors have aged?

Refutation: After replacing the PSU with the Be Quiet! Straight Power E9 580 W (564 W on the 12 V line), the symptoms were exactly the same. It wasn’t a PSU problem!

Hypothesis #2: The increased heat production is causing the GPU, CPU, or video card VRMs to overheat.

Refutation: (1) GPU and CPU core temperatures were carefully monitored during stress testing and did not exceed 90°C. (2) During isolated CPU/GPU stress testing, CPU/GPU core temps were the same, yet there were no crashes. (3) With an open case and all fans set to maximum, the CPU+GPU test failed just as quickly. It was not a heat issue.

That left me with only one hypothesis.

Hypothesis #3: Loading the GPU is somehow causing the CPU to lose enough power to become unstable. In other words, the CPU Vcore is set high enough for isolated CPU load, but not high enough for combined CPU+GPU load. This was a novel hypothesis; I hadn’t seen it discussed in any of the overclocking resources that I had read.

Confirmation: In the words of Colonel Hans Landa, that’s a bingo! Upping the Vcore from 1.25 V to 1.275 V improved the stability by a large margin. Instead of failing after 5-30 minutes (as verified in multiple tests), IBT+FurMark failed after 108 minutes. (Notice that if the crashes had been due to overheating, the test would have failed more quickly than before, as increasing the CPU Vcore made the CPU hotter.)

Still, the system was not fully stable. In order to make it IBT+Furmark stable for several hours, I would have probably had to increase Vcore to around 1.3 V. Incidentally, this is close to the Vcore that the CPU chooses automatically for a 4.3 GHz clock speed, if you select the so-called “offset mode” instead of forcing a fixed Vcore. The lesson here would be that Intel knows what they’re doing when choosing these automatic settings.

The right way to ensure your overclock is fully stable

Run FurMark and IntelBurnTest at the same time for a few hours – at least as long as you expect to operate with combined CPU+GPU load. For example, if your typical gaming session is 3 hours, you should run IBT+FurMark for at least 3 hours. Of course more is better.

One tricky part to combined CPU+GPU stress testing is that FurMark needs some free CPU cycles in order to stress the GPU properly. If you fully load the CPU with IBT, Furmark will be bottlenecked and the GPU load will be much less than 100% – on my system it was around 75%.

The solution is to find the right settings for IntelBurnTest that will result in stressing the CPU while leaving just enough free CPU cycles to allow FurMark to get GPU load above 95%. In my case, the highest combined load was produced with IBT set to “High” and 3 threads.

Another piece of advice is to stick to “offset mode”, i.e. let the CPU set the Vcore automatically depending on the clock frequency. This may result in rather high voltages and high power draw (for example, a 3570K @ 4.3 GHz and full load has a Vcore of 1.32 V, which is higher than usually reported by overclockers at this clock speed), but at least you’re using a Vcore that has been blessed (and presumably extensively tested) by Intel.

My personal choice was to scale back my overclock to 4.1 GHz and choose offset mode. This translates to a Vcore of only 1.192 volts under load and, as far as I can tell, complete stability (IBT+Furmark 5 hours with no error) – though of course you can never be 100% sure…


Isn’t it overkill to stress-test the CPU and GPU at the same time? That depends. If you’re absolutely sure you’re never going to reach full or near-full CPU+GPU load, then I guess you can stick to standard, isolated stress testing. However, bear in mind that there are games which can place a very high load on the CPU and the GPU at the same time.

Are you saying that most overclocked systems out there are not truly stable? It would seem so. Most overclockers determine a “stable” CPU Vcore based on isolated CPU testing with something like Prime95 or IntelBurnTest. My experience shows that you need a much higher Vcore to ensure stability under combined CPU+GPU load. I suspect most OC’d systems would fail the IBT+Furmark test outlined here rather quickly; they might also crash when running certain games. Of course, my findings would be more trustworthy if someone replicated them – please post your experiences in the comments!

Why would loading the GPU make the CPU unstable? I’m no hardware engineer, but here’s a wild guess: Loading the GPU drains some of the power away from the CPU (or causes small voltage dips from time to time). Therefore, when the GPU is loaded, the CPU will need a higher Vcore to remain stable.

What was your exact setup?

  • Motherboard: Asus P8Z77-V Pro
  • CPU: Intel i5 3570K
  • CPU cooling: Noctua NH-U12P SE2, 120mm Enermax T.B. Silence fan
  • Video card: AMD Radeon HD7850 (MSI Twin Frozr III, 2 GB)
  • GPU cooling: Accelero S1 Plus, 120mm Enermax T.B. Silence fan
  • 16 GB RAM (two 8 GB sticks, Kingston HyperX)
  • SSD: Crucial M4 256 GB, HDD: Western Digital Red 2 TB (WD20EFRX)
  • Case: Fractal Design Define R4
  • Case cooling: 1 rear 140mm Fractal Design Silent Series R2 fan
  • PSUs tested: Corsair HX520W / Be Quiet! Straight Power E9 580W