Generates amazing subtitles in real time
  • JavaScript 83.7%
  • CSS 6.5%
  • Kotlin 6.2%
  • PowerShell 2%
  • HTML 1.6%
Find a file
2026-09-04 15:23:49 +03:00
android Added help 2026-09-04 15:23:49 +03:00
core Added help 2026-09-04 15:23:49 +03:00
desktop Amaze 2026-09-04 15:14:48 +03:00
tools Amaze 2026-09-04 15:14:48 +03:00
.gitignore Amaze 2026-09-04 15:14:48 +03:00
alphatest.png Amaze 2026-09-04 15:14:48 +03:00
package-lock.json Amaze 2026-09-04 15:14:48 +03:00
package.json Amaze 2026-09-04 15:14:48 +03:00
README.md Amaze 2026-09-04 15:14:48 +03:00

musetext

Кинетическая типографика под то, что играет прямо сейчас. Приложение само определяет трек через системный медиа-сеанс, находит синхронизированный текст, режет его на слова и разгоняет по чёрному экрану — с переменной перспективой (аналог CC Power Pin из After Effects), вариативной раскладкой строк и цветными вспышками.

Одно ядро на две платформы: core/ — весь визуальный движок, desktop/ и android/ — только оболочки, которые дают ему доступ к ОС.

core/       общий веб-движок: раскладки, перспектива, эффекты, поиск текста
desktop/    Electron + чтение Windows SMTC через PowerShell
android/    Kotlin + WebView + NotificationListenerService
tools/      дев-сервер для работы над core/ в браузере

Запуск на ПК

npm install
npm start

Собрать установщик:

npm run dist

Требуется Windows 10 1809 или новее. Определение трека работает для всего, что регистрирует медиа-сеанс: Spotify, YouTube Music, браузеры, AIMP, foobar2000, десктопные UWP-плееры.

Работать над визуалом можно и без Electron:

npm run web

и открыть http://localhost:5173 — там доступны ручной ввод трека и локальные аудиофайлы.

Сборка под Android

Открыть папку android/ в Android Studio и собрать app. Или из консоли, если установлен Android SDK:

cd android
./gradlew assembleRelease

Gradle подключает core/ как assets напрямую, копировать ничего не нужно.

После установки приложение попросит доступ к уведомлениям — Android отдаёт список активных медиа-сеансов только владельцу включённого NotificationListenerService. Сервис в MuseNotificationListener.kt пустой: он существует только как пропуск и не читает содержимое уведомлений.


Откуда берётся текст

Порядок источников:

  1. Локальный кэш в localStorage.
  2. LRCLIB — точный запрос, затем поиск в нескольких формулировках сразу: по очищенному названию, по сырому, только по названию, и с переставленными местами исполнителем и названием на случай, если файл назван наоборот. Кандидаты со всех запросов оцениваются вместе.
  3. NetEase — только на десктопе: эндпоинт не отдаёт CORS-заголовки, поэтому запрос идёт через главный процесс Electron.
  4. Файл, брошенный в окно. Он закрепляется за текущим треком навсегда.

Текст с таймингом всегда весомее точного совпадения по имени: строка без таймкодов приложению почти бесполезна, поэтому поиск продолжается, даже когда точный запрос уже что-то вернул.

Форматы

Понимает три вида разметки:

[00:07.17] строка              обычный LRC
[00:07.17]<00:07.17>по <00:07.60>словам    расширенный LRC
00:07.17 - строка              без скобок, с любым разделителем или без него

Часы (1:00:07.5) тоже читаются. Формат без скобок включается, только если так размечен весь файл, иначе строка, начинающаяся со времени суток, была бы принята за таймкод.

Если пословных меток нет, слова раздаются внутри строки пропорционально их длине — достаточно точно, чтобы читалось как караоке.

Метаданные

У файла, брошенного в окно, читаются настоящие теги — ID3v2, MP4/M4A, FLAC, Ogg/Opus — и только если их нет, берётся имя файла. Имена вида y2mate_Название 1080p, 03. Название, Название (Official Video) чистятся перед поиском.

