
Dieses ist eine frühe BETA Version welche dazu gedacht ist das Projekt weiter zu verbessern.
Ich wollte schon länger mal das Projekt umsetzen, welches die Maus als Morsetaste missbraucht, um einen Morsetutor im Browser zu verwirklichen.
Dabei kann die Maus selbst als Paddle Taste missbraucht werden, oder man lötet einen Anschluss an die beiden Tasten an um sie direkt mit einer Morsetaste zu verbinden.
Das Grundgerüst für das Script steht, aber ich bin nicht zu Frieden. Deswegen mein Aufruf an alle Javascript Entwickler
ICH BRAUCHE MAL DEINE HILFE !
Bedienung: Stelle die gewünschte Sendegeschwindigkeit und Tonhöhe ein.
Bewege den Mauszeiger in den hellgrünen Kasten – Fertig – jetzt kannst du die beiden Maustasten als Paddle benutzen.
Inhaltsverzeichnis
Testbereich
Klicken und halten Sie die Maustasten
Technik
Zum Testen braucht ihr erst mal nichts außer der am PC angeschlossenen Maus. Diese einfach in das grüne Feld ziehen und schon kann gemorst werden. Wer seine Paddle Taste anschließen will kann das sehr einfach und kostengünstig.
Nehmt eine (alte) Maus. Öffnet Sie und lötet einfach an den beiden Tastern die Drähte an, welche Ihr an die Paddle Taste anschließt.
Es können mehrere Mäuse parallel betrieben werden. Nur die Maus mit welcher ihr morsen wollt muss in das grüne Feld bewegt werden.

