BLOG

handwired:把普通 JS 代码变成神经网络,4 天 1730 星

Kael Zhang
技术拆解handwired可微编程WebGPUTypeScript开源项目
广告 · Advertisement

10 月 15 日,开发者 chenglou 在 GitHub 创建了 handwired 仓库,README 第一句话是「Turn your regular JavaScript code into a neural net; no training, exact, yet also fully differentiable」:把普通 JavaScript 代码变成神经网络,不用训练,结果精确,还全程可微。截至 10 月 19 日,仓库 1730★、143 fork、open issues 9 个,4 天冲上千星,唯一贡献者 chenglou 提交了 825 次。MIT 许可,TypeScript,bun 构建,在线 demo 挂在 chenglou.me/handwired,克隆下来 bun install && bun start 就能本地跑。有意思的是传播路径:同期 HN 两次提交分别只有 5▲ 和 4▲,热度几乎全部来自 GitHub 一侧。本文按老规矩拆六段。

一、它是谁

作者 chenglou,HN karma 1295,个人简介只留了 GitHub 和 Twitter 链接。社区资料显示他是 React 生态的知名开发者(React Motion 作者、ReasonML 推广者、曾任职 Meta),这部分背景本次未独立核验,正文只按「开发者 chenglou」处理。

项目本身有个时间差要交代:仓库创建于 10 月 15 日,但 lab 目录里的实验记录最早写于 9 月初,还提到 6、7 月的设计讨论,9 月中旬有逐日更新的实验日志,说明开发远早于公开。4 天 825 commits 是公开仓库的提交节奏,不是项目的全部工期,把它理解成「长期准备后一次性公开加持续整理」更贴近证据。

本地 clone 实测(浅克隆,10-19):全仓 1542 个文件,其中 ts 文件 1101 个、合计约 27.5 万行;src 核心目录约 1.8 万行,lab 实验目录约 23.5 万行,tests 约 2.2 万行,docs 六份文档合计 1296 行。文件类型分布是 ts 1101、json 151、md 141、txt 49、html 38、css 22。这不是一个 gist 级别的想法演示,是一个带文档、带测试、带教程、带在线站点的完整工程。

二、机制拆解

handwired 的出发点:程序由 if-else、循环和状态构成,它把这三样东西变换成「矩阵乘法加 reglu」在浮点数上执行。

README 给的例子最直观。a > 4 ? 10 : 12 这样的分支,可以改写成纯算术:先算 above4 = max(0, a-4) - max(0, a-5),max(0, x) 就是 ReLU,对整数 a,above4 在 a>4 时等于 1、否则等于 0;再算 result = above4*10 + (1-above4)*12。两个分支都计算,由 above4 选出其中一个。比较、选择这些控制流原语,就这样被编码成了加减乘和 ReLU。循环和状态同理:状态放进一张 Float32Array 源表,每轮求值读表、算、写回。

下一步是收集:把全部加法、乘法和 relu 收进一个巨大的 matmul,在 JS 或 WebGPU 上执行。因为基本只用这两个原语,README 给出三条性质:整个程序保持可微;权重是手写的,不需要训练;GPU 的成熟优化技术(fusion、sparsity、reordering)对这种极简原语特别好使。

docs/execution-model.md 里的定义比 README 严格得多。程序是一串定长指令,指令只有四种:affine(仿射加权和)、reglu(left * max(0, gate),gate 为常数 1 时退化成 ReLU)、boolean(把 0..1 的分数硬化成精确的 0 或 1)、index(表读取,即按 floor(下标) 从候选表取值)。所有数值语义写到 FP32 的舍入顺序级别,仿射项按存储顺序逐项 fround,文档明确要求任何后端要和参考求值器逐位一致。

比较也不只靠 ReLU 编码这一招。emitCompare 在声明了整数网格的操作数上直接产出精确位:1/64 网格上的 a>=b 只要 2 个操作、2 层;没有网格声明时走符号级联,0..16,384 范围内的 a>=b 要 15 个操作、14 层。索引读取有专门的 lowering,任意表大小下 1 个操作完成,索引范围限制在 ±2^24 以内。你写的不是指令表,而是「builder 代码」:普通 JavaScript 在构建期运行一次,把 emitMultiply、emitCompare 这些宏展开成指令,运行期没有任何分支和循环。

