Клиентский ASR С Faster-Whisper-WASM В Браузере

Автор: Николай СапуновОбновлено: август 202618 мин чтения
Содержание статьи +

Кратко

Клиентское распознавание речи – это когда слова, сказанные в микрофон, превращаются в текст кодом, который работает прямо в браузере, на компьютере или телефоне самого пользователя, и звук при этом никуда не уходит с устройства. Фраза «faster-whisper-WASM» – это собирательное название для семейства инструментов, которые помещают модель Whisper от OpenAI в браузер, но за этим именем прячется важное различие: настоящие браузерные движки – это whisper.cpp, скомпилированный в WebAssembly, и Transformers.js, работающий на WebGPU, тогда как сам «faster-whisper» – отдельная серверная библиотека, которая в браузере не запускается вовсе. Эта статья распутывает названия, проходит весь конвейер в браузере от микрофона до субтитра на экране, объясняет два движка, которые делают тяжёлую работу, и правила браузера, которые их ограничивают, и даёт ясное решение, когда распознавать на устройстве выгоднее, чем отправлять звук на сервер. К концу вы будете знать, что реально, во что обходится каждый кусок по объёму загрузки и расходу батареи и какие функции вашего продукта действительно стоит держать на клиенте.

Почему это важно

Если ваш продукт захватывает речь – инструмент для встреч, функция диктовки, языковое приложение, голосовой интерфейс, киоск – вы упираетесь в одну архитектурную развилку ещё до первой строчки кода: где работает распознавание речи – на ваших серверах или на устройстве пользователя? Распознавание на устройстве обещает три вещи, которых продуктовые команды очень хотят: звук остаётся приватным, потому что никуда не передаётся; функция продолжает работать без интернета; и вы не платите облачный счёт за минуты, сколько бы люди ни говорили. Эта статья для продакт-менеджера, основателя или техлида, который слышал «да мы просто запустим Whisper в браузере» и хочет понять, правда ли это, чего это на самом деле стоит и где оно тихо ломается. Это парная статья к разбору серверного паттерна субтитров; там доказывается, что для групповых звонков лучше распознавать один раз на сервере, а здесь размечен противоположный путь – когда правильное место для «слуха» именно устройство.

Сначала распутаем название

Фраза «faster-whisper-WASM в браузере» делает слишком много работы, и разобрать её на части – самый быстрый способ понять всю область. Внутри прячутся три разные вещи, и путаница между ними – самая частая ошибка в этой теме.

Начнём с модели. Whisper – это модель речь-в-текст, выпущенная OpenAI в 2022 году: программа, обученная слушать звук и записывать слова. Она бывает разных размеров – от «tiny» с 39 миллионами внутренних параметров до «large» примерно с 1,5 миллиарда, где больше параметров означает выше точность, но крупнее и медленнее модель. Оригинальный Whisper – исследовательский релиз, написанный на Python; сам по себе он не приспособлен ни к браузеру, ни к скорости.

Теперь запутанная часть. «faster-whisper» – это конкретный отдельный проект, переписанная реализация Whisper на быстром движке вывода CTranslate2, которая гоняет ту же модель в несколько раз быстрее и с меньшим расходом памяти. Но faster-whisper работает на сервере или на десктопе, на Python, часто на видеокарте. Он не запускается внутри браузера, и официального «faster-whisper-WASM» не существует. Когда люди произносят эту фразу, они почти всегда имеют в виду один из настоящих браузерных движков ниже, а раскрученное имя «faster-whisper» используют как замену слову «быстрый Whisper». Понять это важно: если вы пойдёте искать браузерную сборку faster-whisper, вы её не найдёте и потеряете неделю.

Так что же реально работает в браузере? Тяжёлую работу делают два движка. Первый – whisper.cpp: переписанный Whisper на языках C и C++ без внешних зависимостей, который можно скомпилировать в WebAssembly (сокращённо Wasm) – формат, позволяющий почти-нативному скомпилированному коду исполняться внутри браузера. Второй – Transformers.js: JavaScript-библиотека от Hugging Face, которая гоняет Whisper через браузерный движок ONNX Runtime Web и умеет задействовать видеокарту устройства через новый стандарт WebGPU. Третий, более лёгкий вариант – Moonshine, новая модель, специально созданная для живой транскрипции на устройстве. Именно эти три – а не «faster-whisper» – вы берёте, когда цель – распознавание в браузере.

Рисунок 1. Самое полезное различие в этой теме: «faster-whisper» – серверная библиотека, а реально в браузере работают whisper.cpp (WebAssembly), Transformers.js (WebGPU) и встроенный Web Speech API.

