Een microfoonknop aan een website toevoegen lijkt eenvoudig: neem iemands stem op, herken de woorden en zet ze in een tekstveld. In de praktijk is het veel lastiger om dit betrouwbaar te laten werken in browsers op desktops en mobiele apparaten, vooral wanneer alle spraakherkenning lokaal moet plaatsvinden zonder microfoongeluid naar een server te uploaden.
De uitdaging is niet alleen een geschikt spraakherkenningsmodel kiezen. Een werkende implementatie moet correct omgaan met microfoonrechten, audio-opname, resampling, browserworkers, WebAssembly, de initialisatie van ONNX-modellen, tokenisatie, caching en de koppeling met het tekstveld van de applicatie.
Dit artikel legt uit hoe je praktische, privacyvriendelijke spraak-naar-tekstfunctionaliteit bouwt met OpenAI Whisper, Transformers.js, ONNX Runtime Web, AudioWorklet en een aparte Web Worker. Ook komen verschillende echte integratiefouten aan bod waardoor een microfoonknop in een laadstatus kan blijven hangen, zelfs wanneer de modelbestanden al zijn gedownload.
Het doel is eenvoudig: een bezoeker klikt op de microfoonknop, spreekt op een natuurlijke manier en ziet de herkende tekst in het bestaande invoerveld verschijnen. Voor het verzenden kan die bezoeker het resultaat controleren of bewerken. De microfoonopname hoeft de browser nooit te verlaten.
Wat volledig lokale spraakherkenning in de browser inhoudt
Er zijn twee fundamenteel verschillende manieren om spraakinvoer aan een website toe te voegen.
De eerste gebruikt een spraakherkenningsdienst. De browser neemt geluid op, stuurt dit naar een externe dienst en krijgt herkende tekst terug. Dat kan de implementatie vereenvoudigen, maar zorgt voor afhankelijkheid van een netwerkverbinding, privacyvraagstukken, afhankelijkheid van de beschikbaarheid van de aanbieder en mogelijk kosten op basis van gebruik.
Bij de tweede aanpak vindt inferentie rechtstreeks in de browser plaats. De website downloadt een spraakherkenningsmodel en de runtime die nodig is om het uit te voeren. De browser neemt microfoongeluid op, zet het om in numerieke samples, voert het model lokaal uit en maakt een transcript zonder spraakherkenningsserver.
Dit artikel richt zich volledig op de tweede aanpak.
Gewone spraakherkenning in de browser mag niet worden verward met gegarandeerd lokale herkenning. De Web Speech API kan een herkenningsengine gebruiken die op een server draait. Sommige nieuwere implementaties bieden een optie processLocally, maar de ondersteuning verschilt per browser en besturingssysteem. Als strikt lokale verwerking vereist is, mag de algemene SpeechRecognition-interface niet automatisch als terugvaloptie worden gebruikt.
In plaats daarvan beheert de applicatie de volledige inferentiepijplijn zelf.
Microphone
|
v
AudioWorklet
|
v
Float32 PCM audio
|
v
Dedicated Web Worker
|
v
16 kHz resampling
|
v
Whisper + ONNX Runtime Web
|
v
Tokenizer and text decoding
|
v
React input stateDe website levert nog steeds JavaScript, WebAssembly, configuratiebestanden en modelgewichten aan. Dat zijn gewone statische downloads. Het verschil is dat microfoongeluid lokaal wordt verwerkt in plaats van voor herkenning te worden geüpload. Als de gebruiker de resulterende tekst later via een normaal formulier verzendt, is dat een afzonderlijke handeling in de applicatie.
Een praktisch model kiezen: Whisper Tiny of Base
Whisper is een familie van meertalige spraakherkenningsmodellen. Omdat één model veel talen kan herkennen, is het bijzonder geschikt voor meertalige websites.
Voor korte spraakberichten bieden twee modellen een bruikbare balans tussen herkenningskwaliteit en het verbruik van browserbronnen: Whisper Tiny en Whisper Base.
| Model | Geschatte grootte van de geselecteerde Q8-ONNX-gewichten | Aanbevolen toepassing |
|---|---|---|
| Whisper Tiny | 41 MB | Voorzichtige keuze voor mobiele apparaten en systemen met beperkte middelen |
| Whisper Base | 77 MB | Optie met hogere kwaliteit voor geteste, krachtige apparaten |
Deze groottes hebben betrekking op de geselecteerde ONNX-bestanden voor encoder en decoder in de onderzochte exports. Ze omvatten niet alle ondersteunende bestanden en mogen niet worden verward met het piekgeheugengebruik van de browser.
Whisper Tiny is aantrekkelijk omdat het sneller wordt gedownload, doorgaans minder rekenkracht vraagt en beter past bij mobiele omgevingen waarvan de mogelijkheden onbekend zijn. Whisper Base levert over het algemeen betere herkenningskwaliteit, vooral voor sommige talen waarin kleinere modellen meer fouten maken.
De modelkeuze mag niet alleen op het fysieke RAM-geheugen worden gebaseerd. De browser beschikt mogelijk over aanzienlijk minder geheugen dan de apparaatspecificaties doen vermoeden. Ook het geheugengebruik van de bestaande pagina, WebAssembly-allocaties, tijdelijke tensors en geheugendruk vanuit het besturingssysteem spelen mee.
Een voorzichtige aanpak is om op mobiele apparaten met onbekende mogelijkheden met Tiny te beginnen, Base te reserveren voor geschikte apparaatcategorieën en bij een initialisatiefout van Base terug te vallen op Tiny.
Een mislukte Base-instantie moet worden vrijgegeven voordat Tiny wordt geladen. Beide modellen in het geheugen houden, maakt de terugvaloptie zinloos.
Waarom WebAssembly een verstandig uitgangspunt voor productie is
ONNX Runtime Web ondersteunt het uitvoeren van neurale netwerken in browsers. Voor deze werklast biedt WebAssembly een praktische route voor CPU-inferentie in de belangrijkste desktop- en mobiele browsers.
WebGPU kan ondersteunde modellen op geschikte hardware versnellen, maar de beschikbaarheid ervan betekent niet dat een spraakherkenningspijplijn werkt. Dat een browser navigator.gpu aanbiedt, garandeert niet dat ONNX Runtime het platform ondersteunt, dat de betreffende Whisper-graaf correct initialiseert of dat herhaalde inferentie geen problemen met bronnen veroorzaakt.
Een voorzichtige implementatie kan daarom WASM als backend in productie gebruiken en WebGPU afzonderlijk beoordelen na tests van compatibiliteit, nauwkeurigheid, geheugen en herhaalde sessies.
Bij een eerste implementatie voorkomt WASM met één thread bovendien dat de hele website afhankelijk wordt van cross-origin isolation en SharedArrayBuffer.
env.backends.onnx.wasm.numThreads = 1;
env.backends.onnx.wasm.proxy = false;Door inferentie in een aparte worker uit te voeren, blijft de zware berekening weg van de hoofdthread van JavaScript. Het invoerveld, het virtuele toetsenbord, scrollen en de applicatie-interface blijven responsief terwijl het model audio verwerkt.
Laad spraakherkenning niet tijdens het eerste paginabezoek
Een microfoonfunctie mag bezoekers die haar nooit gebruiken niet opzadelen met veel netwerkverkeer en geheugengebruik.
Importeer Transformers.js niet vooraf in de initiële applicatiebundel. Initialiseer ONNX Runtime niet wanneer de pagina wordt geopend en download modelgewichten niet automatisch wanneer de pagina niets doet.
Laad de herkenningscomponenten pas nadat de gebruiker uitdrukkelijk op de microfoonknop heeft geklikt.
Een bruikbare volgorde bij het eerste gebruik is:
- Verwerk de klik op de microfoonknop.
- Vraag microfoontoestemming via
getUserMedia(). - Als de toestemming wordt geweigerd, toon dan een fout zonder het model te downloaden.
- Als de toestemming wordt verleend, geef de tijdelijke microfoonstream vrij terwijl het model wordt voorbereid.
- Initialiseer de aparte worker dynamisch en laad het gekozen lokale model.
- Wacht totdat de worker bevestigt dat de herkenningspijplijn klaar is.
- Start automatisch met opnemen als de gebruiker de handeling niet heeft geannuleerd.
- Toon een begrijpelijke status die aangeeft dat er wordt geluisterd.
Het toestemmingsverzoek moet gekoppeld zijn aan de handeling van de gebruiker. De applicatie moet ook rekening houden met annuleringen en browserspecifieke beperkingen voor audio-opname, in plaats van ervan uit te gaan dat de opname altijd ongemerkt opnieuw gestart kan worden.
Tijdens het voorbereiden van het model moet een zichtbare status verschijnen, bijvoorbeeld Spraakinvoer voorbereiden. Een laadanimatie zonder duidelijke statusovergang kan zelfs een geslaagde modeldownload laten lijken op een defecte microfoon.
Na initialisatie kan de worker nog even beschikbaar blijven voor een volgende dicteersessie. Een inactiviteitstime-out van ongeveer één minuut is een redelijk uitgangspunt. Daarna kun je de worker beëindigen en het model uit het geheugen vrijgeven. Modelbestanden in de browsercache blijven beschikbaar voor volgende bezoeken.
Ruwe audio opnemen met AudioWorklet
Whisper heeft numerieke audiosamples nodig. MediaRecorder is uitstekend geschikt voor het opnemen van audiobestanden, maar voor lokale spraakherkenning kan het een onnodige omweg via encoderen en decoderen veroorzaken.
De eenvoudigere verwerkingsroute is:
getUserMedia()
|
v
AudioContext
|
v
AudioWorklet
|
v
Float32 PCM buffers
|
v
Dedicated WorkerAudioWorklet voert audioverwerkingscode buiten de hoofdthread van JavaScript uit. Het kan PCM-samples opnemen, meerkanaals audio omzetten naar mono, eenvoudige volumemetingen berekenen en verzamelde buffers naar de worker overdragen.
Een vereenvoudigde initialisatie van de microfoon ziet er zo uit:
const stream = await navigator.mediaDevices.getUserMedia({
audio: {
channelCount: { ideal: 1 },
echoCancellation: true,
noiseSuppression: true,
autoGainControl: true,
},
});
const context = new AudioContext();
await context.audioWorklet.addModule(
'/assets/speech/pcm-worklet.js'
);
const source = context.createMediaStreamSource(stream);
const processor = new AudioWorkletNode(
context,
'pcm-capture'
);
source.connect(processor);
// Connect the worklet through an inaudible output path
// if required to keep the audio processing graph active.De microfooninstellingen zijn voorkeuren, geen garanties. Browsers kunnen verschillende algoritmen gebruiken voor echo-onderdrukking, ruisonderdrukking en automatische versterkingsregeling.
Een belangrijk detail is dat de samplefrequentie van de microfoon niet noodzakelijk 16 kHz is. Veel audiocontexten gebruiken 44.1 kHz of 48 kHz. Whisper verwacht invoer van 16 kHz, dus de opgenomen samples moeten correct worden omgezet.
Simpelweg elke tweede of derde sample weglaten is doorgaans geen juiste oplossing. Een geschikte laagdoorlaat-resampler, bijvoorbeeld op basis van een windowed-sinc-filter, vermindert aliasing en behoudt bruikbare spraakfrequenties bij het verlagen van de samplefrequentie.
Voer de resampling uit in de aparte worker, niet in de realtime callback van AudioWorklet.
Audio efficiënt tussen browserthreads overdragen
AudioWorklet kan veel kleine audioblokken produceren. Elk audioverwerkingsblok als afzonderlijk bericht doorsturen kan onnodige overhead opleveren.
Een praktische aanpak is om korte PCM-blokken te verzamelen en elk blok als ArrayBuffer over te dragen.
worker.postMessage(
{
type: 'audio-chunk',
buffer,
},
[buffer]
);De transferlijst is belangrijk: daarmee wordt het eigenaarschap van de ArrayBuffer overgedragen in plaats van de volledige inhoud onnodig te kopiëren.
De worker kan die blokken aan een audiobuffer toevoegen en ze samenvoegen wanneer de opname stopt.
Het is ook nuttig om de werkelijke samplefrequentie van de opname, het aantal verzamelde samples en de opnameduur bij te houden. Met deze metadata kun je stille opnames onderzoeken zonder gevoelige audiogegevens te loggen.
Pauzes herkennen zonder extra neuraal netwerk
Voor het dicteren van korte tekstberichten is een neurale detector voor spraakactiviteit niet per se nodig.
Een adaptieve RMS-detector kan op een lichte manier spraakactiviteit benaderen. RMS meet de signaalamplitude, zodat de applicatie kan inschatten wanneer iemand na het spreken stil is geworden.
De interface moet altijd handmatig stoppen via een tweede klik op de microfoonknop ondersteunen. Automatische stiltedetectie is een gemaksfunctie, geen voorwaarde voor succesvolle spraakherkenning.
Redelijke beginwaarden voor korte dicteersessies zijn:
| Parameter | Beginwaarde |
|---|---|
| Analyse-interval | 20 ms |
| Ruiskalibratie | 300 ms |
| Bevestiging van spraakactiviteit | 140 ms |
| Stilte vóór automatisch stoppen | 1,4 seconde |
| Time-out zonder spraak | 8 seconden |
| Maximale opnameduur | 30 seconden |
Dit zijn beginwaarden voor de configuratie, geen universeel optimale drempels. Zachte spraak, achtergrondmuziek en microfoonruisverwerking kunnen RMS-metingen beïnvloeden.
Laat gebruikers liever zelf de opname stoppen dan automatisch de laatste woorden van een zin af te breken.
Voor gewone korte tekstinvoer is één transcriptie doorgaans eenvoudiger dan Whisper steeds opnieuw uitvoeren voor tussentijdse resultaten. Neem de uitspraak op, stop de opname, voer het model één keer uit en voeg het definitieve transcript in.
Voor de volledige Whisper-pijplijn is meer nodig dan een ONNX-model
Dit is een van de belangrijkste details van de implementatie.
Whisper-inferentie is niet simpelweg een ONNX-encoder aanroepen en daarna een ONNX-decoder. Een volledige pijplijn voor automatische spraakherkenning heeft ook extractie van audiokenmerken, tokenisatie, generatieconfiguratie en het decoderen van voorspelde tokens tot leesbare tekst nodig.
De volledige verwerkingsketen is:
Raw microphone PCM
|
v
Resampling to 16 kHz
|
v
Whisper feature extractor
|
v
ONNX encoder
|
v
ONNX decoder and token generation
|
v
Whisper tokenizer
|
v
Recognized textEen succesvolle initialisatie van een ONNX-sessie bewijst alleen dat de modelgraaf kan worden geladen. Het bewijst niet dat de volledige herkenningspijplijn werkt.
Bij een geteste integratie met Transformers.js konden de modelsessies wel initialiseren, maar mislukte de transcriptie nog steeds omdat de lokaal opgebouwde pijplijn geen correct geconfigureerde processor en tokenizer had.
De compatibele implementatie initialiseerde deze onderdelen expliciet met lokale modelmetadata.
Het volgende verkorte voorbeeld laat deze versiespecifieke aanpak zien; het is geen complete zelfstandig werkende worker:
import {
WhisperTokenizer,
WhisperFeatureExtractor,
WhisperProcessor,
} from '@huggingface/transformers';
// These values come from local model configuration files.
const tokenizer = new WhisperTokenizer(
tokenizerJson,
tokenizerConfig
);
const featureExtractor = new WhisperFeatureExtractor(
preprocessorConfig
);
const processor = new WhisperProcessor(
{},
{
tokenizer,
feature_extractor: featureExtractor,
},
''
);
// In the affected version-specific integration:
recognizer.tokenizer = tokenizer;
recognizer.processor = processor;De exacte constructorsignaturen en de integratiemethode moeten worden gecontroleerd voor de geïnstalleerde versie van Transformers.js. Sommige versies en modelexports kunnen deze onderdelen automatisch maken; handmatige aanmaak is geen universele vereiste.
Met de volledige pijplijn kan transcriptie van korte opnames worden aangeroepen met een expliciete taal en taak:
const result = await recognizer(audio16k, {
language: 'en',
task: 'transcribe',
return_timestamps: false,
});Als de applicatie de taal van de gebruiker al kent, voorkomt het expliciet doorgeven daarvan onnodige taaldetectie en kan het de voorspelbaarheid bij korte opnames verbeteren.
Leg compatibele versies van Transformers.js en ONNX Runtime vast
Een tweede belangrijke foutbron is de relatie tussen Transformers.js en ONNX Runtime Web.
Transformers.js levert de herkenningspijplijn op hoger niveau, terwijl ONNX Runtime Web de neurale netwerkgrafen uitvoert. Deze pakketten moeten onderling compatibel zijn en passen bij de exacte ONNX-modelexports die worden gebruikt.
In de onderzochte implementatie kon een ontwikkelversie van ONNX Runtime Web de Whisper-encoder initialiseren, maar mislukte het maken van een gekwantiseerde decodersessie. De fout had te maken met een ontbrekende kwantisatieschaal in een DequantizeLinear-bewerking.
Dit was geen probleem met microfoontoestemming, downloaden of het audioformaat. De browser kon de modelbestanden ontvangen, maar de runtime kon een van de vereiste modelgrafen niet initialiseren.
Door zowel de encoder als de decoder rechtstreeks te testen met een stabiele, vastgepinde versie van ONNX Runtime Web werd die specifieke incompatibiliteit opgelost.
De compatibiliteitscontrole was bijzonder nuttig omdat die twee verschillende beweringen uit elkaar hield:
- Het modelbestand is succesvol gedownload.
- De runtime heeft vanuit het modelbestand succesvol een bruikbare inferentiesessie aangemaakt.
Dat is niet hetzelfde.
Voor de geteste modelexports werkte de combinatie van Transformers.js 4.2.0 en ONNX Runtime Web 1.24.3. Een nieuwere ontwikkelruntime liep vast op de gekwantiseerde decodergraaf. Dit is een versiespecifieke waarneming, geen bewijs dat oudere runtimes in het algemeen beter zijn.
Als je een indirecte runtime-afhankelijkheid overschrijft, leg dan de exacte versie vast in het lockbestand van het pakket en documenteer waarom de override nodig is. Een runtime-upgrade moet een volledige regressietest van browserinferentie vereisen, niet alleen een geslaagde pakketinstallatie.
Controleer de workerbundel voordat je Whisper gaat debuggen
Een JavaScript-Web-Worker kan in verschillende moduleformaten worden gemaakt.
Eén integratiefout ontstond doordat de applicatie een worker als ES-module startte, terwijl de gegenereerde workerbundel het mechanisme voor klassieke workers importScripts().
Deze uitvoeringsmodellen zijn niet uitwisselbaar. Een moduleworker kan niet zomaar code uitvoeren die alleen voor klassieke workers is bedoeld.
De praktische les is om de Worker-constructor af te stemmen op de daadwerkelijk gegenereerde bundel, in plaats van een constructoroptie uit een algemene handleiding over te nemen.
De volgende opties zijn bijvoorbeeld niet automatisch gelijkwaardig:
// Module worker: requires a compatible module bundle.
new Worker(workerUrl, { type: 'module' });
// Classic worker: requires a compatible classic bundle.
new Worker(workerUrl);De juiste keuze hangt af van de bundler en de gegenereerde JavaScript-code.
Onderzoek bij een initialisatiefout van de worker de browserconsole, het gegenereerde workerbestand en het netwerkpaneel. Bepaal of de fout al optreedt voordat ONNX Runtime aan de initialisatie begint.
Sommige gebundelde browserbibliotheken berekenen URL's ook relatief ten opzichte van hun eigen modulelocatie. Test daarom bij gebruik in workers de resulterende paden naar runtimebestanden, in plaats van aan te nemen dat de oorspronkelijke pakketrelatieve URL's nog correct werken.
Lever WASM- en JavaScript-modules met de juiste MIME-typen
Zelfs volledig compatibele JavaScript- en ONNX-modelbestanden kunnen mislukken als de webserver onjuiste antwoordheaders terugstuurt.
Een concrete fout betrof een JavaScript-module met de extensie .mjs die werd geleverd als application/octet-stream. De browser weigerde de module omdat het antwoordtype niet geschikt was voor de uitvoering van een JavaScript-module.
De verwachte antwoordtypen zijn doorgaans:
| Bestandsextensie | Content-Type |
|---|---|
| .mjs | application/javascript |
| .wasm | application/wasm |
Controleer het werkelijke antwoord van de productieserver, niet alleen het configuratiebestand.
curl -I https://example.com/runtime/speech/v2/runtime.mjs
curl -I https://example.com/runtime/speech/v2/runtime.wasmDe bovenstaande URL's zijn voorbeelden. Gebruik de daadwerkelijke bestandsnamen met versienummer van de gekozen runtimebuild.
Een correcte HTTP-status alleen is niet voldoende. De browser kan een bestand nog steeds weigeren vanwege het MIME-type, de Content Security Policy, de wijze van module-uitvoering of een onjuiste asset-URL.
Onthoud dat hetzelfde verzoek kan slagen wanneer je het rechtstreeks in een browsertabblad opent, maar kan mislukken wanneer het als uitvoerbare module of WebAssembly-afhankelijkheid wordt geladen.
Gebruik onveranderlijke asset-URL's zonder gebruikers met kapotte runtimebestanden op te zadelen
Whisper-modelbestanden zijn zo groot dat browsercaching belangrijk is.
Versiegenummerde, onveranderlijke assetpaden stellen de browser in staat ongewijzigde model- en runtimebestanden opnieuw te gebruiken, zonder ze bij iedere spraakinvoer opnieuw te downloaden.
Een gebruikelijk cachebeleid is:
Cache-Control: public, max-age=31536000, immutableDit stelt wel een eis aan de uitrol: zodra een URL als onveranderlijk is aangemerkt, mag de inhoud niet stilzwijgend worden vervangen.
Als het binaire runtimebestand moet veranderen, publiceer het dan onder een nieuwe URL met een versienummer.
/models/speech/v1/
/runtime/speech/v1/
/runtime/speech/v2/Het wijzigen van het runtimepad is geen cosmetische ingreep. Als de applicatie dezelfde onveranderlijke URL blijft gebruiken nadat de inhoud is gewijzigd, kan de browser een eerder gecachet, incompatibel WebAssembly-bestand blijven gebruiken.
Het is ook verstandig om de JavaScript-koppelcode en de WebAssembly-bestanden uit dezelfde runtimedistributie te gebruiken. Het combineren van bestanden uit verschillende runtimeversies kan initialisatiefouten veroorzaken die lastig te onderzoeken zijn.
Een manifest dat tijdens het bouwen de verwachte bestanden, groottes en cryptografische controlesommen vastlegt, kan ontbrekende of niet-overeenkomende assets vóór de uitrol opsporen.
Configureer de herkenningsbibliotheek zo dat zij bestanden van de eigen website gebruikt en schakel het laden van externe modellen uit. Dan levert een ontbrekend lokaal model een duidelijke fout op in plaats van onverwacht een download vanaf een externe modelhost te starten.
Waarom de microfoon blijft laden terwijl niets werkt
Een veelvoorkomend symptoom tijdens de ontwikkeling is een microfoonknop die om toestemming vraagt, een laadanimatie toont en uiteindelijk naar een foutstatus terugkeert zonder tekst in te voegen.
De browser kan zelfs aangeven dat de microfoon wordt gebruikt.
Dat bewijst niet dat de spraakherkenner werkt. Het bewijst alleen dat de microfoonopname op enig moment was toegestaan of actief was.
Verschillende onafhankelijke fouten kunnen vrijwel dezelfde symptomen veroorzaken:
- Onjuist MIME-type voor JavaScript-modules. De browser downloadt een runtimemodule maar weigert die uit te voeren.
- Niet-overeenkomend uitvoeringsformaat van de worker. De gecompileerde worker gebruikt API's voor klassieke workers terwijl hij als moduleworker wordt gestart, of andersom.
- Incompatibele ONNX-decoder. De runtime maakt de encodersessie aan, maar loopt vast op de gekwantiseerde decodergraaf.
- Onvolledige herkenningspijplijn. De ONNX-sessies bestaan, maar de tokenizer of audioprocessor is niet correct geïnitialiseerd.
Deze problemen moeten afzonderlijk worden onderzocht. Het oplossen van de ene fout kan de volgende aan het licht brengen.
Het belangrijkste principe bij het debuggen is dat HTTP 200 geen geslaagde modelinitialisatie betekent en een geslaagde modelinitialisatie nog geen geslaagde transcriptie betekent.
Zelfs een geslaagde transcriptie bewijst niet dat het resultaat in het daadwerkelijke tekstveld van de applicatie terechtkomt.
Elke overgang moet een bruikbaar diagnostisch signaal opleveren.
Maak fouten zichtbaar zonder privégesprekken te verzamelen
Voor nuttige diagnostische logs hoef je niet op te nemen wat gebruikers zeggen.
De applicatie kan bijvoorbeeld de volgende technische toestanden bijhouden:
permission-granted
worker-created
runtime-loading
runtime-ready
encoder-ready
decoder-ready
recording-started
pcm-received
recording-stopped
inference-started
inference-completed
result-insertedVermeld bij fouten de fase, een stabiele technische foutcode en opgeschoonde foutmetadata.
Een fout tijdens de modelinitialisatie moet te onderscheiden zijn van geweigerde microfoontoestemming of een lege opname. Een workerfout moet tijdens de ontwikkeling zichtbaar zijn in plaats van door een algemene foutafhandeling te worden verborgen.
Log geen microfoonsamples, herkende zinnen, tokenreeksen of andere spraakinhoud. Voor prestatietelemetrie kun je duurcategorieën, de gekozen modelklasse, initialisatietijd, inferentietijd en foutcategorieën gebruiken.
Zo wordt een niet-werkende microfoon veel makkelijker te onderzoeken zonder de privacyvereisten af te zwakken.
Voeg het transcript in via de React-state
Spraakherkenning is alleen nuttig wanneer het definitieve transcript in het bestaande formulierveld terechtkomt.
In een React-applicatie wordt dat veld vaak beheerd via componentstate. Rechtstreeks iets toewijzen aan input.value kan botsen met de manier waarop het framework rendert.
In plaats daarvan moet de herkende tekst via hetzelfde pad voor state-updates lopen als normale toetsaanslagen.
De integratie moet ook rekening houden met asynchroon gedrag. De herkenning kan pas klaar zijn nadat de gebruiker extra tekens heeft getypt of de cursor heeft verplaatst.
Een verouderde invoerwaarde die bij de start van de opname is vastgelegd, kan nieuwere wijzigingen per ongeluk overschrijven.
Een vereenvoudigd integratiepatroon is:
const insertion = insertTranscript({
currentValue: latestValue,
transcript: recognizedText,
selection: currentSelection,
maxLength: inputLimit,
});
setMessage(insertion.value);
restoreCaret(insertion.caret);De invoeghulp moet de huidige tekst behouden, eventueel de geselecteerde tekst vervangen, passende spaties gebruiken en de maximale veldlengte respecteren.
Unicode verdient aandacht omdat JavaScript de lengte van strings telt in UTF-16-code-eenheden. Als je een string op een willekeurige positie afkapt, kun je een surrogaatpaar splitsen.
Het belangrijkste is dat spraakherkenning het formulier nooit automatisch mag verzenden. De gebruiker moet de tekst vóór verzending kunnen controleren en corrigeren.
Verwerk fouten zonder gewone tekstinvoer te verstoren
Het spraaksysteem moet optioneel zijn.
Als microfoontoestemming wordt geweigerd, de browser geen geheugen kan vrijmaken voor het model, een worker vastloopt of een opname geen spraak bevat, moet het gewone invoerveld blijven werken.
Geef de levenscyclus van de microfoon weer met expliciete statussen, zoals:
idle
preparing
listening
recognizing
errorGebruik toegankelijke labels en zichtbare statusinformatie. Een microfoon die na het laden simpelweg rood of roze wordt, zegt de gebruiker vrijwel niets.
Bij annulering moeten audiotracks, de audiocontext, wachtende buffers en de actieve opnamesessie worden opgeruimd. De interface moet ook voorkomen dat een laat resultaat uit een eerdere geannuleerde opname een nieuwere invoerstatus overschrijft.
Als een groot model vastloopt door geheugenbeperkingen of initialisatieproblemen, geef het dan volledig vrij voordat je een kleiner model probeert. Beperk het aantal herhaalpogingen, zodat de browser niet steeds modellen downloadt of initialiseert die niet kunnen werken.
Schakel niet ongemerkt over op een spraakdienst in de cloud als de lokale herkenning mislukt. Als uitsluitend lokale verwerking een harde eis is, moet een mislukte lokale herkenner ook lokaal falen.
Test de volledige browserpijplijn
Een geslaagde TypeScript-controle of productiebuild is geen afdoende bewijs dat spraakinvoer werkt.
Ook het feit dat de browser ONNX-bestanden kan downloaden, bewijst niet dat beide modelsessies correct initialiseren.
De nuttigste regressietest moet dezelfde worker, runtimebestanden, hetzelfde model, dezelfde processor en dezelfde tokenizer gebruiken als de productieomgeving.
Voer een bekende korte PCM-opname door het echte inferentiepad in de browser en controleer of een niet-leeg transcript wordt teruggegeven.
Test daarna de volledige microfooninteractie in een echte browser.
- Eerste paginabezoek: controleer dat er geen spraakmodellen of runtimebestanden worden gedownload voordat de microfoon wordt gebruikt.
- Koude initialisatie: controleer microfoontoestemming, modelverzoeken, MIME-typen voor JavaScript en WASM en een echte gereedmelding van de worker.
- ONNX-initialisatie: controleer of zowel encoder- als decodersessies kunnen worden aangemaakt.
- Audio-opname: bevestig dat AudioWorklet niet-lege PCM-buffers produceert.
- Resampling: bevestig dat de worker de werkelijke samplefrequentie ontvangt en geldige audio van 16 kHz produceert.
- Handmatig stoppen: controleer of een tweede klik op de microfoonknop de opname betrouwbaar stopt.
- Automatisch stoppen: controleer of natuurlijke pauzes de opname beëindigen zonder zachte spraak af te kappen.
- Transcriptie: controleer of processor, model en tokenizer samen herkenbare tekst teruggeven.
- Integratie met het invoerveld: controleer of de herkende tekst in het echte, door state beheerde veld verschijnt zonder wijzigingen van de gebruiker te overschrijven.
- Herstel na fouten: test geweigerde toestemming, ontbrekende bestanden, workerfouten, mislukte modelinitialisatie, annulering en het terugvallen op een kleiner model.
- Privacy: controleer de netwerkverzoeken en bevestig dat microfoongeluid niet naar een server of analysedienst wordt geüpload.
- Compatibiliteit met mobiele apparaten: herhaal de tests op echte telefoons, met koude starts, warme starts en opeenvolgende dicteersessies.
Tests met opgenomen PCM zijn uitstekend om de inferentiestack te controleren, maar bewijzen niet dat de microfoonopname werkt. De uiteindelijke acceptatietest moet de echte microfoon en de daadwerkelijke gebruikersinterface omvatten.
Een praktische architectuur voor betrouwbare spraakinvoer in de browser
Voor een website die korte, meertalige en privacyvriendelijke spraakinvoer nodig heeft, is de meest praktische architectuur bewust eenvoudig:
React microphone button
|
v
Permission request
|
v
Lazy model initialization
|
v
AudioWorklet PCM capture
|
v
Adaptive RMS / manual stop
|
v
Dedicated Worker
|
v
16 kHz audio resampling
|
v
Whisper Tiny or Base Q8
|
v
Compatible ONNX Runtime WASM
|
v
Whisper processor + tokenizer
|
v
Final transcript
|
v
Controlled React inputDe applicatie moet model- en runtimebestanden met versienummers zelf hosten, inferentie buiten de hoofdthread uitvoeren, onnodige modeldownloads voorkomen, bronnen na gebruik vrijgeven en nooit microfoongeluid voor spraakherkenning uploaden.
Geavanceerdere mogelijkheden, zoals versnelling met WebGPU, neurale detectie van spraakactiviteit, doorlopende tussentijdse transcriptie of extra modelklassen, kunnen later worden overwogen wanneer metingen de extra complexiteit rechtvaardigen.
De belangrijkste les gaat niet over een specifieke bibliotheekversie of modelgrootte, maar over het belang van het controleren van iedere overgang in de spraakherkenningspijplijn van de browser.
Een werkend verzoek om microfoontoestemming betekent niet dat audio wordt getranscribeerd. Een gedownload model betekent niet dat de decoder is geïnitialiseerd. Een geslaagde ONNX-sessie betekent niet dat de tokenizer beschikbaar is. En een geslaagd transcript betekent niet dat de applicatie de tekst correct heeft ingevoegd.
De functie is pas af wanneer een bezoeker op de microfoon kan drukken, op een natuurlijke manier kan spreken, de opname kan stoppen en de herkende woorden in het bewerkbare veld ziet verschijnen, zonder de opname naar een spraakherkenningsserver te sturen.