Encoding
MORSE CODE
Translate text to Morse code and back. Listen to the audio with adjustable speed.
Visual
Audio Playback
15 WPM
Reference Chart
Developer Reference
Core Algorithm & Standalone Script
Standalone, zero-dependency JavaScript implementation powering this tool. Free to inspect, copy, and build upon.
const MORSE = {
'A':'.-','B':'-...','C':'-.-.','D':'-..','E':'.','F':'..-.','G':'--.',
'H':'....','I':'..','J':'.---','K':'-.-','L':'.-..','M':'--','N':'-.',
'O':'---','P':'.--.','Q':'--.-','R':'.-.','S':'...','T':'-','U':'..-',
'V':'...-','W':'.--','X':'-..-','Y':'-.--','Z':'--..',
'0':'-----','1':'.----','2':'..---','3':'...--','4':'....-',
'5':'.....','6':'-....','7':'--...','8':'---..','9':'----.',
'.':'.-.-.-',',':'--..--','?':'..--..','!':'-.-.--','/':'-..-.',
'(':'-.--.',')':'-.--.-','&':'.-...',':':'---...',';':'-.-.-.',
'=':'-...-','+':'.-.-.','−':'-....-','"':'.-..-.','\'':'.----.',
'@':'.--.-.'
};
const REVERSE = {};
for (const [k, v] of Object.entries(MORSE)) REVERSE[v] = k;
function textToMorse() {
const text = document.getElementById('textInput').value.toUpperCase();
const morse = text.split('').map(ch => {
if (ch === ' ') return '/';
return MORSE[ch] || '';
}).filter(Boolean).join(' ');
document.getElementById('morseInput').value = morse;
updateVisual(morse);
}
function morseToText() {
const morse = document.getElementById('morseInput').value.trim();
const text = morse.split(/\s*\/\s*/).map(word =>
word.split(/\s+/).map(code => REVERSE[code] || '').join('')
).join(' ');
document.getElementById('textInput').value = text;
updateVisual(morse);
}
function updateVisual(morse) {
const container = document.getElementById('visualMorse');
container.innerHTML = '';
const tokens = morse.split('');
let i = 0;
while (i < tokens.length) {
if (tokens[i] === '.') {
const el = document.createElement('span');
el.className = 'dot'; el.dataset.idx = container.children.length;
container.appendChild(el);
} else if (tokens[i] === '-') {
const el = document.createElement('span');
el.className = 'dash'; el.dataset.idx = container.children.length;
container.appendChild(el);
} else if (tokens[i] === '/') {
const el = document.createElement('span');
el.className = 'word-space';
container.appendChild(el);
} else if (tokens[i] === ' ') {
const el = document.createElement('span');
el.className = 'morse-space';
container.appendChild(el);
}
i++;
}
}
let audioCtx, playing = false, stopFlag = false;
async function playMorse() {
if (playing) return;
const morse = document.getElementById('morseInput').value.trim();
if (!morse) return;
stopFlag = false; playing = true;
document.getElementById('playBtn').textContent = 'Playing...';
audioCtx = audioCtx || new (window.AudioContext || window.webkitAudioContext)();
const wpm = parseInt(document.getElementById('speedSlider').value);
const dotLen = 1.2 / wpm;
const elements = document.querySelectorAll('#visualMorse .dot, #visualMorse .dash');
let elIdx = 0;
for (let i = 0; i < morse.length; i++) {
if (stopFlag) break;
const ch = morse[i];
if (ch === '.') {
if (elements[elIdx]) elements[elIdx].classList.add('active');
await beep(audioCtx, dotLen);
if (elements[elIdx]) elements[elIdx].classList.remove('active');
elIdx++;
await sleep(dotLen * 1000);
} else if (ch === '-') {
if (elements[elIdx]) elements[elIdx].classList.add('active');
await beep(audioCtx, dotLen * 3);
if (elements[elIdx]) elements[elIdx].classList.remove('active');
elIdx++;
await sleep(dotLen * 1000);
} else if (ch === ' ') {
await sleep(dotLen * 3000);
} else if (ch === '/') {
await sleep(dotLen * 7000);
}
}
playing = false;
document.getElementById('playBtn').textContent = 'Play';
}
function stopMorse() {
stopFlag = true; playing = false;
document.getElementById('playBtn').textContent = 'Play';
document.querySelectorAll('#visualMorse .dot, #visualMorse .dash').forEach(el => el.classList.remove('active'));
}
function beep(ctx, duration) {
return new Promise(resolve => {
const osc = ctx.createOscillator();
const gain = ctx.createGain();
osc.frequency.value = 600;
osc.type = 'sine';
gain.gain.value = 0.3;
osc.connect(gain);
gain.connect(ctx.destination);
osc.start();
osc.stop(ctx.currentTime + duration);
osc.onended = resolve;
});
}
function sleep(ms) { return new Promise(r => setTimeout(r, ms)); }
function copyText(id) {
navigator.clipboard.writeText(document.getElementById(id).value).catch(() => {});
}
document.getElementById('speedSlider').oninput = function() {
document.getElementById('speedVal').textContent = this.value + ' WPM';
};
// Reference grid
const grid = document.getElementById('refGrid');
for (const [ch, code] of Object.entries(MORSE)) {
const item = document.createElement('div');
item.className = 'ref-item';
item.innerHTML = `<div class="ref-char">${ch}</div><div class="ref-code">${code}</div>`;
grid.appendChild(item);
}