Turn your JavaScript into a neural net — no training required: a hands-on guide to Handwired
Cheng Lou’s Handwired turns the JavaScript you already write — branches, loops, state — into a neural net with hand-written weights: exact, no training, yet fully differentiable. MIT-licensed and starred about 1,500 times in its first two days, it ships a 100,000-document GPU search demo, a bit-exact toggle-switch template, and a photo-layout network you can train. This guide clones it, runs the demos, decodes the core trick, and scaffolds your first numeric program.
bun install, bun start, and you get a 100,000-document GPU search demo, a toggle switch proven bit-exact against plain JavaScript, and a trained photo-layout network.- Handwired is chenglou/handwired on GitHub: MIT-licensed, published October 15, 2026, and starred about 1,500 times within its first two days.
- It turns if-else, loops, and state into a fixed list of FP32 operations — weighted sums, products, and ReLU/ReGLU gates — collected into one giant matrix multiplication that runs on plain JS or WebGPU.
- No training is involved by default: the weights are written by hand, so the program is exact — but the program stays differentiable, so you can unfreeze the weights and train them (the learned-gallery demo trains 244 coefficients with Adam).
- The JSON-search demo searches 100,000 generated JSON documents at each keystroke on the GPU and shows its time next to native RegExp’s; without WebGPU both programs run as generated JS in a second worker. Its suite is 184 tests in 10 files, about 45 seconds, no GPU.
- The template/ toggle switch is verified bit-exact:
bun test templateruns the numeric program and a plain-JS reference over a scripted trace and compares them every frame. - Constraints are real: every cell declares a finite range and evaluations throw when a value escapes it; the 100k-document page needs 410 MB of GPU memory; narrow windows get 20,000 documents.
On October 15, 2026, Cheng Lou (@chenglou) pushed a repository that reads like a dare: Handwired. Two days later it had about 1,500 GitHub stars, an MIT license, and one of the strangest ideas in recent open source: take ordinary JavaScript — if-else, loops, state — and turn it into a neural network. No training. Exact. And yet fully differentiable.
The trick is disarmingly simple. A program is made of branches, loops, and state, and every one of those can be rewritten as arithmetic. The README’s opening example compiles this:
const a = 5
const result = (a > 4) ? 10 : 12Into this:
const a = 5
// Math.max(0, x) is ReLU. For an integer a, above4 is 1 if a > 4, and 0 otherwise
const above4 = Math.max(0, a - 4) - Math.max(0, a - 5)
// Both branches compute, and above4 picks one
const result = above4 * 10 + (1 - above4) * 12Check it by hand: a - 4 = 1, Math.max(0, 1) = 1; a - 5 = 0, Math.max(0, 0) = 0; so above4 = 1 and result = 10. Both branches compute — there is no branching at runtime — and the gate picks one. Handwired then collects every +, *, and ReLU into one giant matrix multiplication and runs it on plain JS or WebGPU.
Because the whole program uses only these primitives, two things follow that normally don’t coexist: the weights need no training (they’re written by hand), and the full program stays differentiable. That unlocks the repo’s party tricks — unfreezing the weights and training them, attaching a frozen program to a regular net and training end-to-end, and GPU-level parallelism for single-threaded-style JS. Let’s run it.
What you’ll need
- Bun: every command in the repo runs through it (—
bun install,bun start,bun test). - A modern browser: the demos are web pages. WebGPU gets you the GPU paths; without it the demos fall back to CPU in a worker.
- git and about five minutes.
Step 1: Clone it and start the demo site
The README’s entire install story is one line after cloning:
git clone https://github.com/chenglou/handwired.git
cd handwired
bun install
bun startbun start serves the demo site (the start script runs bun scripts/serve-demo-site.ts). Try the live demos first if you’d rather click before you clone. The headline demo is the JSON search page at /json-search/.
Step 2: Search 100,000 documents at every keystroke
Open /json-search/ on the demo site. The page generates 100,000 JSON documents (package manifests, user records, log events — each up to 512 code points) and searches all of them again at each keystroke, on the GPU, with two fixed numeric programs: a JSON highlighter that classifies every character, and a search that counts each document’s pattern matches. One pass runs every document — every document runs the same operations and reads no other document’s values.
The page shows its own time next to native RegExp’s for the same search, measured on one thread. Flip the GPU/CPU toggle to feel the difference: without WebGPU, both programs run in a second worker as generated JavaScript. A GPU round trip alone costs 0.6 ms or more, so the GPU is pointless for one document — it’s for all 100,000 at once. Fair warning from the README: on a narrow window (under 720 px, e.g. a phone) the page drops to 20,000 documents, and 100,000 of them take 410 MB of GPU memory.
The demo is tested like real software:
bun test --timeout 30000 tests/json-search # 184 tests in 10 files, about 45 s, no GPU
bun run check # type-check and lint, about 3 s