Code
<!DOCTYPE html> <html lang="de"> <head> <meta charset="UTF-8"> <title>Morse-Eingabe mit WPM-Einstellung</title> <style> body { font-family: Arial, sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; } #input-area { width: 300px; height: 100px; border: 2px solid #000; margin: 20px 0; display: flex; justify-content: center; align-items: center; font-size: 18px; cursor: pointer; padding: 15px; background: lightgreen; } input[type=range]:focus { outline: none; } input[type=range] { appearance: none; width: 100%; border-radius: 6px; height: 12px; border: 1px solid blue; background-color: cornflowerblue; } #visual-feedback { width: 50px; height: 50px; border-radius: 50%; background-color: #ccc; margin: 20px auto; } #output { font-size: 18px; margin-top: 20px; } .slider-container { margin-bottom: 10px; } .slider-container label { display: inline-block; width: 200px; } .slider-container input { width: 300px; } .slider-value { display: inline-block; width: 50px; text-align: right; } </style> </head> <body> <h1>Morse-Eingabe mit WPM-Einstellung</h1> <div class="slider-container"> <label for="wpm">Geschwindigkeit (WPM):</label> <input type="range" id="wpm" min="5" max="30" value="15"> <span class="slider-value" id="wpm-value">15</span> </div> <div class="slider-container"> <label for="tone-frequency">Tonfrequenz (Hz):</label> <input type="range" id="tone-frequency" min="400" max="1200" value="800"> <span class="slider-value" id="tone-frequency-value">800</span> </div> <br><br> <div id="input-area" tabindex="0">Klicken und halten Sie die Maustasten</div> <div id="visual-feedback"></div> <div id="output"></div> <script> const inputArea = document.getElementById('input-area'); const output = document.getElementById('output'); const visualFeedback = document.getElementById('visual-feedback'); let morseCode = ''; let currentChar = ''; let plainText = ''; let isPressed = false; let repeatInterval; let pauseTimeout; let lastInputTime = 0; // Einstellungen const wpmSlider = document.getElementById('wpm'); const toneFrequencySlider = document.getElementById('tone-frequency'); const morseDict = { '.-': '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 audioContext = new (window.AudioContext || window.webkitAudioContext)(); function playTone(duration, frequency) { const oscillator = audioContext.createOscillator(); const gainNode = audioContext.createGain(); oscillator.type = 'sine'; oscillator.frequency.setValueAtTime(frequency, audioContext.currentTime); gainNode.gain.setValueAtTime(1, audioContext.currentTime); gainNode.gain.exponentialRampToValueAtTime(0.001, audioContext.currentTime + duration); oscillator.connect(gainNode); gainNode.connect(audioContext.destination); oscillator.start(); oscillator.stop(audioContext.currentTime + duration); } function playTone(duration,frequency) { // GainNodes erstellen const gainNode = audioContext.createGain(); const gainNodePlay = audioContext.createGain(); const gainNodeLimiter = audioContext.createGain(); // Oszillator erstellen const oscillator = audioContext.createOscillator(); oscillator.type = 'sine'; // Typ des Oszillators (z.B. 'sine', 'square', 'triangle', 'sawtooth') oscillator.frequency.setValueAtTime(frequency, audioContext.currentTime); // Frequenz in Hz // BiquadFilter erstellen und konfigurieren const biquadFilter = audioContext.createBiquadFilter(); biquadFilter.type = 'lowpass'; biquadFilter.frequency.setValueAtTime(500, audioContext.currentTime); biquadFilter.Q.setValueAtTime(10, audioContext.currentTime); // Beispiel-Q-Wert // Verbindungen herstellen oscillator.connect(gainNode); gainNode.connect(gainNodeLimiter); gainNodeLimiter.connect(biquadFilter); biquadFilter.connect(gainNodePlay); gainNodePlay.connect(audioContext.destination); // Gain-Werte setzen gainNode.gain.value = 1; // Volle Lautstärke gainNodePlay.gain.value = 1; // Volle Lautstärke gainNodeLimiter.gain.value = 0.55; // Limiter-Gain // Oszillator starten und nach der angegebenen Dauer stoppen oscillator.start(); oscillator.stop(audioContext.currentTime + duration); // Gain reduzieren, um den Ton langsam zu beenden gainNode.gain.exponentialRampToValueAtTime(0.001, audioContext.currentTime + duration); } function getDotDuration() { // PARIS-Methode: 50 Punkte pro "PARIS" bei Standard-WPM return 60000 / (50 * parseInt(wpmSlider.value)); } function addMorseCode(code) { const now = Date.now(); const dotDuration = getDotDuration(); const toneFrequency = parseInt(toneFrequencySlider.value); // Überprüfen Sie die Pause seit der letzten Eingabe if (currentChar && (now - lastInputTime > dotDuration * 3)) { // Pause zwischen Zeichen (3 Punkte lang) evaluateChar(); } else if (morseCode && (now - lastInputTime > dotDuration * 7)) { // Pause zwischen Wörtern (7 Punkte lang) evaluateChar(); morseCode += ' / '; plainText += ' '; } currentChar += code; if (code === '.') { playTone(dotDuration / 1000, toneFrequency); visualFeedback.style.backgroundColor = 'green'; } else if (code === '-') { playTone((dotDuration * 3) / 1000, toneFrequency); visualFeedback.style.backgroundColor = 'blue'; } updateOutput(); lastInputTime = now; resetPauseTimer(); } function resetPauseTimer() { clearTimeout(pauseTimeout); pauseTimeout = setTimeout(evaluateChar, getDotDuration() * 3); } function evaluateChar() { if (currentChar) { const letter = morseDict[currentChar] || '?'; morseCode += currentChar + ' '; plainText += letter; currentChar = ''; updateOutput(); } } function startRepeating(code) { isPressed = true; addMorseCode(code); const repeatDuration = code === '.' ? getDotDuration() : getDotDuration() * 3; repeatInterval = setInterval(() => { if (isPressed) { addMorseCode(code); } }, repeatDuration); } function stopRepeating() { isPressed = false; clearInterval(repeatInterval); visualFeedback.style.backgroundColor = '#ccc'; resetPauseTimer(); } inputArea.addEventListener('mousedown', (e) => { e.preventDefault(); if (e.button === 0) { startRepeating('.'); } else if (e.button === 2) { startRepeating('-'); } }); inputArea.addEventListener('mouseup', stopRepeating); inputArea.addEventListener('mouseleave', stopRepeating); inputArea.addEventListener('contextmenu', (e) => { e.preventDefault(); }); document.addEventListener('keydown', (e) => { if (e.code === 'Space') { evaluateChar(); morseCode += '/ '; plainText += ' '; updateOutput(); } }); function updateOutput() { output.textContent = `Aktuell: ${currentChar}\nMorse: ${morseCode}\nKlartext: ${plainText}`; } // Slider-Ereignisse function updateSliderValue(slider, valueSpan) { valueSpan.textContent = slider.value; } wpmSlider.addEventListener('input', () => { updateSliderValue(wpmSlider, document.getElementById('wpm-value')); if (isPressed) { stopRepeating(); startRepeating(currentChar.slice(-1)); } }); toneFrequencySlider.addEventListener('input', () => updateSliderValue(toneFrequencySlider, document.getElementById('tone-frequency-value'))); // Initialisierung der Slider-Werte updateSliderValue(wpmSlider, document.getElementById('wpm-value')); updateSliderValue(toneFrequencySlider, document.getElementById('tone-frequency-value')); </script> </body> </html>
Das hat ChatGPT draus gemacht, du kannst es gern testen und weiterentwickeln 🙂
Morse-Eingabe mit WPM-Einstellung
body { font-family: Arial, sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; }
#input-area {
width: 300px;
height: 100px;
border: 2px solid #000;
margin: 20px 0;
display: flex;
justify-content: center;
align-items: center;
font-size: 18px;
cursor: pointer;
padding: 15px;
background: lightgreen;
}
input[type=range]:focus {
outline: none;
}
input[type=range] {
appearance: none;
width: 100%;
border-radius: 6px;
height: 12px;
border: 1px solid blue;
background-color: cornflowerblue;
}
#visual-feedback {
width: 50px;
height: 50px;
border-radius: 50%;
background-color: #ccc;
margin: 20px auto;
}
#output { font-size: 18px; margin-top: 20px; }
.slider-container { margin-bottom: 10px; }
.slider-container label { display: inline-block; width: 200px; }
.slider-container input { width: 300px; }
.slider-value { display: inline-block; width: 50px; text-align: right; }
Morse-Eingabe mit WPM-Einstellung
Geschwindigkeit (WPM):
15
Tonfrequenz (Hz):
800
Klicken und halten Sie die Maustasten
const inputArea = document.getElementById(‚input-area‘);
const output = document.getElementById(‚output‘);
const visualFeedback = document.getElementById(‚visual-feedback‘);
let morseCode = “;
let currentChar = “;
let plainText = “;
let isPressed = false;
let repeatInterval;
let pauseTimeout;
let lastInputTime = 0;
// Einstellungen
const wpmSlider = document.getElementById(‚wpm‘);
const toneFrequencySlider = document.getElementById(‚tone-frequency‘);
const morseDict = {
‚.-‚: ‚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 audioContext = new (window.AudioContext || window.webkitAudioContext)();
function playTone(duration, frequency) {
const gainNode = audioContext.createGain();
const oscillator = audioContext.createOscillator();
oscillator.type = ’sine‘;
oscillator.frequency.setValueAtTime(frequency, audioContext.currentTime);
oscillator.connect(gainNode);
gainNode.connect(audioContext.destination);
gainNode.gain.setValueAtTime(1, audioContext.currentTime);
gainNode.gain.exponentialRampToValueAtTime(0.001, audioContext.currentTime + duration);
oscillator.start();
oscillator.stop(audioContext.currentTime + duration);
}
function getDotDuration() {
return 60000 / (50 * parseInt(wpmSlider.value));
}
function addMorseCode(code) {
const now = Date.now();
const dotDuration = getDotDuration();
const toneFrequency = parseInt(toneFrequencySlider.value);
if (currentChar && (now – lastInputTime > dotDuration * 3)) {
evaluateChar();
} else if (morseCode && (now – lastInputTime > dotDuration * 7)) {
evaluateChar();
morseCode += ‚ / ‚;
plainText += ‚ ‚;
}
currentChar += code;
if (code === ‚.‘) {
playTone(dotDuration / 1000, toneFrequency);
visualFeedback.style.backgroundColor = ‚green‘;
} else if (code === ‚-‚) {
playTone((dotDuration * 3) / 1000, toneFrequency);
visualFeedback.style.backgroundColor = ‚blue‘;
}
updateOutput();
lastInputTime = now;
resetPauseTimer();
}
function resetPauseTimer() {
clearTimeout(pauseTimeout);
pauseTimeout = setTimeout(evaluateChar, getDotDuration() * 3);
}
function evaluateChar() {
if (currentChar) {
const letter = morseDict[currentChar] || ‚?‘;
morseCode += currentChar + ‚ ‚;
plainText += letter;
currentChar = “;
updateOutput();
}
}
function startRepeating(code) {
isPressed = true;
addMorseCode(code);
const repeatDuration = code === ‚.‘ ? getDotDuration() : getDotDuration() * 3;
repeatInterval = setInterval(() => {
if (isPressed) {
addMorseCode(code);
}
}, repeatDuration);
}
function stopRepeating() {
isPressed = false;
clearInterval(repeatInterval);
visualFeedback.style.backgroundColor = ‚#ccc‘;
resetPauseTimer();
}
inputArea.addEventListener(‚mousedown‘, (e) => {
e.preventDefault();
if (e.button === 0) {
startRepeating(‚.‘);
} else if (e.button === 2) {
startRepeating(‚-‚);
}
});
inputArea.addEventListener(‚mouseup‘, stopRepeating);
inputArea.addEventListener(‚mouseleave‘, stopRepeating);
inputArea.addEventListener(‚contextmenu‘, (e) => {
e.preventDefault();
});
document.addEventListener(‚keydown‘, (e) => {
if (e.code === ‚Space‘) {
evaluateChar();
morseCode += ‚/ ‚;
plainText += ‚ ‚;
updateOutput();
}
});
function updateOutput() {
output.textContent = `Aktuell: ${currentChar}\nMorse: ${morseCode}\nKlartext: ${plainText}`;
}
function updateSliderValue(slider, valueSpan) {
valueSpan.textContent = slider.value;
}
wpmSlider.addEventListener(‚input‘, () => {
updateSliderValue(wpmSlider, document.getElementById(‚wpm-value‘));
if (isPressed) {
stopRepeating();
startRepeating(currentChar.slice(-1));
}
});
toneFrequencySlider.addEventListener(‚input‘, () => updateSliderValue(toneFrequencySlider, document.getElementById(‚tone-frequency-value‘)));
updateSliderValue(wpmSlider, document.getElementById(‚wpm-value‘));
updateSliderValue(toneFrequencySlider, document.getElementById(‚tone-frequency-value‘));
</
Viele ♥♥♥-liche Grüße aus Sachsen !!
Hallo Funkamateure.
Kommt jetzt: Morsen mit dem Bananenstecker?
Dr. Walter Schau, DL3LH