DEV Community

Cover image for Public Solving: Creating a song with JavaScript
Chris Bongers
Chris Bongers

Posted on • Originally published at daily-dev-tips.com

Public Solving: Creating a song with JavaScript

Today's puzzle is really cool, I enjoyed every step of it, and the outcome is super fun!

The elves have provided us with a piece of paper with letters on them. They say it should be some kind of music.

It's up to us to find and create sound waves for each letter and see what song they send to us.

You can find the complete puzzle here.

If you can't wait, try out the outcome of today's puzzle on this CodePen. (Hint: turn up your volume)

Thinking about the solution

This challenge has multiple parts to it.

In the first part, we have to build a metronome function. This function will execute a function every x time.

I plan to use an interval to solve this.

This would work best as we have a separate function that should stop whatever happens.

The second part consists of finding the right frequency for a note, for which we will use a simple lookup table.

And then, we should play the melody based on a string.
I'll split the string into pieces and call our metronome function to play and remove the letter it played from the string.

We will play the whole song and then stop the metronome function.

Alright, let's get to it.

Building a JavaScript music player

As mentioned, we'll have to work on the metronome function first.

Let's start with the stopMetronome function. This function should simply erase the current interval that's looping.

export const stopMetronome = () => {
  clearInterval(metronomeInterval);
};
Enter fullscreen mode Exit fullscreen mode

As for the start function, we should start by clearing any existing intervals.

clearInterval(metronomeInterval);
Enter fullscreen mode Exit fullscreen mode

Then we need to convert the bpm property into a millisecond interval.
For this, we can use the following formula.

const interval = (60 / bpm) * 1000;
Enter fullscreen mode Exit fullscreen mode

And then we can start our interval.
Each loop should execute the function that's being passed to it.

metronomeInterval = setInterval(() => fn(), interval);
Enter fullscreen mode Exit fullscreen mode

And that's it, we created a metronome function.

We then have to make a lookup table to find the frequencies for each letter.
I've decided to make a simple lookup table.

const noteToFrequency = {
  C: 261.6,
  D: 293.7,
  E: 329.6,
  F: 349.2,
  G: 392.0,
  A: 440.0,
  B: 493.9,
};
Enter fullscreen mode Exit fullscreen mode

And as for the function that should get the frequency for a letter, it's as easy as this:

export const getFrequency = (note) => noteToFrequency[note];
Enter fullscreen mode Exit fullscreen mode

Then we can start on the stopMelody function. This should simply call the stopMetronome function.

export const stopMelody = () => stopMetronome();
Enter fullscreen mode Exit fullscreen mode

Now on to the exciting part, playing the melody.
The first thing we need to do is split the string into an array for each character.

const notes = input.split('');
Enter fullscreen mode Exit fullscreen mode

Then we can call our metronome function and pass the bpm and a new function we'll call play.

startMetronome(bpm, () => play(notes));
Enter fullscreen mode Exit fullscreen mode

This play function is where we will actually play the audio and modifications of the notes array.

We evaluate if the array is empty because then we have to stop playing.

if (!notes.length) stopMelody();
Enter fullscreen mode Exit fullscreen mode

Then we need to retrieve the first letter in the array, which we'll be playing.

const note = notes[0];
Enter fullscreen mode Exit fullscreen mode

This could be a letter or an empty space.
Only if it's a letter should we play it.

if (note !== ' ') {
    playNote(getFrequency(note));
}
Enter fullscreen mode Exit fullscreen mode

And then, we can simply remove the first element from this array.

notes.shift();
Enter fullscreen mode Exit fullscreen mode

That's it!
We build our own custom music player in JavaScript.

Let's test it to see if we succeeded.

Running the test cases

I enjoyed doing this assignment and would love to hear what you would do differently.

Thank you for reading, and let's connect!

Thank you for reading my blog. Feel free to subscribe to my email newsletter and connect on Facebook or Twitter

Top comments (2)

Collapse
 
lexlohr profile image
Alex Lohr

The getFrequency function is obsolete. Another solution would be to iterate over the nodes in a for loop in an async function and await a setInterval after each note; break if stopped is set to true (or use an AbortSignal).

Collapse
 
dailydevtips1 profile image
Chris Bongers

Ah yes, it's just copied from the puzzle where is was a separate function to solve.

Indeed would also be a very nice solution.
Althought with sound and setInterval you might enter some weird stuff from what i've read.