Если текст всё равно не нашёлся, приложение показывает карточку с исполнителем и названием: поправь их и нажми «Искать». Найденное закрепится за треком, так что повторять не придётся.

Про YouTube Music. Определение трека оттуда работает (это обычный медиа-сеанс). А вот текста через публичный API у неё нет, поэтому слова всё равно приходят из LRCLIB или NetEase.

Рендер видео

Раздел Рендер видео в настройках. Кнопка спрашивает, куда сохранить, и пишет файл с настоящим альфа-каналом: чёрного фона в нём нет, только текст.

Формат Что внутри
QuickTime ProRes 4444 prores (4444), yuva444p12le — то, что ждёт таймлайн в After Effects
QuickTime Animation qtrle, argb — без потерь, файл заметно больше
Секвенция PNG rgba, папка с кадрами, ffmpeg не нужен

Разрешение задаётся пресетом или руками, отдельно кадры в секунду и отрезок трека. Галочка «Только текст» убирает эффекты, которые двигают строку.

Как это работает: страница открывается второй раз — скрыто, прозрачно и сразу в нужном разрешении — и прогоняется по кадрам. Ничего не снимается с того окна, которое вы видите, поэтому на результат не влияет ни скорость машины, ни то, что происходит в живом приложении. Рендер трёхсекундного отрезка занял 3.5 секунды.

Два ограничения стоит знать заранее. Полноэкранные слои — вспышки, зерно, скан-линии, виньетка — в прозрачном рендере выключаются всегда: каждый из них превратился бы в сплошную заливку поверх альфы. И для двух форматов QuickTime нужен ffmpeg в системе; если его нет, приложение так и скажет, а секвенция PNG работает без него.

Рендер есть только в десктопной сборке. В браузере и на Android кнопка честно сообщает, что не может.

Какой плеер слушать

Приложение показывает все медиа-сеансы, которые видит система, и само выбирает играющий: «текущий сеанс» по версии Windows часто принадлежит вкладке браузера, которая доиграла час назад. Выбор можно переопределить в настройках, раздел Источник.

VLC 3 не публикует медиа-сеанс вообще — это ограничение самого плеера, а не приложения. Его ловит отдельная дорожка через встроенный веб-интерфейс:

  1. В VLC: Инструменты → Настройки → Интерфейс → Показывать все → Основные интерфейсы → включить Web.
  2. Там же Lua → HTTP → задать пароль.
  3. Перезапустить VLC.
  4. В настройках musetext включить «Ловить VLC» и вписать тот же пароль.

Пять режимов подачи

Классика — строка ставится целиком. Перспектива меняется на протяжении её жизни, строка дрейфует и растворяется на месте.

Разлёт — перспектива задаётся один раз, в момент появления, и дальше не трогается. Кусок летит по прямой с постоянной скоростью и уходит за край экрана, а не тает в воздухе. Длинная строка не ставится одним предложением: она режется на два-три куска, и каждый появляется в своём углу кадра. Рез идёт по знакам препинания, короткие строки не дробятся.

В этом режиме размер куска берётся из громкости голоса в момент его появления, а на смехе, тянутых гласных и вибрато текст трясётся. Смех и тянутые гласные опознаются по самому тексту, вибрато по звуку, если включена реакция на звук.

Кусок не улетает, пока его ещё поют: до последнего слова он едва дрейфует, а разгоняется и уходит за край только после него.

Лесенка — строки стоят уступом: первая прижата влево, последняя вправо, между ними ровные ступени. Ни поворота, ни наклона; глубина идёт только от перспективы, которая задаётся при появлении и дальше не меняется. Ступень держится до тех пор, пока её место в стопке не понадобится снова, поэтому вся лесенка стоит на экране целиком, а не по одной строке.

Число ступеней равно значению «Строк на экране». Поставьте 2, чтобы получить ровно две строки, влево и вправо.

