Morse Code Tutor in pure Javascript
This post describes a little side project (See also the github page ) I did lately and a bit of how it works and some of the decisions I made along the way.
A while ago I encountered a description of the Koch Method for learning Morse code. With the Koch method, the Morse code student learns one letter at a time. The teacher or in this case a program sends a random or semi-random sequence based on a small subset of the alphabet at full speed (usually around 15 words per minute) until they are able to copy that subset of the alphabet reliably. Then the teacher adds a new letter into the mix. Teacher and student repeat this process until the student has mastered the entire desired alphabet.
There are some existing programs to help with this, but I figured that it was a simple enough task that it should be possible to do it in a webpage today, provided that there are audio APIs out there to generate a tone and play it on the user’s speakers. So with the goal of trying to the bare minimum needed to be a Morse code tutor, I got started. Also, this was before I had heard of LCWO (Learn CW Online) which is a much more complete web-based Morse tutor.
To avoid the constant churn of Javascript frameworks and the learning curve associated with them, I decided to do all the UI without one. Technically, it’s not too difficult: Just tag the elements you want to manipulate with an HTML id attribute, use document.getElementById
to find the Dom elements, and do whatever manipulation, reading or writing you need to do. The downside is that it’s difficult to avoid mixing logic and display layout information. For example, each letter in the alphabet had a method that generated HTML to display on alphabet selection page. (Sure, one could use mixins to decouple the alphabet from the UI at least somewhat somewhat, but the example is still valid for now. I ultimately will likely do that if I keep developing this project.) I also end up with some boilerplate methods to handle mapping data from the UI to the code and vice versa and some potential for conflicting initialization values. My little experience with Angular JS suggests that these frameworks give this sort of UI to Code mapping for much less boilerplate.
For generating tones, I used the Javascript Web Audio APIs which, while not completely bleating edge is a somewhat new addition to most web browsers. There’s no Ineternet Explorer support to speak of according to MDN, though modern versions of Edge are supported and I haven’t tested it myself. Here I somewhat regret marrying Morse code timings to this API. I might have more than one type of output in the future, not just audio via the Web Audio API and I’m likely to have more than one type of Morse encoding logic.
As far as the Audio APIs themselves are concerned, they are a lot more extensive than one might expect. To quote the MDN documentation:
The Web Audio API involves handling audio operations inside an audio context, and has been designed to allow modular routing. Basic audio operations are performed with audio nodes, which are linked together to form an audio routing graph. Several sources — with different types of channel layout — are supported even within a single context. This modular design provides the flexibility to create complex audio functions with dynamic effects.
I only used a small fraction of that capability for this project: I created an oscillator tone, then used a GainNode to turn the tone off and on to send Morse code over the user’s speakers. While this works well enough at reasonable speeds, I do still have some issues with timing of very high speed Morse code. Parts of characters tend to get cut off when the speed is too fast. Since the speeds in question are probably far too fast for most people learning Morse code, I avoided most of this issue by setting reasonable limits on speed.
And this is where debugging this becomes somewhat difficult. I don’t know Morse code at the moment and I certainly don’t know it at 40+ words per minute. Among most amateur radio enthusiasts, 30 WPM is considered pretty fast. 40+ WPM is quite rare on the air. Since I don’t know what it should sound like, I sometimes have trouble knowing if my code is in fact sending Morse code correctly. Short of recording its output and going through the audio waveform this would be difficult to test end-to-end. Refactoring the sender to separate timing logic from the WebAudio API might be worth it just to make it unit testable.
When I sat down and tried to use my creation, I found that the UX is quite frankly kindof terrible compared to LCWO and other more interactive ways of learning Morse code. Since I’m currently expecting the user to write down what they copy and score themselves, there’s limited interactivity. In my defense, I was shooting for the bare minimum to be usable, I wasn’t necessarily trying to be the best designed Morse code tutor around. If I continue to work on this project, I may try to think up a more interactive way to for users to progress through the process of learning Morse code.
Despite some of the issues and shortcomings this little project has, I did get it to a usable state and I learned some things and had some fun along the way.