The KIRUPA orange logo! A stylized orange made to look like a glass of orange juice! Tutorials Coding Exercises Videos Books

FORUMS

Customize Theme


Color

Background


Done

Search

Playing a Randomly Generated Sound

by kirupa   |    filed under Coding Exercises

[an error occurred while processing this directive]

Use the Web Audio API to randomly generate a cool sounding sound where the waveform and frequency vary with each play.

The Web Audio API gives us a bunch of low-level building blocks for working with sound. Best of all, these blocks can all be set and manipulated using JavaScript. This opens the door for some creative experiments inside our websites and apps...such as what we'll cover in this article: playing a sound that is randomly generated.

Onwards!

The Example

What we are going to create will look (and sound) a bit like the following where clicking or tapping on the crystal orb will play a sound:

Each time you click on the orb, the sound that will play will be slightly different. In the next section, we'll see exactly how to pull off this effect.

Creating the (Random) Sound

Our sound is going to be generated. This is an important detail, for we aren't going to be taking an existing sound file and manipulating the sound properties inside it. Instead, what we are going to do is start from scratch and have our computer generate a sound for us with a random waveform and frequency. The audio routing graph will look approximately as follows:

Turning this audio graph into code will look as follows:

let context = null;
let waveforms = ["sine", "square", "sawtooth", "triangle"];

function playRandomSound() {
  if (context === null) {
    context = new AudioContext();
  }

  let oscillatorNode = context.createOscillator();
  let gainNode = context.createGain();

  oscillatorNode.type = waveforms[Math.floor(Math.random() * waveforms.length)];

  let frequency = (100 + Math.random() * 10000).toFixed(2);
  oscillatorNode.frequency.value = frequency;

  console.log(`Playing a sound with a ${oscillatorNode.type} waveform at ${frequency}Hz!`);

  gainNode.gain.exponentialRampToValueAtTime(0.00001, context.currentTime + 1);
  oscillatorNode.connect(gainNode);
  gainNode.connect(context.destination);
  oscillatorNode.start(0);
}

Take a few moments and walk through what this code is doing. The bulk of the code here is the bare minimum we need to create an audio context, the oscillator sound source, and the properties of our oscillator such as the waveform type and frequency.

The way we choose a random waveform is by having an array of waveforms that we randomly choose one of sine, square, sawtooth, or triangle from and assign it to our oscillator node's type property:

let waveforms = ["sine", "square", "sawtooth", "triangle"];

function playRandomSound() {
  .
  .
  .
  oscillatorNode.type = waveforms[Math.floor(Math.random() * waveforms.length)];
  .
  .
  .
}

Frequency is a bit more straightforward. We just pick a random frequency value between 100 and 10100 and assign it to our oscillator node's frequency property:

let frequency = (100 + Math.random() * 10000).toFixed(2);
oscillatorNode.frequency.value = frequency;

The rest of the code is almost boilerplate at this point. We connect our oscillator node to our gain node, and the gain is the last stop in our audio graph and it connects to our output: context.destination. That what we've written happens to describe the audio graph illustration we saw earlier is totally by design.

The Full Example

For the full working example that we saw earlier, copy and paste the following HTML, CSS, and JavaScript into a blank page and preview it in your browser:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Play a Randomly Generated Sound</title>
  <style>
    body {
      display: grid;
      place-items: center;
    }

    .container {
      width: 500px;
      height: 350px;
      background-color: #EEE;
      border: 5px solid #333;
      overflow: hidden;
      display: grid;
      place-items: center;
      align-items: center;
      align-content: center;
      justify-content: center;
    }

    .container:hover {
      background-color: #ffe656;
    }

    .container img {
      width: 100px;
      border-radius: 50%;
      background-color: #FFF;
      padding: 40px;
      transform: rotate(15deg);
      transition: transform .2s cubic-bezier(0.175, 0.885, 0.32, 1.275);
      cursor: pointer;
    }
    .container img:hover {
      transform: rotate(-20deg) scale(1.2);
    }
    .container img:active {
      transform: rotate(15deg) scale(.9);
    }

    
  </style>
</head>

<body>
  <div class="container">
    <img id="soundItem" src="https://www.kirupa.com/icon/1f52e.svg" />
  </div>

  <script>

    let soundElement = document.querySelector("#soundItem");
    soundElement.addEventListener("click", playRandomSound, false);

    let context = null;
    let waveforms = ["sine", "square", "sawtooth", "triangle"];

    function playRandomSound() {
      if (context === null) {
        context = new AudioContext();
      }

      let oscillatorNode = context.createOscillator();
      let gainNode = context.createGain();

      oscillatorNode.type = waveforms[Math.floor(Math.random() * waveforms.length)];

      let frequency = (100 + Math.random() * 10000).toFixed(2);
      oscillatorNode.frequency.value = frequency;

      console.log(`Playing a sound with a ${oscillatorNode.type} waveform at ${frequency}Hz!`);

      gainNode.gain.exponentialRampToValueAtTime(0.00001, context.currentTime + 1);
      oscillatorNode.connect(gainNode);
      gainNode.connect(context.destination);
      oscillatorNode.start(0);
    }
  </script>
</body>

</html>

The core JavaScript around using the Web Audio API to generate a should appear unchanged, but you get to see how we tie a click/tap on the visual into a sound playing in this example.

Conclusion

A little bit of randomness goes far in creating a fun experience for our users. Sound usually doesn't fall into this category. For a variety of good reasons, as covered in Sound UX, or Why Our Web Pages Barely Make a Peep, we typically prefer our day-to-day browsing to be a silent affair. In those situations where a sound is appropriate, adding a bit of randomness to how the sound plays can be a nice touch.

The KIRUPA Newsletter

Thought provoking content that lives at the intersection of design 🎨, development 🤖, and business 💰.

Serving you freshly baked content since 1998!
Killer hosting by (mt) mediatemple

Twitter Youtube Facebook Pinterest Instagram Github