• #モールス信号
  • #二分木
  • #Vue
  • #Nuxt3
  • #アニメーション
  • #アマチュア無線
  • #SVG
開発mdx-playgroundメモ

きっかけ

国際モールス符号の文字割り当てが二分木で表現できることを知り、それをインタラクティブに操作できる学習アプリをNuxt 3で作ることにした。元はReactで書かれた実装があり、それをVueに移植する形で開発を進めた。

二分木とモールス符号の関係

モールス符号の全文字は、一本の二分木に収まる。STARTノードから左に進むとダッシュ(-)、右に進むとドット(・)を追加していく。各文字の符号がそのまま木の中の位置を決める。

           START
          /     \
       -(dash)  ・(dot)
        /         \
       T           E
      / \         / \
     M   N       A   I

英語の設計思想: 頻度最適化

サミュエル・モールスは、英語で最もよく使われる文字に短い符号を割り当てた。E(・)とT(-)は1符号で済む。この2文字だけで英文の約22%を占める。シャノンの情報理論(1948年)より100年以上前の直感的な最適化だった。

和文モールスは別の設計思想

日本語の和文モールスでは「む」(-)と「へ」(・)が最短の1符号。「む」と「へ」が日本語で最頻出というわけではなく、英語版とは異なる設計思想で作られている。

実務では和文モールスはほぼ使われず、日本語もローマ字(欧文モールス)で打つのが主流。

数字の位置

数字(0-9)は全て5符号で統一されている。二分木でいうと最も深い階層に位置する。アルファベットのツリーに数字を統合するため、ツリーのmaxDepthを5に拡張した。

実装した機能

二分木ビジュアライザー(SVG描画)

アプリの中心となる機能。SVGで二分木を描画し、英語(A-Z + 0-9)と日本語(ひらがな)の2つのツリーを切り替えて表示できる。

描画の設計:

  • ノード: 円の中に文字ラベルを配置
  • エッジの色分け: ダッシュ=オレンジ、ドット=青
  • STARTノード: ツリーの根。ここからの探索パスを視覚的に追跡する
  • nodeSpacing: ノード間隔の調整でツリー全体のレイアウトを制御

練習モード

文字を選択すると、音声が再生されると同時にツリー上のパスが順番にハイライトされる。

動作の流れ:

  1. 文字選択ボタンをクリック
  2. Web Audio APIでモールス音声を生成・再生
  3. 各符号の再生タイミングに合わせてactiveTraversalを更新
  4. STARTノードから終着点まで、エッジとノードが順次光る
  5. 非アクティブなエッジ/ノードはopacity 0.3でフェード
  6. 終着点ノードは通常より大きい円(r=26)で描画し、黒文字で強調

CSS transitionを使ったハイライトアニメーションで、符号の進行を目で追える。

フレーズ再生機能

単一文字だけでなく、定型フレーズをまとめて再生できる。

プリセットフレーズ:

  • SOS — 遭難信号。・・・ --- ・・・
  • CQ CQ — 不特定多数への呼びかけ。アマチュア無線の交信開始時に使う
  • 73 — 「よろしく」(Best regards)。無線通信の定番挨拶

父がアマチュア無線家で、コールサインはJA6OLU。子供の頃、無線機の前で「CQ CQ」と繰り返し呼びかけている姿をよく見ていた。このアプリでCQ CQのフレーズを再生すると、あの頃の光景を思い出す。

再生の仕組み:

  • フレーズを文字に分解し、文字ごとにツリー探索を実行
  • 文字間・単語間に適切な間隔を挿入
  • ツリー上でリアルタイムにパスが追跡される

コクピットレイアウト(PC向け)

デスクトップ表示では情報を効率よく配置した。

  • 左側: 練習モード(文字選択ボタン群 + コントロール)
  • 右側: 二分木ツリー(SVG描画領域)
  • 上部: コントロール群を横に並べる(光/音出力モード、速度調整、カテゴリ切替)

操作パネルとビジュアライゼーションを横に並べることで、ボタンを押してすぐにツリーの変化を確認できる。

OG画像の設定

/morseページにusePageOgSignatureを使ったOG画像URL生成を追加した。Worker側のgeneralハンドラーで動的生成されるため、Worker側の変更は不要だった。

技術的に工夫した点

React から Vue への移植

元のReact実装をVueに書き直す際、状態管理の違いに注意が必要だった。ReactのuseState + useEffectパターンを、Vueのref + watch/computedに置き換えていく作業が中心になる。

Web Audio API によるモールス音声生成

ブラウザのWeb Audio APIを使い、ダッシュとドットの音声をリアルタイムに生成している。外部の音声ファイルは不要で、OscillatorNodeで正弦波を鳴らす。ダッシュはドットの3倍の長さ、符号間の無音はドット1つ分、文字間はドット3つ分というモールスの国際規格に従った。

SVG描画のチューニング

ノード数が多い(アルファベット26文字 + 数字10文字 + 中間ノード)ため、nodeSpacingとノードサイズの調整に試行錯誤があった。深い階層ほどノードが密集するので、水平方向の間隔を階層ごとに計算している。

アニメーションのタイミング制御

符号の再生タイミングとツリー上のハイライトを同期させる部分が、実装上で最も手間がかかった。setTimeoutのチェーンで各符号の開始・終了タイミングを管理し、activeTraversal配列を順次更新していく。