Back to Software

Morse Code Practice Tool

25 Mar 2016
Progress: Complete

A simple but versatile browser-based tool for practising CW / Morse Code, written using the HTML5 Audio API. Learn More


Start with just K and M, when you can copy with >90% accuracy add R, and so on.

List the characters from which the message will be generated.





Copy the message into this textbox as it's playing to be given a score. If you hit stop (or press enter) the score will be based on how much of the message played.

Code Reference

(Click to play)


There are many Morse Code training tools, but most of them require installing or are platform dependent. I really liked an online tool by AA9PW, which generates audio at the server and streams it to you. This has a number of advantages - totally platform independent, and it will work with basically any browser ever. But when I looked at it the other day, it was no longer working (this may have since been fixed). This encouraged me to make my own tool, along the same lines as AA9PW's and adding some of my own ideas.

Using the HTML5 Audio API has a number of advantages - far less bandwidth used, you can change the speed/pitch as it's playing, and so long as the page is still hosted it will always work, since no processing is done at the server. The disadvantage is you need a fairly recent browser to use it, as the Audio API was only introduced a few years ago.

Receiving (copying) Morse Code is generally much harder than sending it. I had previously made a few quickfire Morse tools, but they were very limited. I've also built both software and hardware Morse Code USB keyboards.


Slowing down morse code evenly can make it quite difficult to hear the 'shape' of the characters. In the Farnsworth method, characters are sent at a reasonable speed, so they sound correct, but with spaces between them so the overall word speed is reduced. As you learn, gradually increase the word speed until it matches the character speed.

In the Koch method, you should start at full speed but with a limited character set. When you can copy just two characters with 90% accuracy, add one more. When you can copy those three with 90% accuracy, add another, and so on. There is a recommended learning order which I've put into the dropdown box. These are chosen at random but with weighted probabilities so the most recent two characters are most likely to appear.

Using the custom character set, characters are chosen with equal probabilities.

When you feel ready to move on to real blocks of text, be prepared to slow down again. Morse Code is designed around the letter frequencies in the English language, with E and T (the most frequent letters) having the shortest codes. So copying real text will be faster than random characters at the same character speed.

The speeds are determined by the word PARIS being 50 dot-lengths long. See the wikipedia page on Morse Code for more info about the timing.


Although you can paste an arbitrary block of text into the source box, it is pretty difficult to do so without reading it. Using RSS is a simple way of fetching meaningful blocks of text that you won't already be familiar with.

Despite my ambition to have this entirely Javascript-based, the same-origin policy means some server involvement is unavoidable. The RSS feeds are bounced off a PHP file on my server so that an AJAX request can be made without breaking this policy. This is possibly open to abuse so I've only given it a fixed set of available feeds, but if there is an RSS feed you want added to the list feel free to message me.

Wikipedia actually has an entire API for fetching its content, and it supports JSONP which would avoid the need to bounce it off my server. However, it isn't really intended for this sort of thing, and grabbing the featured article's description is rather complicated through this method. Easier just to use the RSS feeds in the same way. The feeds are located here and here.


This remembers your most recent settings by storing them in HTML5's localStorage. This is similar to a cookie, but doesn't get sent to the server. You can erase it by clicking here.


If you're following the Koch method it's pretty handy to know when you have hit 90% accuracy. To calculate this accuracy is not entirely straight forward.

The simplest method is to find the Levenshtein distance between the two strings. This gives you the number of single-character edits required to get from one string to the other. Divide that by the length of the first string to get a percentage error, and subtract that from 100% to get the accuracy.

In order to do this I had to implement the Levenshtein distance calculation in javascript. This has probably already been done but I like doing this sort of thing.

/* Find the Levenshtein distance between two strings
 * implemented by mitxela as described on wikipedia
function Levenshtein(s, t){
  var m=s.length, n=t.length, d=[], i,j, cost=0;

  function min(a,b,c){
    if (a<=b&&a<=c) return a;
    if (b<=a&&b<=c) return b;
    return c;
  for (i=0;i<=m;i++) d[i] = [i];
  for(j=1;j<=n;j++) {
    d[0][j] = j;
    for (i=1;i<=m;i++){
      cost = (s.charAt(i-1) == t.charAt(j-1))?0:1;
      d[i][j] = min(d[i-1][j  ] + 1,     // deletion
                    d[i  ][j-1] + 1,     // insertion
                    d[i-1][j-1] + cost)  // substitution
  return d[m][n]
Note that you can actually use s[i] instead of s.charAt(i) with strings in Javascript now (it was added fairly recently). However charAt makes it more explicit to a human reader that it's a string. Since some of these strings could be quite long, I tried to optimize it for speed. I made a couple of jsperf test cases and found that defining our own min function gave a 74% speedup in Chrome but had no effect on Firefox. I originally had a funky bit of implicit type conversion for the cost calculation, but again jsperf showed that the ternary or a conditional would be faster in Chrome, while in Firefox it had no effect. Probably to do with just-in-time compiling. That was all on Firefox 45 and Chrome 49.

The function calculates a matrix of the Levenshtein distances between all the prefixes of the first string and all the prefixes of the second. The last value in this array is the shortest distance between the strings. Instead of returning this value, we can plot the entire matrix.

Enter two strings and hit the button

In this table, I've traced back one possible path in yellow whose distance is equal to the levenshtein distance. Moving horizontally or vertically corresponds to adding or subtracting a character. Moving diagonally represents either no change or replacing a character (you can tell by if the distance value changes).

If the difference between strings is ambiguous, especially if mistakes are adjacent to each other, there can be multiple possible paths through the table which have the same distance. There is no way of knowing which way is the 'correct' path when it comes to what you typed in the morse code box. However, if you are practising near your comfort zone most of the mistakes should be in isolation and unambiguous.

Understanding this, I decided to offer individual scores on a per-character basis so you know which characters are your weakest, and having done that, I added highlighting to the output box to show you exactly where they took place. This is possibly over the top but I really like over-analysing my performance.


Lastly if you just came to this page to turn a block of text into written dots and dashes, or vice versa, you can do that here.