Step 3: Open the toggle template — the “numeric program” pattern
Every Handwired app has the same three layers, and template/ is the canonical example: a toggle switch written as one numeric program, checked against the same switch in plain JavaScript. Run it:
bun run start:template # the page, at http://localhost:3000/
bun test template # its test
bunx tsc -p . # the type-check, which prints nothing when it passesThe three layers, all in toggle.ts:
- Cells. Every number the program reads or keeps between evaluations is a cell of a
Float32Arraysource table, declared once with the helpers insrc/runtime/cells.ts:input(written by JS before each evaluation),event(a bit JS sets to 1 on arrival, reset to 0 by the program),state/stateBit(computed by the program and kept),constant, plus numeric niceties likeoneHotandpermutation. - Builder code. Ordinary JavaScript that runs once and appends operations to a
Circuit. Nothing in the program branches or loops at runtime — the builder does all the deciding up front. - The host loop. JavaScript writes browser inputs into the table, runs the program (one evaluation), then writes the next-state values back and the other outputs to the DOM.
toggle-reference.ts is the same switch in plain JavaScript, and toggle.test.ts runs both over a scripted trace and compares them every frame. Note the README’s warning: bun runs TypeScript without checking types, so run the type-check after every change.
Step 4: Prove the program is exact
This is the step that makes Handwired different from every “AI-generated code” demo: the numeric program is tested for bit-exact agreement with a plain-JavaScript reference implementation, frame by frame:
bun test template
bunx tsc -p .The test passing means the hand-written neural net and the ordinary JavaScript compute the same switch. If you’ve ever distrusted a clever optimization, this is the antidote: a reference implementation and a scripted trace, runnable in one command.

Step 5: Scaffold your own numeric program
docs/tutorial.md’s “A new app folder” section gives the recipe: copy template/ to a new folder next to it at the repo root — the sibling placement matters, because the imports from ../src/runtime/ must still resolve — then register the folder in three places, since the repo’s scripts only see the folders they list:
package.json’stestscript (e.g. extendbun test --timeout 30000 ./tests ./template ./labwith./counter),- a serving script next to
start:template(e.g."start:counter": "bun ./counter/index.html"), tsconfig.json— after that,bunx tsc -p .type-checks the new folder with the root project in under a second.
Then write your cells, your builder code, and — following the template’s discipline — a plain-JavaScript reference plus a scripted-trace test that proves the two agree. The other demo folders (src/json-search, src/handwiredos, src/learned-gallery) are the worked examples to steal from; lab/ is old experiments you can ignore.
Step 6: Unfreeze the weights and train (optional)
Here’s the README’s “what does that even do? Let’s find out!” moment. The learned gallery draws a photo layout with a small network whose coefficients are the program’s own source cells: 20 output slots fed through two ReGLU layers of 6 units, 6,320 operations in 8 layers, 244 source cells as the trained weights. Training steps the coefficients with Adam, using gradients from circuit.backpropagateReference, until the rectangles match the target rectangles:
bun start # the demo site, with the page at /learned-gallery/
bun run start:learning # the page alone
bun test tests/learned-gallery # 41 tests, about 20 sOn the page you edit a sketch of up to 12 rectangles — drag to move, drag a corner to resize, click empty space to add one, Delete removes it — starting from three presets: Story (10 rectangles), Cinema (9), Contact sheet (11). It’s the cleanest possible demo of the idea: the “neural net” and the “program” are the same artifact, so training it is just gradient descent on your code.
What you built
You ran a codebase where JavaScript is a neural network: a 100k-document search engine with no training involved, a toggle switch proven bit-exact against its plain-JS twin, a scaffold for your own numeric program, and a photo-layout network trained end-to-end from hand-written weights. The mental model to keep: cells (the numbers), builder code (ordinary JS that emits one fixed op list), and the host (JS that feeds inputs and reads outputs around each evaluation).
Honest limitations
- Ranges are a contract, not a suggestion. Every cell declares a finite range, and an evaluation throws when a value leaves it. Comparisons (
emitCompare) throw for operands without finite ranges. Precision is FP32 with grid promises — a comparison on gridded operands costs 2 operations; ungridded, 15. Same bits, bigger program. - It’s a research toy, not a framework. There’s no package to install into your app; you copy
template/at the repo root. The docs are being rewritten as we speak (the Oct 15 commits are literally “Entry docs: what four readers without any background tripped on”). - GPU memory is real. The 100k-document page needs 410 MB of GPU buffers; small GPUs get fewer documents, phones get 20,000. The CPU fallback exists but a first search runs seconds slower.
- WebGPU or bust for the GPU demos — an older browser silently gets the CPU worker instead.
Frequently asked questions
Do I need a GPU to run Handwired?
No. The GPU demos need WebGPU; without it — or with the page’s CPU toggle — both numeric programs run in a second worker as generated JavaScript. The full JSON-search test suite (184 tests) runs with no GPU at all.
Is anything in Handwired trained?
By default, nothing: the weights are written by hand in the builder code, which is why the program is exact. Training is opt-in — the learned-gallery demo unfreezes 244 coefficients and trains them with Adam, via gradients from circuit.backpropagateReference, until the rectangles match their targets.
What JavaScript can it actually convert?
The building blocks are if-else, loops, and state, expressed through declared cells (inputs, events, state, constants) and builder code that emits one fixed operation list. Every cell needs a finite declared range — an evaluation throws if a value leaves it — and comparisons need finite ranges to be emitted.