Почему не взять встроенное распознавание браузера?

Прежде чем качать какую-либо модель, справедливо спросить: может, браузер уже делает это бесплатно? Почти делает. Есть встроенная функция браузера под названием Web Speech API, а внутри неё – интерфейс SpeechRecognition, который превращает речь из микрофона в текст несколькими строчками JavaScript. Если бы он решал задачу, эта статья была бы в один абзац.

Он её не решает по двум причинам. Первая – приватность, и она главная. В Chrome – основном браузере, поддерживающем эту функцию, – распознавание по умолчанию вообще не идёт на устройстве. Браузер записывает микрофон и отправляет звук в веб-сервис Google, который его расшифровывает и присылает текст обратно. То есть единственная функция, к которой тянутся, чтобы оставить звук на устройстве, по умолчанию делает ровно наоборот: отправляет звук третьей стороне и требует интернет-соединения. Это рушит цели приватности и работы офлайн, ради которых клиентское распознавание вообще привлекательно.

Вторая причина – поддержка. SpeechRecognition реализуют только Chrome и другие браузеры на Chromium, например Edge; в Safari и Firefox он долго был непоследовательным или отсутствовал. Функция, которая работает в одном семействе браузеров и молча падает в других, – не то, что можно выкатить всем пользователям.

В новых версиях стандарта добавляют переключатель, заставляющий распознавание идти локально на устройстве, и браузеры постепенно добавляют локальные режимы. Но вы не контролируете, какой браузер и какая версия у пользователя, и не можете пообещать клиенту, что звук останется на устройстве, когда движок под капотом может отправить его на сервер. Когда приватность или работа офлайн – реальное требование, нужен движок, который вы поставляете и контролируете сами, то есть загруженная модель, запускаемая через whisper.cpp или Transformers.js. Об этом и остальная статья.

Конвейер в браузере, шаг за шагом

Поместить Whisper на веб-страницу – это не одно действие, а короткий конвейер. Пройти его от микрофона до субтитра – значит увидеть, где сидят настоящая работа и настоящие затраты.

Начинается всё с захвата звука. Браузер запрашивает у пользователя доступ к микрофону через стандартный вызов getUserMedia, который возвращает живой аудиопоток. Этот поток идёт на той частоте дискретизации, что у устройства, обычно 44 100 или 48 000 отсчётов в секунду, но Whisper ждёт звук ровно на 16 000 отсчётов в секунду в один канал. Поэтому следующий шаг пересэмплирует и сводит звук в моно – небольшой кусок кода, обычно работающий в аудио-ворклете (фоновом обработчике звука, который не подвешивает страницу), превращает поток из микрофона в моно-поток 16 кГц, который нужен модели.

Дальше – гейт, и это главный трюк эффективности. Распознавание речи дорого гонять; тишину расшифровывать незачем. Перед моделью стоит маленький дешёвый детектор Voice Activity Detection – VAD, программа, отвечающая на вопрос «да/нет: кто-то сейчас говорит?». Частый выбор в браузере – компактная модель Silero VAD, запускаемая через тот же ONNX Runtime Web, которая оценивает каждый короткий кусочек звука от 0 до 1 по вероятности речи. Дальше проходит только звук, перешагнувший порог. Без этого гейта движок будет молоть каждую секунду фонового гула и жечь батарею впустую.

Затем работает сама модель. Гейченный звук 16 кГц подаётся в Whisper – исполняемый либо как WebAssembly через whisper.cpp, либо на видеокарте через Transformers.js – и модель возвращает текст. Этот текст показывают, и для живого опыта показывают в две стадии: быстрая черновая догадка, пока человек ещё говорит, и устоявшаяся финальная строка после паузы. Поведение «черновик, потом финал» мы подробно разбираем в статье про живые субтитры; оно одинаково и на сервере, и, как здесь, на устройстве.

Вся цепочка – микрофон, пересэмплинг, VAD-гейт, модель, показ – живёт целиком внутри вкладки браузера. Звук не уходит. Это и есть то свойство, которое вы покупаете, а конвейер – его цена.

Рисунок 2. Каждый этап от микрофона до субтитра работает внутри вкладки браузера. VAD-гейт перед моделью держит расход батареи в норме; файл модели – разовая загрузка, которая потом кешируется.

Два движка: WebAssembly против WebGPU

Модель должна как-то исполняться на «железе» устройства, и путей два. Они различаются достаточно, чтобы выбор определил всю вашу функцию, так что стоит понять оба простыми словами.

