Tutorials Books Videos Forums

Change the theme! Search!
Rambo ftw!

Customize Theme


Color

Background


Done

Table of Contents

Playing a Randomly Generated Sound

by kirupa   |    filed under Coding Exercises

[an error occurred while processing this directive]

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 you'll explore in this fun coding exercise.

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. Pretty cool, right?

Onwards!

Starting Point

The easiest way is to fork the following Codepen pen and start adding your modifications:

See the Pen Coding Exercises Start by Kirupa Chinnathambi (@kirupa) on CodePen.

You may want to open the pen in a new window if you want more space to code your solution or bring up the Console to see the output.

If you prefer developing locally in your favorite code editor (like Visual Studio Code), create a new HTML document and copy/paste the following boilerplate/starting content into it:

<!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>Coding Exercises Start</title>
</head>
<body>
  <script>
    
  </script>
</body>
</html>

The HTML you see here is just the bare minimum content needed to help us get our web page up and running. The rest of the content is what you will add entirely on your own!

Getting Your Badge

Once you have completed this challenge, you have earned the awesome bragworthy privilege of adding the following badge to your collection:

To claim it, head over to the forums and respond in the Playing a Randomly Generated Sound topic. Be sure to include a link to your solution or insert a copy of your HTML/CSS/JS in the body of the message:

Once you have created your topic, Kirupa will give you a virtual high-five and ensure this badge is added to your list of assigned badges.

Stuck? Need Help?

We want to make this a fun learning activity. If you are stuck and need help, please ask on the forums. Please explain your problem in detail and one of the many helpful forum members will help you out.

One Possible Solution

If you want to see one way of solving this, check out Kirupa's video and brief article below:

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.

Just a final word before we wrap up. If you have a question and/or want to be part of a friendly, collaborative community of over 220k other developers like yourself, post on the forums for a quick response!

Kirupa's signature!

The KIRUPA Newsletter

Thought provoking content that lives at the intersection of design 🎨, development 🤖, and business 💰 - delivered weekly to over a bazillion subscribers!

SUBSCRIBE NOW

Creating engaging and entertaining content for designers and developers since 1998.

Follow:

Popular

Loose Ends

:: Copyright KIRUPA 2024 //--