Générateur de Code Morse réalisé en HTML

Traducteur de Code Morse — F4HXN

Traducteur de code Morse

Saisissez votre texte : la traduction s’affiche en direct, et vous pouvez l’écouter à la vitesse de votre choix.

Vitesse 20 WPM
Tonalité 600 Hz

Chronologie standard PARIS : point = 1 temps, trait = 3 temps, espace entre signes = 1 temps, entre lettres = 3 temps, entre mots = 7 temps. À 20 WPM, un temps dure 60 ms.

Le code source


<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="author" content="F4HXN">
<meta name="description" content="Traducteur de code Morse avec lecture audio a vitesse reglable (WPM), ponctuation et chiffres - F4HXN">
<meta name="copyright" content="CC0 1.0 Universel">
<title>Traducteur de Code Morse — F4HXN</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;700&display=swap" rel="stylesheet">
<style>
  :root{
    --bleu:#0076a5;
    --bleu-clair:#298ad4;
    --encre:#0f1e28;
    --gris:#5a6b75;
    --trait:#e2e8ec;
    --fond-doux:#f5f8fa;
    --blanc:#ffffff;
  }
  *{box-sizing:border-box}
  .fmorse{
    font-family:"DM Sans",system-ui,sans-serif;
    color:var(--encre);
    max-width:1200px;
    margin:0 auto;
    line-height:1.6;
  }
  .fmorse .fm-wrap{
    background:var(--blanc);
    border:1px solid var(--trait);
    border-radius:14px;
    padding:28px;
  }
  .fmorse h2.fm-titre{
    font-size:1.5rem;
    font-weight:700;
    margin:0 0 4px;
    color:var(--encre);
    letter-spacing:-.01em;
  }
  .fmorse .fm-sous{
    margin:0 0 22px;
    color:var(--gris);
    font-size:.95rem;
  }
  .fmorse label{
    display:block;
    font-size:.82rem;
    font-weight:600;
    text-transform:uppercase;
    letter-spacing:.05em;
    color:var(--bleu);
    margin:0 0 8px;
  }
  .fmorse textarea{
    width:100%;
    min-height:96px;
    resize:vertical;
    font-family:"DM Sans",sans-serif;
    font-size:1.05rem;
    line-height:1.5;
    color:var(--encre);
    background:var(--blanc);
    border:1px solid var(--trait);
    border-radius:10px;
    padding:14px 16px;
    transition:border-color .15s ease, box-shadow .15s ease;
  }
  .fmorse textarea:focus{
    outline:none;
    border-color:var(--bleu-clair);
    box-shadow:0 0 0 3px rgba(41,138,212,.15);
  }
  .fmorse .fm-sortie-bloc{margin-top:22px}
  .fmorse .fm-sortie{
    font-family:"JetBrains Mono",ui-monospace,monospace;
    font-size:1.2rem;
    line-height:2;
    letter-spacing:.02em;
    color:var(--bleu);
    background:var(--fond-doux);
    border:1px solid var(--trait);
    border-radius:10px;
    padding:16px 18px;
    min-height:64px;
    word-break:break-word;
    white-space:pre-wrap;
  }
  .fmorse .fm-sortie:empty::before{
    content:"Le code Morse s’affichera ici.";
    color:var(--gris);
    font-family:"DM Sans",sans-serif;
    font-size:.95rem;
    letter-spacing:normal;
  }
  .fmorse .fm-car{
    display:inline-block;
    padding:0 2px;
    border-radius:4px;
    transition:background .08s linear, color .08s linear;
  }
  .fmorse .fm-car.actif{
    background:var(--bleu-clair);
    color:var(--blanc);
  }
  .fmorse .fm-controles{
    display:grid;
    grid-template-columns:1fr 1fr;
    gap:20px;
    margin-top:22px;
    padding:20px;
    background:var(--fond-doux);
    border:1px solid var(--trait);
    border-radius:10px;
  }
  .fmorse .fm-reglage .fm-reglage-tete{
    display:flex;
    justify-content:space-between;
    align-items:baseline;
    margin-bottom:8px;
  }
  .fmorse .fm-reglage-tete span:first-child{
    font-size:.82rem;
    font-weight:600;
    text-transform:uppercase;
    letter-spacing:.05em;
    color:var(--bleu);
  }
  .fmorse .fm-valeur{
    font-family:"JetBrains Mono",monospace;
    font-weight:700;
    font-size:.95rem;
    color:var(--encre);
  }
  .fmorse input[type=range]{
    width:100%;
    height:6px;
    -webkit-appearance:none;
    appearance:none;
    background:var(--trait);
    border-radius:3px;
    outline:none;
  }
  .fmorse input[type=range]::-webkit-slider-thumb{
    -webkit-appearance:none;
    appearance:none;
    width:20px;height:20px;border-radius:50%;
    background:var(--bleu);
    cursor:pointer;
    border:3px solid var(--blanc);
    box-shadow:0 0 0 1px var(--trait);
  }
  .fmorse input[type=range]::-moz-range-thumb{
    width:20px;height:20px;border-radius:50%;
    background:var(--bleu);
    cursor:pointer;
    border:3px solid var(--blanc);
  }
  .fmorse input[type=range]:focus-visible::-webkit-slider-thumb{
    box-shadow:0 0 0 3px rgba(41,138,212,.35);
  }
  .fmorse .fm-boutons{
    display:flex;
    flex-wrap:wrap;
    gap:12px;
    margin-top:22px;
  }
  .fmorse button{
    font-family:"DM Sans",sans-serif;
    font-size:1rem;
    font-weight:600;
    padding:12px 22px;
    border-radius:10px;
    cursor:pointer;
    border:1px solid transparent;
    transition:background .15s ease, opacity .15s ease;
  }
  .fmorse .fm-jouer{background:var(--bleu);color:var(--blanc)}
  .fmorse .fm-jouer:hover{background:var(--bleu-clair)}
  .fmorse .fm-stop{background:var(--blanc);color:var(--bleu);border-color:var(--trait)}
  .fmorse .fm-stop:hover{background:var(--fond-doux)}
  .fmorse .fm-copier{background:var(--blanc);color:var(--gris);border-color:var(--trait)}
  .fmorse .fm-copier:hover{background:var(--fond-doux)}
  .fmorse button:disabled{opacity:.45;cursor:not-allowed}
  .fmorse button:focus-visible{outline:2px solid var(--bleu-clair);outline-offset:2px}
  .fmorse .fm-note{
    margin-top:16px;
    font-size:.85rem;
    color:var(--gris);
  }
  @media (max-width:640px){
    .fmorse .fm-wrap{padding:20px}
    .fmorse .fm-controles{grid-template-columns:1fr}
    .fmorse .fm-boutons button{flex:1 1 auto}
  }
  @media (prefers-reduced-motion:reduce){
    .fmorse *{transition:none!important}
  }