可微性也是结构性的,不是补丁。梯度可以顺着这四条指令往回流:affine 的梯度就是系数,reglu 的梯度带上 ReLU 门,boolean 这类硬化操作在文档里被标成「rounding point」,后端可以对其它求和自由重排、融合,唯独这些点要保证语义。对索引求梯度时,调用方必须显式选择 zero 或 backward-difference 两种策略之一,否则直接抛错:设计上宁可报错也不偷偷给你一个没定义的梯度。这套约定让「整个程序可微」从口号变成了有边界的合同。

再往上一层是时间与动画。host-timing.ts 把弹簧动画离散成 4ms 步长,按刚度和阻尼组合预计算一个 2×2 步进矩阵,宿主每帧把矩阵写进 4 个输入 cell,程序内用 emitApplySpringMatrix 应用。也就是说连 UI 动画这种最不像神经网络的东西,也被编译进了同一张数值表。

三、工程成色

src 分四块:runtime 是核心运行时,cells、host-timing、numeric-ui-core、prefix-networks 四个文件;handwiredos 是一个完整桌面演示,窗口管理器、Photos、Notes、Files 全部编译成数值程序,约 5500 行;json-search 约 6200 行,是搜索演示;learned-gallery 是学习类演示。tests 目录 42 个测试文件,覆盖比较语义、gather lowering、数值语义、弹簧矩阵等,json-search 一个模块就带 184 个测试、跑约 45 秒。template 目录放了一个开关按钮的教学示例,docs/tutorial.md 逐行讲解,旁边配一份普通 JS 写的参考实现,测试钉死两者输出一致。docs 里还有 patterns.md(484 行)、behavior-choices.md、execution-model.md 等六份文档,工程习惯相当规整。

HandwiredOS 这个演示值得多说两句,它是全仓库最有说服力的展示。窗口拖拽、最大化、点击选中照片、文本换行、虚拟滚动列表,这些交互全部不出现在宿主 JS 里,而是编译进数值程序:窗口命中测试用 emitCompare 在 1/64 像素网格上算,指针坐标先舍入到网格再进程序,所以每个比较都是精确整数位。Files 窗口里那个虚拟列表干脆是程序自身的值列表,64 个 DOM 槽位由程序分配行。宿主只负责把浏览器事件写进源表、跑一次求值、把输出写回 DOM。换句话说,一个能用的桌面系统,运行时本体就是一张浮点数表上的矩阵运算。

性能数字记录在 lab/status.md,细到微秒级。HandwiredOS 整个桌面编译成 20,381 个活跃操作、230 层、45,436 个存储权重,单次求值约 133µs,空闲帧约 141µs,打字帧约 282µs。json-search 的 README 自己承认 GPU 单次往返就要 0.6ms 以上,单文档毫无收益,收益只在 10 万文档同构并行这一种场景:每份文档跑同样的操作、不读别人的值,一遍全过。

README 自述的用例(作者口径)有三个:解冻权重直接训练,作者原话风格是「那会发生什么?试了才知道」;把冻结的特定程序接上通用神经网络端到端训练,「nanoGPT 能同时学莎士比亚和你的 UI?」;以及传统单线程写法的 JS 获得 GPU 级并行,JSON 搜索在 10 万文档上毫秒级完成。三个用例都用问号收尾,实验姿态写在脸上。

提交节奏本身是个观察点:单人 4 天 825 次提交。作者没有自称任何生成方式,但 lab 里 9 月 3 日的复盘笔记写明,作者梳理过「6 月和 7 月的 Claude 对话」以及「6 月的 Codex 设计讨论」,复盘还请了三组独立审计。材料摆在这,判断留给读者。

还有一点诚实度证据:那份 9 月复盘笔记里写着「最令人担忧的发现也很具体:一些正确性检查在比较共享同一错误的实现」。项目主动把自己测试体系的漏洞记录在案,而不是等别人挖出来,这在明星仓库里不常见。

四、与同类对比