Камера — вся песня разложена в одну длинную ленту вправо, а едет не текст, а точка обзора. Строка стоит на своём месте в мире и оказывается по центру ровно в свой таймкод. Она рождается уже за правым краем и снимается со сцены, только когда целиком ушла за левый: никаких проявлений и растворений в воздухе, строку просто режет край кадра. Сверху медленный крен, лёгкое покачивание и дыхание трансфокатора. Скорость и крен настраиваются.

Тоннель — песня становится коридором, а зритель летит по нему. Каждая строка это плита объёмного текста, стоящая в своей точке глубины: часть написана на левой стене, часть на правой, что-то поперёк прохода, что-то на полу и потолке. Плита не двигается сама, движется точка обзора.

Объём настоящий, а не тень: у каждого слова свои боковые грани, сложенные стопкой копий по оси Z и затенённые от светлого ближнего ребра к тёмному дальнему. Тень так не умеет, потому что выдавливание идёт вдоль нормали плиты, а когда плита развёрнута к стене, это направление выходит из её плоскости.

Стены повёрнуты примерно на 5565 градусов, а не на прямой угол: под 90 строка стоит ребром и читается, только когда вы с ней поравнялись. Строка собирается по словам на подлёте, в ритме собственных слов, и стоит целиком ровно в свой таймкод. Глубина граней настраивается ползунком «Объём букв», скорость полёта берётся из «Скорости камеры».

Фон

По умолчанию за текстом стоит обложка трека: сильно размытая, почти чёрная, и подсвечиваемая цветными вспышками по музыке. Смысл в том, что картинку освещают, а не показывают.

Обложка берётся из тегов файла (ID3v2 APIC, MP4 covr, FLAC PICTURE, Vorbis METADATA_BLOCK_PICTURE), а если файла нет, ищется по исполнителю и названию через поиск iTunes — ключ не нужен, ответ отдаётся с открытым CORS.

Если обложки нет нигде, остаются одни вспышки в палитре пресета. Всё это отключается и регулируется в разделе Фон: режим, размытие, базовая яркость и сила вспышек.

Длительность строки

Тайминг в LRC отмечает только начало строки. Наивный способ дотянуть её до следующей строки ломается там, где после фразы идёт проигрыш: слова расползаются на всю паузу.

Поэтому по умолчанию стоит по темпу пения. Скорость пения оценивается по самому треку: у строки внутри фразы отношение «символы к промежутку» близко к правде, у строки перед проигрышем оно занижено, так что настоящий темп лежит в верхней части распределения. Берётся высокий процентиль, и длительность строки считается из её длины, но не длиннее промежутка до следующей.

На реальном треке это укоротило 10 строк из 36, худшую с 8 секунд до 1.35. Строки внутри фраз не тронуты.

Режим по темпу + обрыв на тишине делает то же самое и вдобавок гасит строку, когда после последнего слова звук держится тихим. Работает только при включённой реакции на звук: без анализатора взять это неоткуда.

Режим до следующей строки — прежнее поведение, если оно вам нужно.

Текст в скобках

Бэк-вокал и ад-либы почти всегда пишут в скобках. По умолчанию такие слова приглушены, их можно вернуть в обычный вид или убрать совсем. Если в строке не было ничего кроме скобок, она пропускается целиком.

Что настраивается

Панель по клавише S или по кнопке рядом с названием трека.

Вайб — пресет: шрифт, палитра, энергия, набор раскладок и эффектов. На «Авто» пресет выбирается по плотности текста, длине строк, наличию кириллицы и, если включена реакция на звук, по реальной энергии трека.

Пресет Шрифт Характер
Brutal Stalinist One монументально, медленно, крупно
Edit (AE) Oswald диагонали и перспектива, как в эдите
Hyperpop Unbounded быстро, ярко, много вспышек
Tech JetBrains Mono мелко, плотно, RGB-сдвиг и скан-линии
Pixel Tiny5 пиксельно, кислотно
Dream Unbounded мягко, крупно, с зерном
Raw BBH Bartle если файл шрифта на месте

Движение — скорость выезда, сила перспективы, наклон, кегль, сколько строк держится на экране, отступ от краёв, насколько раньше строка появляется и сколько висит после.

«Отступ от краёв» задаёт поле, за которое строка не заедет в момент появления. Считается по повёрнутому габариту, так что наклонная строка тоже остаётся в кадре. Раскладки, которые нарочно уходят за край, это поле игнорируют.

Эффекты — вспышки, тряска, зум-панч, RGB-сдвиг, инверсия, зерно, скан-линии, виньетка. Каждый — отдельная величина от нуля до единицы. Пока стоит галочка «Эффекты по пресету», они следуют за вайбом; любое движение ползунка её снимает.

Синхронизация — сдвиг текста относительно звука и реакция на звук.

Управление

Под названием трека стоит ряд кнопок, дублирующий все горячие клавиши: спрятать интерфейс, перетасовать раскадровку, сдвинуть текст на ±0.1 с, пауза, полный экран. Плюс шестерёнка справа от названия открывает настройки. На телефоне это единственный способ до них добраться, поэтому кнопки есть всегда.

Когда интерфейс спрятан, вернуть его можно коротким тапом или щелчком по экрану. Долгое нажатие под это не попадает — оно занято другим.

Клавиши

H спрятать интерфейс
S настройки
F полный экран
R перетасовать раскадровку
[ ] сдвиг текста на ±0.1 с
Пробел пауза
Esc закрыть панель или карточку

Есть и кое-что ещё, если поставить шрифт Tiny5 и подержать пять секунд левую кнопку мыши или палец на экране. Там стрелки и WASD, включая русскую раскладку, а на телефоне управление перетаскиванием: сердце ходит за движением пальца, а не прыгает под него. Выход по Esc или по крестику в углу. Пока идёт игра, клавиши интерфейса отключены, чтобы S не открывал настройки вместо шага вниз.


Шрифты

В комплекте идут четыре гарнитуры под лицензией SIL Open Font License — Stalinist One, Unbounded, Oswald, JetBrains Mono и Tiny5. Все с кириллицей.

BBH Bartle и Druk распространять нельзя, поэтому их нет в репозитории. Положите свои файлы сюда, и они сами появятся в списке шрифтов:

core/fonts/user/BBHBartle.woff2   (или .woff / .ttf / .otf)
core/fonts/user/Druk.woff2

Как это устроено

Перспектива. core/js/powerpin.js считает гомографию, которая переносит четыре угла элемента в четыре произвольные точки, и отдаёт её как CSS matrix3d. Это ровно то, что делает CC Power Pin. Углы задаются не свободно, а через коэффициенты сжатия рёбер — так трапеция остаётся выпуклой, а точка схода не подходит вплотную к строке. Без этого ограничения длинная строка схлопывается в нечитаемую ленту.

Раскладки. core/js/layouts.js — семь способов поставить строку: крупно по центру, с уходом за край, по диагонали, с сильной перспективой, субтитровым пролётом, вразброс и стопкой. Выбор взвешенный и зависит от пресета, повтор подряд подавляется. Всё детерминировано от идентификатора трека, так что одна и та же песня всегда режется одинаково, а R меняет зерно.

Часы. Позиция от ОС приходит раз в 250300 мс и дёргается. core/js/timing.js держит собственные часы на performance.now() и подтягивает их к каждому отчёту, а прыжок больше 0.6 с считает перемоткой. Плееры, которые не публикуют позицию вовсе, тоже работают: в отчёте есть метка времени, от которой позиция экстраполируется.

Отрисовка. Никаких CSS-анимаций — все трансформации переписываются каждый кадр от позиции в треке. Текст всегда ровно там, где песня.

Ограничения

  • Определение трека на десктопе — только Windows. Ядро при этом кроссплатформенное: на macOS и Linux остаётся ручной ввод и локальные файлы.
  • VLC 3 не отдаёт медиа-сеанс — только через его веб-интерфейс, см. выше.
  • Реакция на звук работает на десктопе (петля системного звука через Electron). На Android обычному приложению системный звук не отдают, поэтому там визуал идёт только по часам текста.
  • Точность позиции зависит от плеера. Некоторые публикуют её редко или не публикуют вовсе — на такой случай есть ползунок сдвига.