</style>
</head>
<body>
<div class="fmorse">
  <div class="fm-wrap">
    <h2 class="fm-titre">Traducteur de code Morse</h2>
    <p class="fm-sous">Saisissez votre texte : la traduction s’affiche en direct, et vous pouvez l’écouter à la vitesse de votre choix.</p>

    <label for="fm-texte">Texte à traduire</label>
    <textarea id="fm-texte" placeholder="Entrez votre texte ici — lettres, chiffres et ponctuation sont pris en charge."></textarea>

    <div class="fm-sortie-bloc">
      <label for="fm-sortie">Code Morse</label>
      <div id="fm-sortie" class="fm-sortie" aria-live="polite"></div>
    </div>

    <div class="fm-controles">
      <div class="fm-reglage">
        <div class="fm-reglage-tete">
          <span>Vitesse</span>
          <span class="fm-valeur"><span id="fm-wpm-val">20</span> WPM</span>
        </div>
        <input type="range" id="fm-wpm" min="5" max="35" step="1" value="20" aria-label="Vitesse en mots par minute">
      </div>
      <div class="fm-reglage">
        <div class="fm-reglage-tete">
          <span>Tonalité</span>
          <span class="fm-valeur"><span id="fm-freq-val">600</span> Hz</span>
        </div>
        <input type="range" id="fm-freq" min="400" max="900" step="10" value="600" aria-label="Fréquence de la tonalité en hertz">
      </div>
    </div>

    <div class="fm-boutons">
      <button type="button" class="fm-jouer" id="fm-jouer">Écouter</button>
      <button type="button" class="fm-stop" id="fm-stop" disabled>Arrêter</button>
      <button type="button" class="fm-copier" id="fm-copier">Copier le Morse</button>
    </div>

    <p class="fm-note">Chronologie standard PARIS : point = 1 temps, trait = 3 temps, espace entre signes = 1 temps, entre lettres = 3 temps, entre mots = 7 temps. À 20 WPM, un temps dure 60 ms.</p>
  </div>
