Saturday, June 25, 2016

A Simple Web-Based Morse Code Keyer Using HTML and Javascript

For some years I've wanted to write a little app that allowed me to use a couple of keys on my computer keyboard (or the mouse buttons on my mouse) as a dit/dah keyer, but to make it available for other hams, I wanted it in a universal language.

Whereas Windows users have VisualBASIC, Linux and Mac users do not, and whereas Linux/Mac users have shell scripting, such is pretty limited in the Windows world.

But everybody has a web browser.

It's only recently that the HTML5 web standard has made my goal fairly trivial.

So if you'd like to code a bit of web-page to play some computer-keyboard Morse, come along....

Of course, you'd probably like to know what the result will be, before you follow these instructions, so click here to give the keyer a tryout.

If you've never written a web page, let's start out with that. I won't deal with all the exacting web standards, but I will do some of the basics. Open a text editor of some sort that produces plain ASCII text (like Notepad on Windows, or TextEdit on Mac, or Gedit on Linux - be aware that the Mac TextEdit has a few gotchas; you have to remember to convert to Plain Text before you save your document, and to manually add the .html extension, and to turn off Smart Quotes and Smart Dashes in the Edit / Substitutions menu), and type in the following:


Hello, World!

Save the document as plain text, giving it a name like MorseKeyer.html. If you have a web presence, you can place this file on your web server, but if you don't, just save it to your hard drive, perhaps on your Desktop. Then open a web-browser, and either browse to your web server, or do a File / Open and browse to the document. When you open it, you should see the words "Hello, World!" in your web-browser. Yea! You have a web page. But it's really not up to web standards, so let's add the basic minimum it should have. Edit your document so that it now looks like the following:


<html>
<head>
</head>
<body>
"Hello, World!"
</body>
</html>

This still doesn't exactly meet proper web standards, but it's good enough for our purposes. Save the document (as plain text, remember, with an .html extension), and then refresh (or reload) your web document/page. You shouldn't see any visible change.


Now, in the <head> and </head> lines, add this little bit:


<head>
        <title>My Morse Code Keyer</title>
</head>

Save the document and reload it in your web-browser, and you should see the Window or Tab title change to say "My Morse Code Keyer".

The <header> section is for web-page housekeeping stuff; the <body> section is where most of your web-page code goes.

In the <body> section, replace the "Hello, World!" with something more meaningful, like:


<body>
<h1>Welcome to Kent's Web-based Morse Code Keyer!</h1>
<p>Press the down arrow key for a dah, and the right arrow key for a dit.</p>
</body>

(Later, you can choose to use different keys than what's convenient on this MacBook keyboard on which I'm currently typing, and change the message accordingly.)

The <h1> tells your web-browser to display the enclosed text as a header, using the built-in definition for the first-lever of headers. An <h2> would use the second-level of built-in header definitions, etc.

The <p> defines its enclosed text as being a paragraph. Although not strictly required for your web page to work, it's good to get into the habit of using this tag around your paragraphs.

Save your document, reload it in your web-browser, and you should see the appropriate changes.

We're about a third of the way to our goal. We'll do the next two-thirds of our project in a Javascript script. We need a little more programming power than HTML by itself can handle. But since Javascript is built into modern web browsers, we won't have to download/install anything extra. And despite the similarity in names, Javascript has no relation to Java; they are two separate programming tools.

Now, for part two: watching for a keypress. (The third and final part will be to generate dit and dah tones based on which key is pressed.)

In the <body> section, just above the closing </body> tag, add this section:


<script>
addEventListener("keydown", alert("Hey! We're listening for a keypress!"), false);

function dealWithKeyboard(event) {
    alert(event.keyCode + " has been pressed");
}

</script

This code tells the web-browser that this is a Javascript script (more properly the <script> line should be <script type="text/javascript">).

The script command, "addEventListener" tells the browser to listen for an event, in this case the event of a key being pressed down (you could also listen for a keyup, or a mouse button, or several other options), and when it hears one, to go to the function named "dealWithKeyboardEvent", which we build ourselves. We could call the function pretty much anything we wanted, as long as it matches both here in the addEventListener command and in the function definition. For example, we could call it "playMorseTones" if we wanted. The "false" operator at the end of the command just tells the command to not "capture" further input.