Первый путь – WebAssembly, его использует whisper.cpp. WebAssembly – это способ взять код на быстрых компилируемых языках вроде C и исполнять его внутри браузера почти с нативной скоростью. Он работает на основном процессоре устройства – CPU. Чтобы идти быстрее, он опирается на два дополнения: SIMD, позволяющий одной инструкцией обрабатывать сразу несколько чисел, и потоки (threads), которые распределяют работу по нескольким ядрам CPU одновременно. WebAssembly широко поддерживается и работает без видеокарты, что делает его надёжным «полом», – но он ограничен скоростью CPU, поэтому на крупной модели может не успевать за живой речью.

Второй путь – WebGPU, его использует Transformers.js. WebGPU – новый веб-стандарт, разработанный той же группой, что стандартизирует веб, который позволяет веб-странице задействовать видеокарту устройства – GPU – для общих вычислений, а не только для отрисовки графики. Поскольку GPU создан выполнять тысячи маленьких вычислений параллельно, он подходит для матричной математики внутри речевой модели куда лучше CPU. Hugging Face измерили, что WebGPU гоняет их модели до ста раз быстрее, чем путь WebAssembly на той же работе. Эта громкая цифра зависит от задачи и держится не для каждой модели, но направление реально: когда GPU доступен, он меняет то, какой размер модели вы можете гонять вживую в браузере.

С WebGPU есть нюанс: он новее, поэтому есть не везде. Он включился по умолчанию в Chrome и Edge в 2023 году, добрался до Safari от Apple и Firefox от Mozilla в течение 2025-го, и к концу 2025 года работал по умолчанию во всех основных браузерах, – но на старом браузере или слабом устройстве его может не быть. Поэтому надёжный продукт относится к WebGPU как к быстрому пути, а к WebAssembly – как к запасному: используйте видеокарту, когда она есть, и спускайтесь на CPU, когда её нет.

Есть ещё одно правило, на котором спотыкаются команды именно на пути WebAssembly. Чтобы использовать потоки – работу на нескольких ядрах CPU, – WebAssembly нужен общий блок памяти, а браузеры разрешают это только когда страница включает режим безопасности под названием cross-origin isolation (межисточниковая изоляция). На практике это значит, что ваш сервер должен слать с каждой страницей два конкретных HTTP-заголовка (для протокола – Cross-Origin-Opener-Policy и Cross-Origin-Embedder-Policy). Забудьте их – и многоядерный путь молча отключится, транскрипция поедет на одном ядре и поползёт. Это строчка конфигурации, а не правка кода, и это самая частая причина, почему демо Whisper в браузере, бывшее быстрым на машине разработчика, тормозит в проде.

Рисунок 3. Путь CPU (WebAssembly) – надёжный запасной, работающий почти везде; путь GPU (WebGPU) куда быстрее, но новее. Заголовки cross-origin isolation – легко упускаемое требование для многоядерного пути WebAssembly.

Во что это реально обходится: загрузка, память и батарея

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

Первая затрата – загрузка модели. Модель – это файл, который браузер должен скачать в первый раз, а потом он кешируется для следующих визитов. В формате whisper.cpp размеры конкретны: модель «tiny» – около 75 мегабайт, «base» – около 142, «small» – около 466. Сжатие модели приёмом под названием квантизация – хранение её чисел меньшим числом бит, чтобы ужать файл ценой небольшой потери точности, – снижает их примерно до 31, 57 и 182 мегабайт. По-человечески: квантизованная «base» на 57 мегабайт – это загрузка размером с короткий видеоролик: норм по домашнему Wi-Fi, заметно на телефоне со слабым сигналом и реальный фактор, если пользователь на тарифе с оплатой за трафик.

Вторая затрата – память. Модель должна лежать в рабочей памяти устройства, пока работает. Грубый ориентир: «tiny» нужно несколько сотен мегабайт памяти во время работы, а размеры покрупнее – пропорционально больше. На современном ноутбуке это незаметно; на бюджетном телефоне с несколькими открытыми приложениями это может стать разницей между плавно и с рывками.

Третья затрата – батарея и нагрев, и платится она только пока модель реально работает. Именно поэтому VAD-гейт из конвейера так важен: расшифровывая только речь и пропуская тишину, вы режете время работы модели – а значит, и расход батареи – до доли. Функция диктовки, гоняющая модель только пока пользователь держит кнопку, стоит почти ничего; вечно слушающий транскрайбер без гейта согреет телефон прямо в руке.

