DogEar TTS engine — browser correctness rig

The one question: does the shipped engine bundle (tts-engine.js + shelf ONNX Runtime + Kokoro q8 graph), running in THIS real browser, produce clean (non-NaN) waveforms on long inputs? In the Node proxy, a ~450-token run returned an all-NaN waveform, and even after the shipped 140-token split one piece of the stress paragraph still NaN’d (107,400 of 699,000 samples); the first run of this rig in a real browser found the same defect on the long sentence below (162,600 of 422,400 samples). This page runs the shipped bundle as built — no stub, no reimplementation — through its own createEngine({modelDir, runtimeDir}).speak(text, voice, speed) entry, the same call the app makes.

This build carries the non-finite guard: the driver scans every synthesized piece for NaN/non-finite samples, retries a bad piece once on a fresh inference session, then as smaller split fragments, and fails loudly rather than returning silence. The Recovery column reports the driver’s own per-run record: clean first try and clean after retry are different facts and are never merged — a recovered run is marked “CLEAN (after retry)” and named again in the verdict line. The long sentence runs five extra trials and the whole paragraph two extra, because the defect is intermittent.

Conditions (as loaded in this browser)

Model assembly

The 86 MB model exceeds the per-file asset limit, so it is served as four chunks which this page concatenates before the engine asks for it. The engine fetches the model by URL exactly as in production; this page answers that one fetch from the assembled buffer (the same seam the app’s service worker occupies in production). Concatenation is the only transformation — the SHA-256 below must equal the registry pin or the battery will not run.

Assembling…

Battery

Voice af_heart, speed 1. Expected phoneme counts were measured with the branch’s own G2P in Node (gold+silver lists); if a run’s measured count differs from its expected count, the silver lists likely did not load in this browser (the engine then runs gold-only) and the run is still valid but not the same input — compare counts first.

RunPhonemes (expected)Tokens SamplesNaN samples PeakTimeVerdictRecovery