Skip to content

Finetune Lab — a real training loop on your GPU (proof, not a product)

Today: proof. Roadmap: LoRA-style adaptation of tiny speech models, fully in-browser. Below, a from-scratch neural net trains with real gradient descent — forward pass, backpropagation, Adam — on your GPU through 9 hand-written WGSL compute kernels. Weights visibly move, loss visibly falls. Nothing is uploaded; there is no server in the loop.

Honesty first: this page never implies we fine-tune Woof or any TTS model today. What trains here is a deliberately tiny toy task that exercises exactly the machinery in-browser speech-model fine-tuning will need.

The lab

What this is: a from-scratch toy network trained on your GPU to prove the in-browser training pipeline — 5,313 weights, real forward + backprop + Adam in WGSL. It is not a speech model; nothing on TTSLab fine-tunes Woof or any production TTS today. That is the roadmap step this demo de-risks (why large fine-tunes are hard, the Woof story).
Requesting a WebGPU adapter…

How it works

The model is a 16 → 64 → 64 → 1 tanh MLP — 5,313 trainable parameters, Glorot-initialized from a fixed seed. It fits one period of a wave you pick (sine, square, triangle) over a full batch of 128 samples. Inputs pass through a fixed Fourier feature map (8 sin/cos pairs) — a disclosed, non-trainable input encoding that simply makes the fit converge fast enough to watch in real time. Optimization is genuine full-batch gradient descent with Adam (β₁ 0.9, β₂ 0.999).

Like our Woof WebGPU backend (22 kernels), everything runs over the raw WebGPU API — no ML framework, no ONNX Runtime, no dependency. But where Woof's kernels only run a frozen network, these 9 kernels train one: 22 dispatches per step flow forward, compute every weight's gradient by backpropagation, apply Adam, and read diagnostics back once per frame. Gradients are overwritten (never accumulated) by exactly one dispatch each, so no zeroing pass is needed between steps.

KernelRoleWhat it does
ft_mmfp32 matmulall 3 forward layers — one workgroup per (output, token), shared-memory tree reduction
ft_tanhactivationin-place tanh after layers 1 and 2
ft_mse_deltaoutput deltamean-squared-error deltas plus per-sample errors in one pass
ft_wgradweight gradients6 dispatches: every weight and bias gradient, reduced over the batch
ft_dgradlayer gradientsbackprop into each hidden layer through the transposed weights
ft_tanh_bwdchain rulemultiplies deltas by sech², the tanh derivative
ft_adamoptimizerfull Adam update (bias-corrected) — 6 dispatches, one per parameter tensor
ft_sqnormdiagnosticsgradient sum-of-squares reduction for the live gradient norm
ft_histogramdiagnosticsatomicAdd 32-bin weight histogram for the 'weights visibly move' chart

Verified status (honest)

The training math is unit-tested end to end in Node against a mocked GPU device: the CPU reference trainer passes a finite-difference gradient check (analytic backprop ≡ numerical derivatives) and a real CPU training run, the dispatch plans are asserted kernel-by-kernel against the WGSL binding surfaces, and a seeded run is bit-reproducible. The WGSL kernels themselves follow the same dispatch, buffer and reduction patterns as the browser-verified Woof GPU backend, but a headed-browser training run of this page is pending — the strongest proof is the demo above: if the loss falls in front of you, your GPU just ran real backprop. If it doesn't, that's a bug and we'd want to hear about it.

Robustness is part of the contract, not an afterthought: no WebGPU means a clear message with no CPU fallback (a typed-array trainer would be orders of magnitude slower and prove nothing about the GPU pipeline), device loss is caught and surfaced as a graceful reset, every GPU wait is time-bounded, and steps per animation frame are capped so the tab stays responsive.

Roadmap: from toy to speech

Shipped today: the proof. A real GPU training loop in a tab — sequential compute passes with inter-pass memory coherence, per-step uniform updates, GPU-to-CPU readbacks without stalling the frame loop, weight histogramming via atomics, device-lost recovery.

Next (roadmap, not shipped): LoRA-style adaptation of tiny speech models, fully in-browser. The idea: freeze a small model's weights on your GPU, train only tiny low-rank adapter matrices on data that never leaves your device, and keep the base weights read-only in memory. The training loop above is the piece that has to exist first.

Why not fine-tune Woof or Kokoro today? Memory ceilings, mostly. A browser tab shares one budget between GPU and CPU allocations — iOS Safari hard-kills tabs around 1.5–2 GB, storage bindings are capped at 128 MiB by default, and optimizer states (Adam alone triples weight memory) multiply everything. Fine-tuning even a "tiny" TTS model naively blows through those numbers on phones. The full accounting, with sources, is in the limits report, and the story of the smallest speech model we do run is in Underdog Woof: a deep dive.