Вот сделка одним решением: модель крупнее точнее, но качается дольше, ест больше памяти и сильнее сажает батарею. Для большинства живых функций на устройстве золотая середина – модель «tiny» или «base», квантизованная, с VAD-гейтом и WebGPU там, где он доступен. Такая комбинация держит загрузку под сотней мегабайт, память – умеренной, а расход батареи – привязанным к тому, сколько люди реально говорят.

Числовой пример: успевает ли устройство за живой речью?

Вопрос, который решает, возможна ли вообще живая транскрипция на устройстве, прост: успевает ли модель расшифровать секунду звука меньше чем за секунду? Если да – она поспевает за живой речью; если нет – отстаёт, и субтитры опаздывают всё сильнее с каждым предложением. Инженеры меряют это коэффициентом реального времени (real-time factor, RTF) – временем, которое модель тратит на обработку звука, делённым на длину этого звука. Ниже 1,0 – быстрее реального времени; выше 1,0 – не успевает.

Разберём пример. Допустим, на некотором ноутбуке среднего уровня модель «base» Whisper, работающая на CPU через WebAssembly, тратит 1,2 секунды на расшифровку каждой 1 секунды звука:

RTF = время обработки ÷ длина звука
RTF = 1,2 с ÷ 1,0 с
RTF = 1,2   → выше 1,0, значит не успевает вживую

При 1,2 она отстаёт – годится для расшифровки готовой записи, но слишком медленно для живых субтитров. Теперь переключим ту же модель на GPU через WebGPU и допустим, что она обрабатывает ту же 1 секунду звука за 0,25 секунды:

RTF = 0,25 с ÷ 1,0 с
RTF = 0,25   → заметно ниже 1,0, с запасом успевает вживую

При 0,25 у неё есть запас. Этот разрыв – от «слишком медленно» до «комфортно» – практическая причина, почему WebGPU изменил то, что возможно в браузере. Точные числа зависят от устройства, размера модели и звука, поэтому мерить нужно на том «железе», которое реально есть у ваших пользователей, а не верить бенчмарку с топовой машины разработчика. Но форма ответа надёжна: на CPU для живой работы держитесь наименьших моделей; с GPU можно гонять модель крупнее и точнее и всё равно успевать.

Whisper не строился для живого – поэтому есть Moonshine

Есть структурная причина, почему живая транскрипция на Whisper неуклюжа, и стоит знать о новой модели, которая это чинит. Whisper спроектирован обрабатывать звук фиксированными кусками по тридцать секунд. Подайте ему три секунды речи – и он всё равно добьёт вход до тридцати секунд внутри себя и сделает работу полного окна, то есть для коротких живых кусочков выполнит кучу лишних вычислений. Для расшифровки подкаста это нормально; для субтитрирования живого разговора мелкими порциями – неэффективно.

Модель 2024 года Moonshine от компании Useful Sensors была построена именно чтобы починить это для живого использования. Вместо постоянных тридцатисекундных окон Moonshine масштабирует обработку под фактическую длину поданного звука – три секунды речи стоят трёх секунд работы, а не тридцати. Создатели сообщают, что она работает примерно в пять раз быстрее сопоставимой модели Whisper «tiny» на коротком клипе без потери точности, и она достаточно мала, чтобы работать на телефонах и даже на Raspberry Pi. Для продукта, чья вся работа – живая транскрипция коротких реплик на устройстве (голосовые команды, живые субтитры, диктовка), Moonshine всё чаще оказывается лучше подогнанным движком, и гоняется он через ту же браузерную машинерию, что и Whisper.

Вывод не «всегда берите Moonshine». Whisper остаётся более обкатанным и шире поддержанным выбором с большим покрытием языков и крупной экосистемой. Суть в том, что модель – заменяемая деталь конвейера с Рисунка 2, и подбор модели под задачу (Whisper для общей транскрипции, Moonshine для тесных живых циклов) – часть того, чтобы делать это хорошо.

Клиент или сервер? Решение, которое действительно важно

Всё вышесказанное служит одному продуктовому решению: распознаванию работать на устройстве или отправлять звук на сервер и расшифровывать там? Оба варианта валидны; правильный ответ зависит от задачи, и две статьи этой пары доказывают две стороны. Вот честное сравнение.

КритерийКлиентский ASR (в браузере)Серверный ASR (звук наверх)
Приватность звукаСильная – звук не покидает устройствоСлабее – звук попадает к вам или к вендору
Работа офлайнДа – после загрузки модели связь не нужнаНет – нужна живая связь
Облачная плата за минутыНет – работает на «железе» пользователяЕсть – счёт за минуту звука
Затрата для пользователя сразуЗагрузка модели (31–182 МБ) + батареяНет – устройство только шлёт звук
Потолок точностиОграничен тем, что мелкая модель тянет вживуюВысокий – крупнейшие модели на GPU-сервере
Работа на слабом телефонеИногда – упирается в память и CPU/GPUДа – устройство лишь пишет и грузит
Согласованность у всехЗависит от возможностей устройстваОдинаковая – один движок на всех
Лучше всего для1:1, диктовка, офлайн, приватность, киоскиГрупповые звонки, запись, поиск, макс. точность

Читайте по своему продукту. Если вы строите приложение диктовки с упором на приватность, полевой инструмент, который обязан работать без сигнала, киоск или звонок один-на-один, где звук не должен касаться сервера, – клиент и есть правильное место для распознавания, и затраты (загрузка модели, батарея, более низкая точность мелкой модели) того стоят. Если вы строите групповые встречи, которые ещё и записываете, ищете по ним и переводите, или вам нужна точность крупнейших моделей – правильное место сервер, и наш разбор серверного fan-out – это паттерн для вас. Многие зрелые продукты делают и то и другое: гоняют мелкую модель на клиенте ради мгновенного приватного отклика и шлют звук на сервер, когда пользователь включает функции повышенной точности. Конвейер из этой статьи – клиентская половина такой схемы.

Рисунок 4. Развилка, которой служит вся статья. Клиентское распознавание выигрывает в приватности, офлайне и нулевой плате за минуты; серверное – в точности, согласованности и слабых устройствах. Зрелые продукты часто гоняют оба.

Частая ошибка: ставить самую большую модель, какую нашли

Самая частая и самая разрушительная ошибка в клиентском ASR – выбирать модель только по точности и тянуться к крупной, потому что в тихом тесте она показывает лучший результат. Она прекрасно работает на быстром ноутбуке разработчика с готовым аудиофайлом – и так и уезжает в релиз.

На реальном телефоне среднего уровня крупная модель – катастрофа сразу в трёх отношениях. Это огромная загрузка – сотни мегабайт или гигабайт, – которую многие пользователи бросят, не дождавшись. Она требует больше памяти, чем может выделить бюджетное устройство, поэтому подвисает или падает. И она работает медленнее реального времени на CPU, поэтому живые субтитры, которые она должна была питать, отстают всё сильнее с каждым предложением. Функция, идеально отработавшая на демо, непригодна для тех, ради кого её строили.

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

Где здесь Фора Софт

Мы строим живые видео- и голосовые продукты, где этот выбор «клиент против сервера» возникает постоянно – видеоконференции, телемедицинские консультации, e-learning, полевые и киосковые приложения, – и делаем выбор по каждой функции, а не один раз на весь продукт. Когда заказчику нужно, чтобы звук оставался на устройстве ради приватности или продолжал работать без связи, мы гоняем распознавание в браузере по описанному здесь конвейеру: захват на 16 кГц, гейт через voice-activity detection, мелкая квантизованная модель на WebGPU с запасным путём WebAssembly, и заголовки cross-origin isolation, чтобы многоядерный путь действительно включился. Когда задача – записанная групповая встреча, которой нужны ещё поиск и перевод, мы вместо этого расшифровываем на сервере. Поскольку мы работаем в здравоохранении и образовании, где приватность и офлайн часто жёсткие требования, а не приятные мелочи, мы относимся к клиентскому распознаванию как к полноценному варианту и подбираем модель под реальные устройства, которые носят пользователи наших заказчиков.

Главное

  • «faster-whisper» – серверная библиотека; реальные браузерные движки – whisper.cpp (Wasm) и Transformers.js (WebGPU).
  • Встроенный Web Speech API по умолчанию шлёт звук в Google – это не на устройстве.
  • Конвейер: микрофон → 16 кГц моно → VAD-гейт → Whisper → субтитр, всё во вкладке.
  • WebGPU гоняет модель на GPU, до ~100x быстрее, чем путь CPU на WebAssembly.
  • Потокам WebAssembly нужна cross-origin isolation (заголовки COOP + COEP), иначе они молча отключаются.
  • Подбирайте модель под худшее устройство; добирайте точность VAD и шумоподавлением, а не объёмом.

Что почитать дальше

Строите такую систему?

Подберём параметры кодирования под ваш контент и посчитаем стоимость доставки до старта разработки.