All the function does at this point is display a pop-up window that tells us what key we've pressed. The "alert" command is what generates this pop-up window. (If you Mac TextEdit users forgot to turn off SmartQuotes, those double-quotes won't be real double-quotes, and your command won't work.)

The "event" parameter holds the keycode of the key that is pressed, and passes it to our function. The ".keyCode" is a built-in function that returns the keycode's value from that "event" parameter. If you wanted to, you could use a different name for this parameter, such as "e" or "theKeyThatWasPressed", but "theKeyThatWasPressed.keyCode" is more tedious to type than "e.keyCode", which itself is not quite as self-explanatory as "event.keyCode".

Now would be a good time to try pressing a few keys to figure out what you want to use for your Dit and your Dah, and make a note of the keycodes generated by those keys.

At this point, your entire "MorseKeyer.html" document should look something like this:


<!DOCtype html>
<html>
<head>
        <title>My Morse Code Keyer</title>
</head>
<body>
<h1>Welcome to Kent's Web-based Morse Code Keyer!</h1>
<p>Press the down arrow key for a dah, and the right arrow key for a dit.</p>

<script>
addEventListener("keydown", alert("Hey! We're listening for a keypress!"), false);

function dealWithKeyboard(event) {
    alert(event.keyCode + " has been pressed");
}
</script>

</body>
</html>

Now that we've captured the keypresses, we have to do different things based on which key is pressed. We'll handle this with a "switch" statement, which is a form of an If-Then statement you may remember from your high school programming class.

We no longer need the pop-up box telling us what key we've pressed, so we can delete it, or just comment it out. Let's comment it out, so that it won't be treated like a computer programming instruction anymore, but just a comment. This way we can always uncomment it later if we need to test other keys.

Just add a couple of forward slashes somewhere at the front of the line, like so:


//     alert(event.keyCode + " has been pressed");

Now if you reload your page and press a key, you won't see anything happen, because our function no longer does anything.

So let's give it something to do.

Just after the newly-commented alert line, add these lines:


switch(event.keyCode) {
  case 39:
    alert("You pressed the Right Arrow");
    break;
  case 40:
    alert("You pressed the Down Arrow");
    break;
}

Now if you reload your web page, and press one of your two chosen keys, you should see a pop-up window telling you which key you pressed. All other keys are ignored.

I don't like "magic numbers" in my program, so rather than test the case of "40" and "39", I'm going to use variables with meaningful names here, so that the lines become


case _Dit:
and

case _Dah:

Then earlier in the script, just below the opening <script> tag, I'll define these two variables. The entire <script> section now looks like this:


<script>   
        var _Dah = 40; // 40 = down arrow
        var _Dit = 39; // 39 = right arrow

        addEventListener("keydown", dealWithKeyboard, true);

        function dealWithKeyboard(event) {
//          alert(event.keyCode + " has been pressed");
            switch(event.keyCode) {
                case _Dit:
                   alert("You pressed the Dit key");
                   break;
                 case _Dah:
                   alert("You pressed the Dah key");
                   break;
             }
          }
</script>

Now if you want to change your keys for Dit and Dah, just change those variables.

Now we're two-thirds of the way toward our goal. All that is left is to generate the tones.

For the last and final section of our program, we'll just replace the pop-up alert boxes with sound-generation code.

So comment out the Dit alert lines, and add the code following:


//                    alert("You pressed the Dit key");

                    // Create the audio context
                    var context = new AudioContext();
                    var oscillator = context.createOscillator();
                    oscillator.frequency.value = 220;
 
                    // Connect the oscillator to our speakers
                    oscillator.connect(context.destination);

                    // Start the oscillator now
                    oscillator.start(context.currentTime);

                    // Stop the oscillator after "DitLength" seconds from now
                    oscillator.stop(context.currentTime + 1);

Now if you save the code and reload your web page, and then press the Dit key, you should hear a tone of 220 hertz. (The Dah key does not yet work.)

Again, there are a couple of "magic numbers"in this code, so let's replace them with variables.