概念上,handwired 属于「可微编程」这个老方向:让普通程序的控制流以可微的数值形式存在,和神经符号系统的思路沾亲。但它的路线有自己的位置:不是训练一个网络去逼近程序,而是把手写程序精确地编译成神经网络形态,权重不是训练的产物,权重就是程序本身。可微解释器类工作通常要为可微性牺牲精确性,用软化替代分支;handwired 反过来,先用范围声明、整数网格、符号级联这些手段把精确性钉死,再顺手收获可微性。「免训练、精确、可微」三个词同时成立,靠的正是这个编译方向的选择。这一对照只作概念层面的定位,不涉具体项目的优劣断言。

五、局限

三条。其一,数值边界:用 ReLU 编码整数比较依赖操作数落在声明的网格和范围内,仓库用范围声明加精确位来兜底,但这意味着写程序的人要自己维护数值契约,浮点世界里超出范围的输入没有魔法,索引读取超出 ±2^24 直接拒绝。其二,规模膨胀:分支两边都算,操作数随程序复杂度增长,status.md 里有现成例证,Notes 应用迁入后 HandwiredOS 从 14,019 个活跃操作涨到 20,381 个,存储权重从 31,359 涨到 45,436,单次求值从约 75µs 涨到约 133µs。一个玩具桌面已经两万操作,真实应用规模可想而知。其三,实用性:作者自己就把项目定性为实验,README 写着「what does that even do? Let’s find out!」,这是一手证据,不用替他拔高。

六、判断与谁能用

先看热度结构:HN 两帖合计 9 个赞,GitHub 4 天 1730★,流量从 trending 和社交媒体进场,技术论坛几乎没接住。这个反差本身说明,多数人是被「代码变神经网络」这个概念钩住的,点星成本远低于读完 execution-model.md 的成本。star 数在这里度量的是话题性,不是采用度。

分人群给判断。想理解可微编程的人,这是目前教材级的入口:读 docs 六份文档,跑 template 的开关示例,bun 一条命令起本地 demo,半天成本,比读论文直观。做 GPU 数值计算或编译器的人,可以偷师它的 macro 设计,emitCompare、index read 这些宏如何在构建期展开成极简指令、如何用 rounding point 标记保留重排自由度,是可以直接借鉴的手法。找生产工具的人,现在别来:open issues 9 个,接口随时会变,作者的定位就是实验。

研究玩具还是范式信号,4 天定不了论。但可以确定的是,「程序即网络」这个编译目标,被一个人用 27 万行 TypeScript 做到了能跑桌面系统、能毫秒搜 10 万文档的程度,文档和测试的完备度对得起 star 数。这件事本身已经值得一次拆解。

信息来源

  • 仓库本体:github.com/chenglou/handwired(README、docs/execution-model.md、docs/tutorial.md、docs/patterns.md、template/、src/json-search/README.md、lab/status.md、lab/reassessment-2026-09-03.md,10-19 本地 clone 核验;行数与文件数为本地 find/wc 统计)
  • 星数、fork、创建时间、贡献者与提交数:GitHub API(10-19 快照,星数仍在变动)
  • HN 提交记录:id=50105849(5▲)、id=50105735(4▲),10-19 快照
  • 作者背景中未经核验的部分,正文已标「社区资料显示」
广告 · Advertisement

常见问题

handwired 是什么?

一个把普通 JavaScript 代码变成神经网络的开源工具(TypeScript,MIT 许可)。程序里的 if-else、循环、状态被变换成矩阵乘法加 reglu 在浮点数上执行,可在 JS 或 WebGPU 上跑。卖点是免训练、精确、全可微。

它怎么用代码表达比较运算?

用 ReLU 编码整数比较:例如 a>4 可写成 max(0,a-4)-max(0,a-5),结果在 a>4 时为 1、否则为 0;分支选择变成两个分支都计算再用该值加权。代价是程序规模膨胀与浮点边界问题。

适合生产使用吗?

作者自己在 README 里都说「试了才知道」,定位是实验项目。目前更适合研究可微编程、GPU 并行化传统程序的人群;冻结程序接神经网络端到端训练等用法仍在探索阶段。