DIFFUSION!用加噪和去噪来切换语言
网站右上角的 EN/中 切换,怎样在一个晚上里变成一段正向加噪、逆向去噪的扩散动画:先在 Codex 里做,再由 Claude Code 接着完成。
点一下页面右上角的 EN/中。页面会先化作一片噪声,再从噪声里还原成另一种语言。我管它叫 DIFFUSION! 这篇记录这个想法从哪来,以及它在一个晚上里是怎么做出来的:大部分由 Codex 完成,Codex 额度用完以后,再由 Claude Code 接着做完。
想法
扩散模型是我的研究方向之一。扩散模型建立在两个过程上。正向过程一步步往图像里加高斯噪声,直到只剩噪声:
其中 是高斯噪声。逆向过程从纯噪声出发,在文本提示这类条件的引导下一步步去噪。
切换语言正好可以套进这个过程:对英文页面跑正向过程,到纯噪声时,页面已经不带任何语言;再以“中文”为条件跑逆向过程,中文页面就出现了。前后两半是对称的,语言恰好在什么都看不见的那一刻完成替换。
需要说明的是,这里并没有神经网络。动画由 CSS 模糊、一张噪声画布和时机精确的换页组成,只是在形态上模仿了扩散过程。
Codex:从模糊到扩散
这部分是在同一个 Codex 会话里做的,评论功能和访客统计也是在这个会话里加的。语言切换在大约一小时里改了七轮。
1. 保持阅读位置。 我先要求切换语言后停在原来读到的地方。中英文篇幅不同,同样的滚动距离会落到别处。Codex 改成按我正在读的卡片或章节定位,再记住在其中读到了多少。书架如果展开着,切换后也保持展开。
2. 一层模糊。 接着我想让过渡柔和一点:先高斯模糊,再变清晰。Codex 做了一个半秒的版本:150 毫秒模糊、换页、320 毫秒变清晰。能用,但更像是加载效果。
3. 真正的噪声。 然后我说清楚了真正想要的效果:页面要模糊到几乎全是高斯噪声,再去噪成另一种语言;中译英和英译中效果一样,顶部菜单栏也要一起模糊。我还建议它参考 iOS 和 macOS 里的动画。Codex 加了一层全屏覆盖层:48 px 的背景模糊,加一张高斯噪声画布。每个半程 650 毫秒,在中点、噪声完全不透明时换页。
4. 同一条时间线。 那一版里,语言按钮的滑块先滑过去,噪声随后才开始。我希望两者在同一条时间线上:滑块到达中点时,页面正好是纯噪声;滑到另一端时,页面正好完全清晰。
5. 非线性加噪。 接着我发现噪声加得太匀速:滑块还在动,页面就已经模糊了,几乎看不清它在移动。我希望靠近中点的前半段噪声加得少、加得慢,后半段加得多、加得快,离开中点时反过来,前后对称。Codex 给噪声单独设了一条三次曲线。前半程里,记 为走向中点的进度,噪声不透明度为
后半程为 。走到一半时噪声只有 ,滑块的移动还看得清。滑块用另一条曲线,经过中点时不会停顿。
6. 不再刷新。 还剩两个问题:切换时其实整页重新加载了;Safari 还会从噪声画面取色,让标签栏跟着变色。Codex 把语言切换改用 Astro 的客户端路由,在同一个页面里替换译文,噪声动画不中断。评论、书架、Dota 卡片等组件在每次换页后重新初始化。它还把页面和覆盖层的背景固定成白色,减少 Safari 标签栏的变色。
7. 第一次点击。 最后,我发现页面加载后第一次切换有一点卡顿。Codex 查到点击时有两件事在做:初始化噪声画布,和请求另一种语言的页面。它把这两件事都挪到点击之前的空闲时间。还没来得及测完,Codex 的额度就用完了。
Claude Code:补完测试,再换成白噪声
我换到 Claude Code 继续。它先重新读取项目,了解 Codex 做了哪些改动。这和上一篇正好反过来:那次是 Codex 接了 Claude Code 的班。
补完测试。 Claude Code 在线上测量了刚加载完页面后的第一次切换:桌面和手机宽度、两个方向,都没有掉帧。唯一的空档是中点换页时的 26–50 毫秒。这时屏幕被噪声完全盖住,噪声本身也是每 42 毫秒才换一帧,所以几乎看不出来。测试中它还发现,自己的自动化点击会带来 130 毫秒的卡顿,于是改成在页面内部触发点击来测量。
白色噪声。 效果跑通以后,我发现噪声在白色网站上显得发灰发黑。原因在分布:灰度取自 ,一半像素比中灰还暗。Claude Code 给了四种方案,从原来的中灰到几乎全白,我选了最淡的 。现在到中点时,页面会变成一片细密、接近白色的颗粒。
4K 下点对点。 在我的 4K 显示器上,颗粒看起来有点糊。原来画布的最长边被限制在 960 像素,再拉伸铺满屏幕,每个颗粒占 4×4 个物理像素,还被平滑插值。Claude Code 把画布改成按物理像素分配,总像素封顶 4096²,并关闭了平滑。现在在 4K 屏上,一个颗粒正好对应一个物理像素。每帧绘制约 5–7 毫秒,在每秒 24 帧、每帧 42 毫秒的预算内绰绰有余。
噪声是怎么生成的
每秒 24 次在整个 4K 屏幕上生成新的高斯噪声,听起来开销很大。办法是把工作拆开:
- 每个页面一次: 用 Box–Muller 方法生成 4096 个服从 的灰度值,存成查找表。这一步在浏览器空闲时完成;鼠标移到切换按钮上时也会提前触发。
- 每一帧,只在切换期间: 用很快的 xorshift 伪随机数生成器从查找表里取值,填满一张 512×512 的图块。GPU 把图块平铺到整个屏幕,每帧随机平移,接缝不会对齐。
- 静态兜底: 画布下面垫着一张预先生成的小噪声图,作为 CSS 背景,以防画布还没画出来。
不切换语言时,这些都不会运行。
回头看
想法一句话就能说完,做到感觉对却花了十几轮反馈。几乎每一轮都来自看着效果、发现一个具体问题:滑块停得太早,噪声太暗,4K 下颗粒发糊。两个工具都很擅长把这些意见变成改动,再测量结果,而不只是告诉我“已完成”。
这次接力也是双向的:Codex 搭起了扩散动画,额度用完后由 Claude Code 收尾。
想试试的话,点任意页面右上角的 EN/中 就行。
评论
昵称和评论内容为必填;邮箱和 Website 为选填。填写邮箱可接收回复通知,邮箱不会公开显示。评论审核通过后公开,中英文文章共享评论。