How to easily generate a Beep (notification) sound with JavaScript
Tags: javascript, webdev
Published: 2021-11-19 17:22:37

I know, I know, sounds on web applications? Yuck!. Don't even consider using beeps or any other sound on serious business applications unless it's strictly necessary, most of the times a simple notification element in the window like a tiny modal will do the trick.

Most of the users will condemn your application if you decide to implement sounds without any special reason at all. It should only emit any sound to notify users about messages, warnings or errors of your app and definitely, this shouldn't be an obligatory feature. The user should know that something is happening on the background when he isn't paying attention to the application (it's really useful on daily trading-like applications when the stop-loss or take-profit is triggered), so be sure to implement as well a way to disable the sounds in your application for your user whenever he wants to.

In case that you need to implement a notification-like sound in your application occasionally without too much hassle, I will explain to you 2 ways to do it in the web nowadays using JavaScript.

## A. Using the AudioContext of the browser

The first option to simulate a Beep sound is to generate audio with plain JavaScript thanks to the [AudioContext](https://developer.mozilla.org/en-US/docs/Web/API/AudioContext) class of the browser (through the Web Audio API). This interface represents an audio-processing graph built from audio modules linked together, each represented by an AudioNode.

According to the Mozilla Developer Network, an audio context controls both the creation of the nodes it contains and the execution of the audio processing or decoding. You need to create an AudioContext before you do anything else, as everything happens inside a context. It's recommended to create one AudioContext and reuse it instead of initializing a new one each time, and it's OK to use a single AudioContext for several different audio sources and pipeline concurrently.

In the case of a "beep" implementation, the following function will allow you to generate this sound easily with just a few lines of code:

```javascript
// The browser will limit the number of concurrent audio contexts
// So be sure to re-use them whenever you can
const myAudioContext = new AudioContext();

/**
 * Helper function to emit a beep sound in the browser using the Web Audio API.
 * 
 * @param {number} duration - The duration of the beep sound in milliseconds.
 * @param {number} frequency - The frequency of the beep sound.
 * @param {number} volume - The volume of the beep sound.
 * 
 * @returns {Promise} - A promise that resolves when the beep sound is finished.
 */
function beep(duration, frequency, volume){
    return new Promise((resolve, reject) => {
        // Set default duration if not provided
        duration = duration || 200;
        frequency = frequency || 440;
        volume = volume || 100;

        try{
            let oscillatorNode = myAudioContext.createOscillator();
            let gainNode = myAudioContext.createGain();
            oscillatorNode.connect(gainNode);

            // Set the oscillator frequency in hertz
            oscillatorNode.frequency.value = frequency;

            // Set the type of oscillator
            oscillatorNode.type= "square";
            gainNode.connect(myAudioContext.destination);

            // Set the gain to the volume
            gainNode.gain.value = volume * 0.01;

            // Start audio with the desired duration
            oscillatorNode.start(myAudioContext.currentTime);
            oscillatorNode.stop(myAudioContext.currentTime + duration * 0.001);

            // Resolve the promise when the sound is finished
            oscillatorNode.onended = () => {
                resolve();
            };
        }catch(error){
            reject(error);
        }
    });
}
```

You can use it like this, providing as first argument the volume of the sound, as second argument the frequency of the oscillator to simulate a note and finally the duration of the sound in milliseconds:

```js
// Simple beep
beep(
    // Set the duration to 0.2 second (200 milliseconds)
    200,
    // Set the frequency of the note to A4 (440 Hz)
    440,
    // Set the volume of the beep to 100%
    100
);
```

You can generate either a low- or high-pitched sound adjusting the frequency of the function to the note that you want (you can use the following Audio Frequency Chart):

![Audio Frecuency Chart](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a02l0b3axzdw7hzoir79.jpg)

You may as well create chainable "beeps" as the implementation uses JavaScript Promises:

```js
function delay(duration) {
    return new Promise((resolve) => {
        setTimeout(() => resolve(), duration);
    });
}

// Emit a set of beeps
// to simulate a Ready, Set, Go! 
// It goes like: 
// (low pitch) Beep 
// (1 second silence) 
// (low pitch) Beep
// (1 second silence)
// (low pitch) Beep
// (1 second silence)
// (higher pitch) Beep!!!

Promise.resolve()
  .then(() => beep())
  .then(() => delay(1000))
  .then(() => beep())
  .then(() => delay(1000))
  .then(() => beep())
  .then(() => delay(1000))
  .then(() => beep(200, 870));
```

For a detailed explanation about the frequency and pitch in AudioContext of JavaScript, [I recommend you to read this article from Teropa](https://teropa.info/blog/2016/08/10/frequency-and-pitch.html). You can as well change the type of wave of the audio node:

*   sine
*   square
*   sawtooth
*   triangle

Another cool tutorial you can read about the wave forms [is the Waveforms article by Josh W. Comeau](https://pudding.cool/2018/02/waveforms/).

You can test this beep sound in the following Fiddle:

{% jsfiddle https://jsfiddle.net/ourcodeworld/7v3p4Le6/10/ %}

## B. Playing a custom audio file

The other option that you have, in case that your "beep" sound needs to be way more sophisticated, like a "You're Just a Chicken by Tommy Wiseau in The Room":

{% youtube hF_-5pZ_kxY %}

You may do it as well, feel free to do whatever you want in your software!. But seriously, depending of the needs of your applications, the beep sounds that you can create with the Web Audio API may not be the best ones.

If you have already an MP3 or WAV audio file with the notification sound, you can use plain JavaScript to play it as a beep sound. The following function represents the mentioned logic using your own custom audio file:

```js
/**
 * Helper function to emit a beep sound in the browser using an Audio File.
 * 
 * @param {number} volume - The volume of the beep sound.
 * 
 * @returns {Promise} - A promise that resolves when the beep sound is finished.
 */
function beep(volume){
    return new Promise((resolve, reject) => {
        volume = volume || 100;

        try{
            // You're in charge of providing a valid AudioFile that can be reached by your web app
            let soundSource = "https://www.w3schools.com/html/horse.mp3";
            let sound = new Audio(soundSource);

            // Set volume
            sound.volume = volume / 100;

            sound.onended = () => {
                resolve();
            };

            sound.play();
        }catch(error){
            reject(error);
        }
    });
}
```

This implementation allows you to create a chain as well just like the beep implementation with the Web Audio API:

```js
function delay(duration) {
    return new Promise((resolve) => {
        setTimeout(() => resolve(), duration);
    });
}

// Emit 3 horse neighs subsequently!
Promise.resolve()
  .then(() => beep())
  .then(() => delay(1000))
  .then(() => beep())
  .then(() => delay(1000))
  .then(() => beep())
  .then(() => delay(1000))
  .then(() => beep());
```

You can test this custom beep sound in the following fiddle:

{% jsfiddle https://jsfiddle.net/ourcodeworld/vus8jkrn/2/ %}

Thank you for your time to read this article. Don't forget to visit [Our Code World](https://ourcodeworld.com) for more awesome content about software development.