</div>

<script>
(function(){
  var 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':'----.',
    '.':'.-.-.-',',':'--..--','?':'..--..',"'":'.----.','!':'-.-.--',
    '/':'-..-.','(':'-.--.',')':'-.--.-','&':'.-...',':':'---...',
    ';':'-.-.-.','=':'-...-','+':'.-.-.','-':'-....-','_':'..--.-',
    '"':'.-..-.','$':'...-..-','@':'.--.-.',
    'É':'..-..','È':'.-..-','À':'.--.-','Ç':'-.-..','Ù':'..--'
  };

  var texte  = document.getElementById('fm-texte');
  var sortie = document.getElementById('fm-sortie');
  var wpm    = document.getElementById('fm-wpm');
  var freq   = document.getElementById('fm-freq');
  var wpmVal = document.getElementById('fm-wpm-val');
  var freqVal= document.getElementById('fm-freq-val');
  var bJouer = document.getElementById('fm-jouer');
  var bStop  = document.getElementById('fm-stop');
  var bCopier= document.getElementById('fm-copier');

  var audioCtx = null;
  var timers = [];
  var enLecture = false;
  var lettres = [];   // modele : { code, span, motAvant }

  // --- Traduction en direct + construction du modele ---
  function traduire(){
    var t = texte.value;
    sortie.innerHTML = '';
    lettres = [];
    var motAvant = false;     // un espace precede-t-il la prochaine lettre ?
    var premiere = true;

    for (var i=0;i<t.length;i++){
      var brut = t[i];
      if (/\s/.test(brut)){
        if (!premiere) motAvant = true;
        sortie.appendChild(document.createTextNode('   '));
        continue;
      }
      var code = MORSE[brut.toUpperCase()];
      if (!code) continue;    // caractere non pris en charge : ignore

      var span = document.createElement('span');
      span.className = 'fm-car';
      span.textContent = code;
      sortie.appendChild(span);
      sortie.appendChild(document.createTextNode(' '));

      lettres.push({ code: code, span: span, motAvant: motAvant });
      motAvant = false;
      premiere = false;
    }
  }

  function texteMorse(){
    return texte.value.toUpperCase().split('').map(function(c){
      if (/\s/.test(c)) return '/';
      return MORSE[c] || '';
    }).filter(function(x){return x!=='';}).join(' ');
  }

  // --- Lecture audio, timing PARIS ---
  function stop(){
    enLecture = false;
    timers.forEach(clearTimeout);
    timers = [];
    if (audioCtx){ try{ audioCtx.close(); }catch(e){} audioCtx = null; }
    var actifs = sortie.querySelectorAll('.fm-car.actif');
    for (var i=0;i<actifs.length;i++) actifs[i].classList.remove('actif');
    bStop.disabled = true;
    bJouer.disabled = false;
  }

  function jouer(){
    stop();
    if (!lettres.length) return;

    audioCtx = new (window.AudioContext || window.webkitAudioContext)();
    var u  = 1.2 / parseInt(wpm.value,10);   // duree d'un temps, en secondes
    var f  = parseInt(freq.value,10);
    var t0 = audioCtx.currentTime + 0.15;
    var curseur = t0;

    var osc  = audioCtx.createOscillator();
    var gain = audioCtx.createGain();
    osc.type = 'sine';
    osc.frequency.value = f;
    gain.gain.value = 0;
    osc.connect(gain).connect(audioCtx.destination);
    osc.start(t0);

    function bip(debut, duree){
      var r = 0.006; // rampe anti-clic
      gain.gain.setValueAtTime(0, debut);
      gain.gain.linearRampToValueAtTime(0.28, debut + r);
      gain.gain.setValueAtTime(0.28, debut + duree - r);
      gain.gain.linearRampToValueAtTime(0, debut + duree);
    }

    enLecture = true;
    bJouer.disabled = true;
    bStop.disabled = false;

    for (var i=0;i<lettres.length;i++){
      var L = lettres[i];

      // espace AVANT la lettre : 3 temps entre lettres, 7 temps entre mots
      if (i > 0) curseur += (L.motAvant ? 7*u : 3*u);

      // surlignage synchronise
      (function(span, debut){
        timers.push(setTimeout(function(){
          if(!enLecture) return;
          var a = sortie.querySelectorAll('.fm-car.actif');
          for (var k=0;k<a.length;k++) a[k].classList.remove('actif');
          span.classList.add('actif');
        }, (debut - t0) * 1000));
      })(L.span, curseur);

      // signes de la lettre, avec 1 temps entre chaque signe
      for (var j=0;j<L.code.length;j++){
        var d = L.code[j]==='-' ? 3*u : u;
        bip(curseur, d);
        curseur += d;
        if (j < L.code.length - 1) curseur += u;
      }
    }

    var finMs = (curseur - t0) * 1000 + 250;
    timers.push(setTimeout(function(){ if(enLecture) stop(); }, finMs));
  }

  // --- Evenements ---
  texte.addEventListener('input', function(){ if(enLecture) stop(); traduire(); });
  wpm.addEventListener('input',  function(){ wpmVal.textContent  = wpm.value;  });
  freq.addEventListener('input', function(){ freqVal.textContent = freq.value; });
  bJouer.addEventListener('click', jouer);
  bStop.addEventListener('click', stop);
  bCopier.addEventListener('click', function(){
    var m = texteMorse();
    if(!m) return;
    navigator.clipboard.writeText(m).then(function(){
      var ancien = bCopier.textContent;
      bCopier.textContent = 'Copié';
      setTimeout(function(){ bCopier.textContent = ancien; }, 1200);
    });
  });

  traduire();
})();
</script>
</body>
</html>

Fiche technique
Domaine
CW Morse
Difficulté
★☆☆☆☆ — Facile
Temps estimé
Environ 1 minute de lecture
Nombre de vues
2 856 vues
Créé le
22 décembre 2023
Mis à jour le
2 août 2026
Intelligence artificielle
L’intelligence artificielle a participé au processus de rédaction de cet article en apportant une assistance à la mise en page, à la correction de la syntaxe, de la grammaire, de l’orthographe et d’éventuelles erreurs ou améliorations de formulation.
Certains schémas, codes, illustrations ou éléments graphiques peuvent également avoir été réalisés avec l’aide d’outils d’intelligence artificielle.
En savoir plus ...

2 réflexions au sujet de « Générateur de Code Morse réalisé en HTML »

  1. Il vous manque les symboles tiers et les chiffres, une vitesse de 15 a 20 et une pause de 3 secondes entre chaque code, dans le cas contraire cela est instable et trop rapide.

    Exemple : wpm 20

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *