Random tone generator using Web Audio API This is a random frequency audio generator made with the Web Audio API by Mozilla. The end-goal is an audio frequency trainer: the website plays an audio frequency and the user tries to guess which frequency it is. The frequencies will be limited to those found on 31 band graphical equalisers, and I'll include multiple difficulties.

This is my first step: the generator for the 'easy' difficulty (4 frequencies).

// create web audio api context
var audioCtx = new AudioContext();

// create oscillator (tone) and gain (volume) node
var tone = audioCtx.createOscillator();
var volume = audioCtx.createGain();

// create array of frequency values
var frequencies = ["100", "400", "1600", "6300"];
// pick a random frequency
var frequency = frequencies[Math.floor(Math.random() * frequencies.length)];

// set oscillator type (sine, square, sawtooth or triangle)
tone.type = 'sine';
// set oscillator frequency in Hz
tone.frequency.value = frequency;
// set gain volume (above 1 will clip)
volume.gain.value = 0.5;

// set tone destination to go through volume
tone.connect(volume);
// set volume destination to output
volume.connect(audioCtx.destination);

// start oscillator x seconds after timestamp
tone.start(1);
// stop oscillator x seconds after timestamp
tone.stop(4);


Don't mind the excessive commentation, I've only just started using JavaScript and don't trust myself to remember what every line of code does after a few days.
Reusable code

You code is not very flexible, and the frequencies somewhat arbitrary and spanning a large 6 octaves. 

You can encapsulate the code in a function that creates an object you use to play the random tone as needed, allowing you to add features as you need.

Note friendly

Consider using standard musical notes rather than random frequencies. 

To get the frequency of a note you use the expression 

freq = 2 ** (note / 12) * 440;


where note is the number of semi tones from A4 440Hz. So to find C4 which is 3 notes up  2 ** (3 / 12) * 440;

To get the closest note to a frequency you use 

note = Math.round(Math.log2(freq / 440) * 12);


Basic example

The example creates that object randomNotes and plays musical notes rather than random tones.



const randomNotes= (() =&gt; {
    
    /* Create the objects needed to play a tone */
    const audio = new AudioContext();
    const volume = audio.createGain(); 
    volume.gain.value = 0.5;
    volume.connect(audio.destination);
    
    /* Function to calculate frequency of a note starting at A4 and stepping semi tones*/    
    const freq = note =&gt; 2 ** (note / 12) * 440; // 440 is the frequency of A4
    const randomNote = () =&gt; notes[Math.random() * notes.length | 0]; // the bitwise Or does the same as Math.floor
    const notes = [-25, -2, 22, 46]; // Close to your 100, 400, 1600 and 6300

    /* Create and return the object that is the randomNote */    
    return {
        play(startIn = 1, playFor = 4) { // startIn and playFor is time in seconds
            const now = audio.currentTime;
            const oscillator = audio.createOscillator();
            oscillator.type = "sine";
            oscillator.connect(volume);            
            oscillator.frequency.value = freq(randomNote());
            oscillator.start(now + startIn);  
            oscillator.stop(now + startIn + playFor);  
        }
    };
})();




Then to play a random note just call 

randomNotes.play();


or playing two tones

randomNotes.play();
setTimeout(randomNotes.play, 6000);


or overlapping tones 

randomNotes.play();
setTimeout(randomNotes.play, 3000); // starts half way through the first


Reusing the code

The Advantages of using this approch is that it becomes very easy to extend the functionality. Say you want to add volume control, or be able to add notes to the random list, just add a setter to the object returned,

// from above snippet
/* Create and return the object that is the randomNote */    
return {
    set volume(value) { volume.gain.value = value },
    set note(value) { notes.push(value) }

    // continue on from above snippet
    play(startIn = 1, playFor = 4) { ...


You can then set the volume in a more abstract way and add notes to the random set with ease.

randomNotes.note = 12; // Add note A5 to the random set
randomNotes.volume = 0.8; // Increase the volume


More sound.

You will notice that there is a new oscillator created each time, and that the audio context's current time continues to tick. It is much more efficient to create one oscillator and change its frequency as needed and using timers to change the volume to start stop the sound. An oscillator can not restart after it has been stopped. 

But this is not the best either as the audio context can chew some serious power and CPU even when not playing sound (On some audio hardware). Ideally you suspend the audio context and resume it as needed, in conjunction to using the gain control to start and stop the oscillator, as the audio context can sometimes be a little slow when suspending .

Personally I would create this type of thing using an audio buffer and just write the waveform directly to the buffer then play it.