<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Kal Oliver</title>
    <description>The latest articles on DEV Community by Kal Oliver (@kal_oliver).</description>
    <link>https://dev.to/kal_oliver</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4129876%2F6b7e2b07-53e2-41b8-b1c0-303a1d93fb9a.png</url>
      <title>DEV Community: Kal Oliver</title>
      <link>https://dev.to/kal_oliver</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/kal_oliver"/>
    <language>en</language>
    <item>
      <title>I Built a Morse Code Translator in the Browser With Plain JavaScript</title>
      <dc:creator>Kal Oliver</dc:creator>
      <pubDate>Thu, 17 Sep 2026 12:32:48 +0000</pubDate>
      <link>https://dev.to/kal_oliver/i-built-a-morse-code-translator-in-the-browser-with-plain-javascript-cif</link>
      <guid>https://dev.to/kal_oliver/i-built-a-morse-code-translator-in-the-browser-with-plain-javascript-cif</guid>
      <description>&lt;h2&gt;
  
  
  I Built a &lt;a href="https://the-morse-code-translator.com/" rel="noopener noreferrer"&gt;Morse Code Translator&lt;/a&gt; in the Browser With Plain JavaScript
&lt;/h2&gt;

&lt;p&gt;I've always found Morse code fascinating — a whole alphabet built from two symbols. So I did what developers do when they're curious about something: I built a tool for it. No frameworks, no build step, just JavaScript and the Web Audio API. Here's how the core of it works.&lt;/p&gt;

&lt;h2&gt;
  
  
  The data is the easy part
&lt;/h2&gt;

&lt;p&gt;Morse code is just a lookup table. Every character maps to a string of dots and dashes:&lt;br&gt;
const MORSE = {&lt;br&gt;
  A: '.-',   B: '-...', C: '-.-.', D: '-..',  E: '.',&lt;br&gt;
  F: '..-.', G: '--.',  H: '....', I: '..',   J: '.---',&lt;br&gt;
  K: '-.-',  L: '.-..', M: '--',   N: '-.',   O: '---',&lt;br&gt;
  P: '.--.', Q: '--.-', R: '.-.',  S: '...',  T: '-',&lt;br&gt;
  U: '..-',  V: '...-', W: '.--',  X: '-..-', Y: '-.--',&lt;br&gt;
  Z: '--..',&lt;br&gt;
  0: '-----', 1: '.----', 2: '..---', 3: '...--', 4: '....-',&lt;br&gt;
  5: '.....', 6: '-....', 7: '--...', 8: '---..', 9: '----.'&lt;br&gt;
};&lt;/p&gt;

&lt;h2&gt;
  
  
  Encoding: text → Morse
&lt;/h2&gt;

&lt;p&gt;Split the input into characters, map each one, and use a / to mark word breaks:&lt;br&gt;
function textToMorse(text) {&lt;br&gt;
  return text.toUpperCase().trim().split('').map(ch =&amp;gt; {&lt;br&gt;
    if (ch === ' ') return '/';&lt;br&gt;
    return MORSE[ch] || '';&lt;br&gt;
  }).filter(Boolean).join(' ');&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;textToMorse('SOS'); // "... --- ..."&lt;/p&gt;

&lt;h2&gt;
  
  
  Decoding: Morse → text
&lt;/h2&gt;

&lt;p&gt;The nice trick here is you don't need a second table — just invert the first one:&lt;/p&gt;

&lt;p&gt;const REVERSE = Object.fromEntries(&lt;br&gt;
  Object.entries(MORSE).map(([char, code]) =&amp;gt; [code, char])&lt;br&gt;
);&lt;/p&gt;

&lt;p&gt;function morseToText(morse) {&lt;br&gt;
  return morse.trim().split(' ')&lt;br&gt;
    .map(code =&amp;gt; code === '/' ? ' ' : (REVERSE[code] || ''))&lt;br&gt;
    .join('');&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;morseToText('... --- ...'); // "SOS"&lt;/p&gt;

&lt;h2&gt;
  
  
  The fun part: making it beep
&lt;/h2&gt;

&lt;p&gt;Text on a screen is fine, but Morse is meant to be heard. The Web Audio API lets you generate a clean tone without loading a single audio file:&lt;/p&gt;

&lt;p&gt;const ctx = new (window.AudioContext || window.webkitAudioContext)();&lt;/p&gt;

&lt;p&gt;function beep(duration) {&lt;br&gt;
  return new Promise(resolve =&amp;gt; {&lt;br&gt;
    const osc = ctx.createOscillator();&lt;br&gt;
    const gain = ctx.createGain();&lt;br&gt;
    osc.type = 'sine';&lt;br&gt;
    osc.frequency.value = 600;        // 600 Hz is the classic CW tone&lt;br&gt;
    osc.connect(gain);&lt;br&gt;
    gain.connect(ctx.destination);&lt;br&gt;
    osc.start();&lt;br&gt;
    setTimeout(() =&amp;gt; { osc.stop(); resolve(); }, duration);&lt;br&gt;
  });&lt;br&gt;
}&lt;/p&gt;

&lt;h2&gt;
  
  
  Timing is what makes it sound right
&lt;/h2&gt;

&lt;p&gt;This is the detail most beginners miss. Morse isn't just short and long beeps — the silences are part of the language. The standard ratios are:&lt;/p&gt;

&lt;p&gt;const DOT  = 80;        // one unit (ms)&lt;br&gt;
const DASH = DOT * 3;   // a dash is 3 units&lt;br&gt;
const SYMBOL_GAP = DOT; // gap between dots/dashes in a letter&lt;br&gt;
const LETTER_GAP = DOT * 3;&lt;br&gt;
const WORD_GAP   = DOT * 7;&lt;/p&gt;

&lt;p&gt;Then playing a sequence is just walking the string and awaiting the right duration:&lt;br&gt;
async function play(morse) {&lt;br&gt;
  for (const symbol of morse) {&lt;br&gt;
    if (symbol === '.') { await beep(DOT); }&lt;br&gt;
    else if (symbol === '-') { await beep(DASH); }&lt;br&gt;
    else if (symbol === ' ') { await wait(LETTER_GAP); }&lt;br&gt;
    else if (symbol === '/') { await wait(WORD_GAP); }&lt;br&gt;
    await wait(SYMBOL_GAP);&lt;br&gt;
  }&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;const wait = ms =&amp;gt; new Promise(r =&amp;gt; setTimeout(r, ms));&lt;/p&gt;

&lt;p&gt;Get those gaps wrong and it sounds like noise. Get them right and suddenly it sounds like the real thing.&lt;/p&gt;

&lt;h2&gt;
  
  
  From snippet to real tool
&lt;/h2&gt;

&lt;p&gt;The version above is the skeleton. The finished project adds things I didn't expect to need: adjustable speed (WPM), a light that flashes in sync with the audio for visual learners, Farnsworth timing to make it easier to learn by ear, and even a decoder that reads Morse out of an image. You can play with the live version here — Morse Code Translator — no sign-up, it just runs in the browser.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I learned
&lt;/h2&gt;

&lt;p&gt;The encoding is trivial; the experience is where all the work hides. Timing, audio, and accessibility turned a 20-line snippet into a real tool. If you're looking for a small weekend project that touches data structures, the Web Audio API, and async timing all at once, a Morse translator is a great one.&lt;/p&gt;

&lt;p&gt;Would love feedback from other devs — what would you add? A real telegraph-key input mode is next on my list.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>beginners</category>
      <category>showdev</category>
    </item>
  </channel>
</rss>
