webTLM demos / webLTM (opaque) / Visible Thinking / Real Retrofit
SmolLM2‑135M · one real pretrained layer, looped, fine‑tuned

Same Trick, Real Language

webLTM proves the mechanism — a block that loops over its own latent state with no chain-of-thought slot — on 153K parameters that only add numbers. This demo proves the mechanism retrofits onto an actual pretrained language model: take a real 30-layer transformer, freeze 29 of those layers, replace the 10 in the middle with one shared, looped layer, and lightly fine-tune only that one layer on real English.

102.65M total params 3.54M fine-tuned (3.4%) base: HuggingFaceTB/SmolLM2-135M trained on wikitext-2, 500 steps, CPU
01

What actually got retrained

Prelude and coda are the base model's own weights, byte-for-byte, run once each. The only thing that changed is one layer, looped.

embed
(frozen)
→
prelude
layers 0–9
(frozen)
→
core
1 layer × r
(fine-tuned)
→
coda
layers 20–29
(frozen)
→
norm + head
(frozen, tied)

The core starts as a deep-copy of the original layer 15, then gets looped r times per forward pass and re-trained with randomized depth (r sampled 1–12 every step) — the same trick webLTM uses, so one set of weights has to work across a range of thinking budgets rather than memorizing a single loop count. No KV-cache: every generated token recomputes the whole sequence through prelude + core×r + coda, which is why this demo is slow (see §03) — that's real transformer compute, not a shortcut.

02

What 500 CPU steps actually bought

Only the core layer's 3.54M parameters have gradients; everything else stays exactly as SmolLM2 shipped it. Loss and a same-prompt sample, logged every 50 steps:

steptrain lossval loss (r=2)val loss (r=10)val loss (r=12)
012.856.2913.3114.34
1005.444.825.886.30
3005.214.564.985.06
4994.854.424.885.05
step 0"The history of the city began when" — nothing generated yet, base weights un-retrofitted
step 100"The history of the city began when James of the Unitedistic 404-40-100013034 is a wall of the the first"
step 300"The history of the city began when the first public or public property of the city and the city was to be the city"
step 499"The history of the city began when the area was a planned " 33st " and a city " block in the square and in 1800 it was built"

Real words, real (if shaky) English syntax, by the end — capitalization, punctuation, plausible nouns for "history of a city" (planned, block, square, built, 1800). Not fluent. 500 steps on a CPU, fine-tuning 3.4% of the parameters, was never going to produce a chatbot.

❗ One honest miss, stated plainly: in webLTM's original addition task, more loop iterations clearly helped on harder problems. Here they don't — held-out loss is lowest at r=2 and gets steadily worse through r=10 and r=12, the opposite of the finding this whole project set out to demonstrate. The likely reason: 500 steps fine-tuning only one layer isn't enough to teach it to do useful new work on iterations 3–12 — it mostly learned to be a good single pass and a mediocre one when forced to loop further. A real budget (more steps, more of the stack unfrozen) would very plausibly close this gap; this run didn't have one. The architecture is real. The result training bought in 26 minutes on a CPU is a starting point, not a finished model.

03

Run it yourself

The model and tokenizer load from Hugging Face and run entirely in this tab via onnxruntime-web (WASM, no GPU needed) — set how many times the core layer loops, and watch it generate.

Slow by design. No KV-cache means every token recomputes the full sequence through ~20–32 transformer-layer passes, in WASM, single-threaded (GitHub Pages doesn't enable the cross-origin isolation multi-threading needs). Expect several seconds per token, and a ~350MB one-time model download. That cost — and the honest quality miss above — is the real price of "real" versus webLTM's 597KB toy next door.
6