This is the story of me learning a whole lot in an attempt to impress my father.
For his part, he goes around like he’s seen every damn thing under the sun, so it was a reach goal.
I asked my dad what he would like me to build for him for Father’s Day/his birthday (same day this year!) and he said, “Oh you don’t have to build me anything.” And when I pressed, then he dithered a little bit about what was possible. At one point he even told me I couldn’t build anything that hadn’t already been built. Ok, not the point, Dad.
Eventually, we landed on Morse code.
His number one hobby is ham radio, and he’s been trying to practice more Morse code to pass some test or just be better at listening in to people’s conversations or something. I guess it’s impressive to be good at code. 😉
So the idea was I would create a page that would:
- Allow him to select a number of Morse code characters to be played.
- Play that out loud (in dashes and dots) without printing it to the screen so he could test his listening skills
- Allow him to then reveal the answer on the screen so he could compare his answer with the solution.
My original idea for this app was to find an API that spits out Morse code audio and use that to randomize the sounds that my dad wanted. In retrospect, that actually might have been easier than what I ended up doing. I had done some preliminary research and found a Morse code API, so I thought I was pretty much ready to build it out, except the audio files were base64-encoded. The code was “encoded.” Haha, ha, sigh.
I put Dad Code on the shelf.
Then I started streaming on Twitch and my software training program went on break. This dovetailed into a perfect storm of me having the time to build this project in a different way, and I had help from developers on stream who were bored at work.
And it came just in time, because suddenly it was the week before Father’s Day.
If you’ve been scanning up to this point, now we’re going to get into the code. If you want to follow along with the code, here’s the link to the repo. I’ll also post some in-line snippets below. GitHub
The devs I was talking to about the site design recommended ditching the Morse API and building a client-side library to play the sound. I’d get to use the Audio() function in the web API, exciting!
I did a little research on how audio worked in the web API and got to work.
I started by connecting a
getMorse() function to an event listener. I built an input to take in a number. The value would tell my
getMorse() function how many elements of Morse code to play. My dad wanted to hear between 1 and 10, and I made the input have a max of 15 in case he wanted more.
Next, I had to make my own Morse code dictionary. I found a source where they had already written out the code as an object, AMAZING! I copied that into my main.js and set it up as a dictionary constant.
Next, because my dad wanted a random selection of symbols to play, I added a randomizer loop to return a random list of keys from the dictionary based on the value provided by the user.
- I needed to play the sound for each dash and dot in each symbol at a steady rate.
- I needed to add a space between symbols so that they were intelligible.
So the next step was to add the sounds to the Audio class as constants so I could play them with the
playAudio() method later.
I had to set up a nested for loop because the
'i' array is the one created by the click event, which just adds the random keys to the “dashAndDots” array. This creates an array that looks like this:
[‘...’, ‘..--’, ’.---’, ‘--..-’]
The program cannot read the individual dashes and dots in that array so I needed a second loop (
‘j’) to play the dashes and dots. That allows the program to take each index of the
dashAndDots array and split it into a tiny array that can be turned into sound.
Just to review:
- we started with a number,
- which randomly produced a list of symbols from our dictionary,
- which are encoded into dash and dot strings in an array,
- and then we are splitting them into individual dots and dashes
Audio()API know which sound to play when the loop happens.
So when I set this up, it looked like this:
Two notes about this:
- There were a few testing rounds before we got to this stage. First we didn’t have the
setTimeout()in there and all the sounds were playing at once. That was awful. We did a lot of playing around with the timing of the space between the
'j'loop elements, so it was possible to hear the space between the dots and dashes.
- It was not originally one giant async/await function. That was a change that really helped the program come together. Nothing was really working consistently until we wrapped the whole function with the async tag and made the
The last issue with the playback was that even though the timing between the dots and dashes was working well, I didn’t have any way to add space between the
'i' elements. Someone suggested adding another
setTimeout(), but I ended up hard-coding the solution.
Since the space between symbols needed to be consistent, I decided to add an asterisk to the end to each of the keys in my Morse code dictionary.
Then I added a two millisecond blank sound to the Audio class as the constant
Finally, I added it as a third condition to my
'j' loop and it works as intended!
The sound was working, and even though the free dash and dot sounds I found online were not pretty, I had completed the first two tasks of the app. On to the third!
Making the symbols appear in the DOM was pretty straight forward. I knew I could make the
playArray print to the console, because I had tested how the symbols were coming into the
getMorse() function from the dictionary. Now I just needed to print them in the DOM, but make them hidden to the user at first.
I used a regular old
<h3> tag as a landing zone for my array and then I used a querySelector and innerText to get it there:
Finally, I added a button in the HTML and connected it to a click event which triggers
All the pieces were in place and it was ready for Dad!
My dad loved this app. When I revealed it to him on Father’s Day, he did not remember asking for it or what his criteria were. This was a little frustrating to me because I created it based specifically on his specs, but he’s a 75-year-old retiree who lives in Florida and is constantly surrounding himself with vaguely radioactive antennae. WHAT DID I EXPECT?
Anyway, the main concern that I had about the app was that the timing would be incorrect. In Morse code the space between letters should be the same as three dots. Since I didn’t really know how long my dot sound was, I was really having to estimate how long that was in milliseconds.
I got an update from my dad a few days after Father’s Day that the timing was great and it would allow him to get up to the pace of 15 words a minute, which was his current goal. Great! Mission accomplished.
And yet, when I posted about the app on Twitter, I got mixed reviews, for one very important reason: iOS Safari browsers have audio autoplay turned off by default. In theory (and practicality) this is a great feature, but for my Morse code app, it meant that the
getMorse() function only played the first sound and then stopped.
Yikes, sorry to the 57% of mobile users (allegedly, that's the % of folx with iPhones) who might be trying to learn Morse code.
So it works great on desktop browsers and Android, if you want to give it a try. Check it out: https://morsecodetest.netlify.app/
Here’s to a day when all browsers on all devices can play dashes and dots to their heart's content! I’ve opened an issue for the iOS auto-play on GitHub, if anyone wants to submit a PR, because I have been thwarted on finding a fix thus far!
For now, try out Dad Code and remember that even dads can benefit from a bespoke birthday app now and then.
Dad (circa 2007)