Русский · 🌐 English
Тёмный минималистичный тренажёр азбуки Морзе Буквы по отдельности · Метод Коха · Группы символов · Реальные слова · Позывные · XP и достижения
🌐 morse.r9old.ru — живая версия
MorseWave — веб-тренажёр азбуки Морзе, который проведёт от полного нуля до приёма на слух реальных позывных и подготовки к сдаче радиолюбительского экзамена (в «Группах» есть режим экзамена по нормативам «Первой категории»). Без фреймворков на фронтенде и без Bootstrap: чистый JS (Web Audio API) + собственная тёмная дизайн-система.
Прогресс (XP, уровень, серия дней, выученные символы) хранится локально в браузере — регистрация не обязательна, тренироваться можно сразу. Аккаунт — опциональная надстройка, которая даёт три вещи: место в таблице лидеров на главной (только по явной кнопке «Опубликовать»), синхронизацию всего прогресса между устройствами (начал на компьютере — продолжил с телефона) и восстановление доступа по почте. MySQL под капотом хранит справочные данные (достижения, банк позывных), анонимную статистику сообщества, аккаунты и серверные копии прогресса.
Интерфейс двуязычный (русский / английский) — переключатель в шапке рядом с профилем, подробности в разделе «Как это устроено внутри».
| Раздел | Что внутри |
|---|---|
| 📖 Буквы | Три подрежима: отправка ключом (тап/пробел, с живым сайдтоном; три набора — «Латиница + цифры», порядок Коха, кириллица), приём на слух (играет символ → нажми на экранной клавиатуре; отдельный набор «Кириллические символы») и ритм ключа (сравнивает реальные длительности точки/тире/пауз с идеалом 1:3:1 — скорость свободно регулируется, идеал пересчитывается под неё; 5 точных повторов подряд оттачивают ритм буквы и дают разовый XP; наборы — «Латиница + цифры» и кириллица, без порядка Коха — он там ни при чём) |
| 👾 Вторжение (BETA) | Мини-игра-тауэр-дефенс рядом с «Буквами»: пришельцы летят на базу с буквой на слух, символ надо распознать и нажать на самодельной QWERTY-клавиатуре (или на физической клавиатуре компа) до того, как враг долетит — с базы летит лопата. Скорость врага зависит от сложности кода символа, до 5 одновременных целей по ходу волны из 100 попаданий, победная волна даёт бонус XP от остатка HP базы и скорости реакции |
| 🎯 Метод Коха | Символы сразу на целевой скорости; новый символ открывается при точности ≥90%; можно вручную установить любое число открытых символов (в обе стороны) |
| 🔢 Группы символов | Приём на слух групп по 2–5 символов: буквы / цифры / микс / только выученные / свои, скорость и интервалы Фарнсворта — слайдерами; режим экзамена по нормативам радиолюбительской категории |
| 📝 Реальные слова | Приём частых английских слов и радиообменных фраз (CQ CQ DE R9OGL K, UR RST 599 599): тренирует узнавание слова единым звуковым образом, а не сборку по буквам. Третий таб в «Группах» |
| 💬 Сокращения | Q-коды и радиолюбительские сокращения (CQ, QTH, 73…) на слух с вариантами ответа |
| 📡 Позывные | Тренировка на реальных по формату позывных (включая российские форматы), банк расширяется скриптом-генератором |
| 🏆 Достижения | Ачивки, условия хранятся в БД (achievements) — можно добавлять новые без правки кода |
| 🎯 Задание дня | Целевое задание под этап игрока (новичку — «изучи буквы», дальше — «прими на слух», знающему алфавит — группы) + бонус +50 XP раз в день |
| 💡 Сигнальная лампа | Визуальный аналог звука — можно тренироваться совсем без звука |
| 🏆 Таблица лидеров | Топ-10 на главной по опыту и по серии дней + отдельной строкой настоящее место залогиненного пользователя, если он не попал в десятку; отдельная страница со всей таблицей целиком (leaderboard.php), публикация — только по явной кнопке «Опубликовать» |
| 👤 Аккаунт | Опционально: синхронизация прогресса между устройствами с индикатором «когда синхронизировано», восстановление пароля по почте, самостоятельное удаление аккаунта |
| 💾 Бэкап прогресса | Скачать/загрузить прогресс файлом на странице профиля — аккаунт не нужен. Импорт сливает данные «только вверх», поэтому старый файл поверх нового прогресса безопасен |
| Кнопка на странице «Достижения» — полный сброс (localStorage и серверной копии) с подтверждением | |
| 🌐 Русский / English | Переключатель языка в шапке; письма, юридические страницы и API-ошибки тоже переведены |
- PHP 8+ — без фреймворка, простой роутинг по файлам
- MySQL / MariaDB — через PDO
- Vanilla JS — Web Audio API для звука, без сборки и npm
- Кастомный CSS — дизайн-система в
assets/css/style.css, без Bootstrap - Docker — для локальной разработки (PHP+Apache, MySQL, phpMyAdmin)
Коротко — локально через Docker:
git clone <URL_РЕПО> morse-trainer
cd morse-trainer
docker compose up -d --buildСайт на http://localhost:8080, phpMyAdmin на http://localhost:8081.
Полные инструкции — запуск без Docker, шпаргалка по командам, деплой на VPS, миграции базы, настройка почты и администрирование (кто становится админом и как передать права) — в DEPLOY.md.
morse-trainer/
├── docker-compose.yml # локальный запуск: PHP+Apache, MySQL, phpMyAdmin
├── docker/ # Dockerfile + конфиг Apache (AllowOverride для .htaccess)
├── README.md # этот файл — обзор проекта на русском (основной, так видят GitHub-посетители по умолчанию)
├── README.en.md # обзор проекта на английском
├── DEPLOY.md # запуск, деплой, миграции, администрирование
├── CLAUDE.md # контекст и правила для Claude Code
├── CHANGELOG.md # история изменений, v2.49+ (свежие записи сверху)
├── CHANGELOG-archive.md # история изменений, v2.48 и старше
├── lang/
│ ├── ru.php # словарь строк интерфейса — русский
│ └── en.php # словарь строк интерфейса — английский
├── api/ # JSON-эндпоинты: achievements, callsigns, stats, register,
│ # login, logout, me, sync_progress, refresh_published_stats,
│ # leaderboard, verify_email, resend_verification, captcha,
│ # admin_users, admin_rename_user, admin_delete_user,
│ # admin_set_admin, push_progress, pull_progress,
│ # delete_progress, delete_account, request_password_reset,
│ # reset_password, update_account, unpublish_stats
├── config/
│ ├── database.example.php # шаблон конфига БД (коммитится)
│ ├── database.php # реальный конфиг БД с кредами (в .gitignore)
│ ├── mail.example.php # шаблон конфига почты (коммитится)
│ ├── mail.php # реальный конфиг почты с ключом (в .gitignore)
│ └── .htaccess # запрет веб-доступа к папке
├── database/
│ ├── schema.sql # ЕДИНСТВЕННЫЙ источник истины по схеме БД
│ ├── seed_callsigns.php # генератор пачки позывных
│ └── .htaccess # запрет веб-доступа к папке
├── includes/ # header / footer / nav + auth.php, mailer.php, i18n.php,
│ # resend_mailer.php, captcha.php, leaderboard_guard.php
├── storage/sessions/ # файлы PHP-сессий (закрыты .htaccess, в git не хранятся)
├── assets/
│ ├── css/style.css # дизайн-система
│ └── js/ # morse-data, daily, audio, progress, signal, input, i18n + страницы
├── index.php # главная + задание дня
├── leaderboard.php # вся таблица лидеров целиком
├── learn.php # буквы: отправка / приём на слух / ритм ключа
├── koch.php # метод Коха
├── groups.php # группы символов 2–5 (+ реальные слова, сокращения)
├── callsigns.php # тренировка позывных
├── achievements.php # достижения + сброс прогресса
├── account.php # профиль (вкладки: аккаунт / звук / отображение / бэкап)
├── terms.php · privacy.php # пользовательское соглашение и политика конфиденциальности
├── admin.php # админка: пользователи, переименование, удаление, выдача прав
└── settings.php # 301-редирект на account.php (настройки переехали в профиль, v2.24)
Все начисления округляются до целых. Уровень растёт как 1 + √(XP / 80)
(т.е. 2-й уровень — 80 XP, 3-й — 320 XP, 5-й — 1280 XP и так далее).
| Режим | Сколько | Условия и защита от фарма |
|---|---|---|
| Буквы → отправка ключом | +25 XP за символ | Один раз за символ — когда впервые набрана серия из 5 верных повторов подряд. Повторные серии по выученному символу XP не дают. Ставка одинаковая для латиницы и кириллицы — без надбавки за сложность (осознанное упрощение, v2.51.1). |
| Буквы → приём на слух | +1 XP за верный ответ | Спам верной плиткой в паузе между раундами заблокирован. Ставка одинаковая для латиницы и кириллицы (в v2.51.1 кириллица недолго давала +2, но это путало — одинаково выглядящие латинская и кириллическая «M» получали разный XP; откачено в v2.51.7). |
| Буквы → ритм ключа | +25 XP разово за букву | Та же анти-фарм схема, что и у отправки ключом: XP не капает за отдельные попытки, а выдаётся один раз, когда набрано 5 точных повторов подряд (буква верная И точность ритма ≥ 80% от идеала 1:3:1). Любая осечка обнуляет серию; повторная тренировка уже отточенной буквы — без XP. |
| Вторжение (BETA) | (База 1 XP) × 2 × (Плотность волны / 3) (как в Кохе), + бонус ×2 за добивание Босса. Бонус победы волны (10–130 XP). |
Награда растет соразмерно плотности волны (количеству пришельцев на экране), используя балансную формулу из Метода Коха/Групп, чтобы предотвратить фарм опыта. Боссы (группы символов) при добивании дают дополнительный опыт, равный их длине ×2. Бонус победы начисляется только за полностью пройденную волну (100 попаданий). |
| Метод Коха | 2 × (символов_открыто / 15, но не более 1 и не менее 0.15) × (длина_группы / 3) XP за каждый верно принятый символ |
Чем больше открыто символов и длиннее группы — тем выше ставка (максимум 2 XP/символ при полном наборе). На старте с 2 символами ставка минимальная — фармить лёгкие сессии невыгодно. |
| Метод Коха — бонус | +10/+20/+30 XP (равен числу групп в сессии) | Только если сессия завершена целиком с точностью ≥ 90 %. Ачивки за метод считаются по честно заработанному уровню (kochLevelEarned) — протащить бегунок до конца и получить ачивку нельзя. |
| Группы символов | 2 × √(размер_набора / 26, но не более 1 и не менее 0.15) × (длина_группы / 3) XP за каждый верно принятый символ |
Ставка зависит от размера выбранного набора и длины групп. Начисляется сразу за каждую отвеченную группу. |
| Группы — повтор ошибок | Опыт x3 от базового + бонус 5 XP за безошибочную группу | Генерируются новые случайные группы с замешиванием символов, в которых была ошибка (если перепутал две буквы — в группу попадут обе). Бонус работает как анти-абуз: начисляется только если точность в оригинальной сессии была ≥ 60%. Если меньше (умышленный проклик пустых ответов) — XP за ошибки не начисляется (0 XP). |
| Группы — экзамен | ставка сессии × верные символы | Только если экзамен доигран до конца — досрочная остановка обнуляет XP экзамена (в общие счётчики группы всё равно идут). |
| Реальные слова | 1.2 XP за символ (слова) / 1.5 XP (фразы) | Ставка ниже, чем в группах: связный текст предсказуем, недослышанную букву можно восстановить по смыслу. Если слово принято хуже чем на 60 % — XP за него не начисляется вовсе (иначе банк слов фармился бы вслепую). Пробелы во фразах в счёт не идут. |
| Сокращения (Q-коды) | +5 XP за верный ответ | — |
| Позывные | +20 XP за верно принятый позывной | Начисляется сразу, не дожидаясь конца сессии. |
| Задание дня | +50 XP бонусом | Один раз в день. Задание целевое под этап игрока (см. daily.js): новичку — «изучи 3 новые буквы», осваивающемуся — «прими 20 символов на слух», знающему алфавит — приём групп. Для группового задания параметры сессии должны совпадать с заданием и точность быть не ниже 50 % (прокликать пустые ответы не выйдет, фикс v2.26). |
| Бонус за скорость (только в задании дня) | множитель XP speedXpFactor(wpm): ≤12 wpm → ×1.0, выше — +4 %/wpm, потолок ×1.6 (≈27 wpm) |
Применяется только к групповому заданию дня, где скорость навязана. В обычных сессиях, «Реальных словах» и Кохе не действует — там скорость выбирает сам человек. Смысл: не заставлять новичка гнаться за 24 wpm ради тех же 50 XP, но честно доплачивать за высокий темп. Фарма нет — XP по-прежнему только за верный символ, а быстро принять сложнее. |
Выглядит как недосмотр, но это осознанное решение (v2.33) — не сводите их к одной.
В Группах набор символов выбирает сам человек, поэтому маленький набор обязан давать пропорционально меньше XP. Прежний делитель 15 упирался в потолок слишком рано: свой набор из 15 самых лёгких знаков давал ровно ту же ставку, что полный алфавит из 36, — это была работающая лазейка. Делитель 26 (полный латинский алфавит) её закрывает, а корень вместо линейной пропорции нужен, чтобы честные промежуточные наборы вроде «Цифры» (10 знаков) не штрафовались слишком жёстко. Числа подобраны так, что оба основных пресета — «Буквы» (26) и «Буквы + цифры» (36) — дают прежние 2.0 XP/символ: баланс существующих режимов не поехал.
В Кохе набор задан уровнем, а не выбором человека. Пофармить маленьким набором там нельзя в принципе: для этого пришлось бы сидеть на низком уровне, который и так даёт минимальную ставку. Поэтому формула осталась старой — ужесточение не закрыло бы ни одной лазейки, зато срезало бы мотивацию на средних уровнях метода.
Серия дней (🔥 streak) засчитывается только за реальную завершённую тренировку (сессия Коха/групп/позывных или задание дня) — просто зайти на сайт недостаточно.
assets/js/audio.js—MorseAudioпроигрывает точки/тире через осциллятор Web Audio API, поддерживает интервалы Фарнсворта (символы на одной скорости, паузы — на другой, более медленной).onSymbolколбэк отдаётdurationMs— используется для точной синхронизации визуализации.assets/js/input.js—TelegraphKeyпревращает удержание клавиши (тап по экрану или пробел) в точки/тире по длительности нажатия относительно текущегоwpm, плюс живой сайдтон (реальный звук) во время удержания. Пороги:< 2×unit→ точка, иначе тире; пауза2.5×unit— граница буквы, ещё+4×unit— граница слова.assets/js/daily.js—DailyChallenge: единый источник «задания дня» для главной и режимов-исполнителей, задание считается из пары «дата + этап игрока».assets/js/signal.js—SignalLine(осциллограф из точек/тире) иMorseLamp(визуальная лампа) — сквозные визуальные элементы, синхронно отражающие и звук, и ввод на всех страницах тренировки.assets/js/progress.js— весь прогресс пользователя в одном ключеlocalStorage(morsewave_progress_v1): XP, уровень, серия дней, выученные символы, уровень Коха, статистика, разблокированные достижения.Progress.resetAll()— полный сброс.- Синхронизация и её индикатор.
Progress.pushNow()шлёт весь Progress-объект вapi/push_progress.phpи при ответеok: trueзапоминает момент в отдельном ключеmorsewave_last_sync+ шлёт событиеprogress:synced;account.jsрисует по нему строку «☁ Синхронизировано: 5 минут назад». Метка ставится только при реальном успехе. - Бэкап файлом —
Progress.exportBackup()/Progress.importBackup(). Импорт намеренно идёт через тот жеmergeFromServer(), что и слияние при логине: «только вверх» (max по числам, union по множествам). Поэтому загрузить старый файл поверх свежего прогресса безопасно. - Достижения определяются в MySQL (
achievements, полеcondition_typecondition_value), а проверка условий против статистики происходит на клиенте вProgress.checkAchievements()— сопоставление типов см. вprogress.js(statValue()).
- Защита лидерборда (
includes/leaderboard_guard.php) — перед публикацией вuser_statsзначения обрезаются по правдоподобному потолку (привязан к возрасту аккаунта), отсекая грубую накрутку. Реальный прогресс не меняется — режется только публичная строка (подробности в CHANGELOG, v2.40). - Интернационализация (
includes/i18n.php,lang/ru.php,lang/en.php,assets/js/i18n.js) — язык выбирается по?lang=→ cookiemw_lang→Accept-Languageбраузера → по умолчанию русский. Хелперt('ключ')доступен и в PHP-шаблонах, и (как глобальная функцияt()) в JS черезwindow.MW_I18N, которыйincludes/header.phpкладёт в<head>. Фолбэк при отсутствующем переводе: текущий язык → русский → сам ключ. Слоговые мнемоники букв (MORSE_MNEMONICSиCYRILLIC_MNEMONICSвmorse-data.js) — фонетическая подсказка, завязанная на русское звучание, поэтому в английском интерфейсе она осознанно отключена, а не переведена дословно. - Кириллическая азбука Морзе (
learn.php, v2.51) — отдельная таблица на 33 кода (CYRILLIC_CODE/CYRILLIC_TO_CHARвmorse-data.js, в обучающем наборе 31 буква —ЪиЁисключены из тренировок, чтобы не путать сЕи не загромождать процесс редкими знаками), намеренно не смешанная с латинскойMORSE_CODE/MORSE_TO_CHAR: часть кириллических букв совпадает по коду с «похожей по звучанию» латинской (так исторически устроена сама азбука), поэтому общий реверс-словарь сделал бы декодирование ключом непредсказуемым.TelegraphKey(input.js) принимает таблицу декодирования параметром/черезsetTable(), а не жёстко зашитым глобалом. Прогресс по кириллице хранится с префиксомRU_(learnedLetters) — латинскаяAи кириллическаяАникогда не пересекаются, а ачивка «Полный алфавит» считается строго по латинице+цифрам (progress.js), несмотря на то что кириллические записи лежат в том же массиве.
Актуальная история (v2.49 и новее) — в CHANGELOG.md, свежие записи сверху. Более ранние версии, вплоть до самой первой, — в CHANGELOG-archive.md.
- Аккаунты — опциональная надстройка над localStorage (не обязательны для
тренировки). Клиент остаётся источником истины по XP, поэтому абсолютной
защиты от накрутки нет; публичный лидерборд прикрыт серверным санитарным
потолком (
leaderboard_guard.php, v2.40) — он отсекает грубые значения, но это «замок от честных людей», а не пуленепробиваемая защита. Один и тот же прогресс из одного браузера технически можно опубликовать от нескольких аккаунтов — барьером служит email-подтверждение + мягкое предупреждение при повторной публикации. - Письма шлются через Resend API (см. DEPLOY.md) — на shared-хостинге SMTP оказался ненадёжен (нет DKIM, PTR общего IP не указывает на домен, Gmail тихо режет письма).
- Капча на азбуке Морзе (
includes/captcha.php) — базовая защита от примитивных скриптов, не от целенаправленного обхода. TelegraphKeyкалибрует точку/тире относительно выбранногоwpm, а не адаптируется к реальному темпу пользователя.- Кириллица есть только в «Буквах» (отправка + приём на слух) — «Метод Коха» и «Группы символов» остаются латинскими. Это осознанно, не недосмотр: у Коха фиксированный классический порядок символов, который по определению латинский, а Группы/режим экзамена ориентированы на программу радиолюбительского экзамена (латиница+цифры) — кириллический набор там просто не на что было бы натянуть.
Порядок примерно = приоритет (подробности и нюансы — в CLAUDE.md):
- Свой адаптивный декодер приёма на слух — подстраивается под реальную скорость передачи по таймстемпам, а не под выбранный wpm
- Адаптивная калибровка ключа под реальный темп пользователя
- (дальний прицел) Нативное Android-приложение (не PWA)
- (исследование) Подключение реального телеграфного ключа (адаптер на Arduino / Web Serial / микрофонный вход)
Уже сделано: восстановление пароля по почте, экспорт/импорт прогресса файлом, целевые задания дня, самостоятельное удаление аккаунта, защита лидерборда, двуязычный интерфейс (русский/английский), кириллическая азбука Морзе в «Буквах» (v2.51), полная таблица лидеров + своё место на главной (v2.52), оценка ритма ключа в «Буквах» (v2.53), мини-игра «Вторжение» — тауэр-дефенс на азбуку Морзе (v2.54, статус BETA), достижения за волны вторжения и за отточенный ритм ключа (v2.55–v2.56).
Освоил приём отдельных знаков и позывных — дальше по маршруту (см. «Путь новичка» на главной) идёт Morse Walker: симулятор радиосвязи с pile-up, режимы контестов и POTA, генератор российских позывных. Это отдельный проект, не часть MorseWave — исходный автор W6NYC, доработка и русификация — тоже R9OGL: github.com/loloka/morsewalker.
MIT — делай с этим что хочешь, см. LICENSE.