Replace the "220" with "_Freq", and the "1" with "_DitLength", and then declare these two (and a Dah) variables:


        var _Dah = 40; // 40 = down arrow
        var _Dit = 39; // 39 = right arrow
        var _Freq = 220; // Tone frequency
        var _DitLength = 1; // Length of the Dit
        var _DahLength = _DitLength * 3; // A Dah is usually three times a Dit

and replace those magic numbers with their variable names:

Now add the Dah key handler: replace the alert-Dah line with:


//                    alert("You pressed the Dah key");

                    // Create the audio context
                    var context = new AudioContext();
                    var oscillator = context.createOscillator();
                    oscillator.frequency.value = _Freq;
 
                    // Connect the oscillator to our speakers
                    oscillator.connect(context.destination);

                    // Start the oscillator now
                    oscillator.start(context.currentTime);

                    // Stop the oscillator after "DahLength" seconds from now
                    oscillator.stop(context.currentTime + _DahLength);

If you run your code now, you should have a working keyer. I find that a _DitLength of 0.6 works better than 1. You can also tinker with the frequency.

With a little more programming, you could even have these values input by the user. And you may hear a bit of click in your tones; I do on my MacBook. But this is a simple program; I'll leave it to you to work out all the kinks.

Just in case I've left something out, or left something a little vague, here's the entire .html document for reference:


<!doctype html>
<!-- From http://code.tutsplus.com/tutorials/the-web-audio-api-what-is-it--cms-23735 -->
<html>
    <head>
        <title>My Morse Code Keyer</title>
    </head>
    <body>
        <h1>Welcome to the Web Audio API</h1>
    <p>Press Down Arrow for Dit, Right Arrow for Dah.</p>

    <script>   
        // Duration of the dits and dahs
        var _DitLength = 0.06;
        var _DahLength = _DitLength * 3;
        var _Freq = 440;
        var _Dah = 40; // 40 = down arrow
        var _Dit = 39; // 39 = right arrow

        addEventListener("keydown", dealWithKeyboard, true);

        function dealWithKeyboard(event) {
//            alert(event.keyCode + " has been pressed");
            switch(event.keyCode) {
                    case _Dit:
//                    alert("down");

                    // Create the audio context
                    var context = new AudioContext();
                    var oscillator = context.createOscillator();
                    oscillator.frequency.value = _Freq;
 
                    // Connect the oscillator to our speakers
                    oscillator.connect(context.destination);

                    // Start the oscillator now
                    oscillator.start(context.currentTime);

                    // Stop the oscillator after "DitLength" seconds from now
                    oscillator.stop(context.currentTime + _DitLength);

                    break;

                case _Dah:
//                    alert("down");

                    // Create the audio context
                    var context = new AudioContext();
                    var oscillator = context.createOscillator();
                    oscillator.frequency.value = _Freq;
 
                    // Connect the oscillator to our speakers
                    oscillator.connect(context.destination);

                    // Start the oscillator now
                    oscillator.start(context.currentTime);

                    // Stop the oscillator after "DahLength" seconds from now
                    oscillator.stop(context.currentTime + _DahLength);

                    break;
            }

        }
    </script>
    </body>
</html>

Have fun playing with your new web-based Morse code keyer!

Originally published at https://kentwest.blogspot.com/2016/06/a-simple-web-based-morse-code-keyer.html

2 comments:

Nathaniel said...

Kent,
What a fun simple project and tutorial!
First, I must confess that I've not yet had time to build it myself using your instructions. I have been able to run some real-world browser compatibility tests and I've compiled the results below.

It works perfectly so far in both Firefox for Windows as well as Firefox on OS X.

On my Windows 10 Chrome and Edge (the IE replacement), after I play a total of 6 tones (dit and/or dah), it stops working but no errors or anything. A reload of the page and it works again for 6 more tones. I've gotten the same result in Chrome on Mac.

On my Safari for Mac 9.0.1 it simply doesn't work at all and the same for IE 11 on Windows 10.



Kent West said...

Thanks for the comment. And for the usage testing.

I find that it doesn't work on Konqueror on Debian GNU/Linux, or on the "Web Browser" (a very generic name, seems to me, I believe from the Gnome Project) on the same platform, and on Chromium (parent/cousin of Chrome), it also only plays six beeps.

Firefox works fine. Interesting....