DIFFUSION! Switching languages by adding and removing noise
How the EN/中 switch on this site became a forward-and-reverse diffusion animation, built in one evening across a Codex session and a Claude Code session.
Click the EN/中 switch at the top of this page. The page dissolves into noise, then the noise clears into the other language. I call it DIFFUSION! This post covers where the idea came from and how it was built in one evening. Codex built most of it, and Claude Code took over when Codex reached its usage limit.
The idea
Diffusion models are one of my research topics. A diffusion model is built on two processes. The forward process adds Gaussian noise to an image step by step until only noise is left:
where is Gaussian noise. The reverse process starts from pure noise and removes it step by step, guided by a condition such as a text prompt.
A language switch fits this picture well. Run the forward process on the English page. At pure noise, the page no longer has a language. Then run the reverse process with “Chinese” as the condition, and the Chinese page appears. The two halves are symmetric, and the language changes at the moment when nothing is visible.
To be clear, there is no neural network here. The animation is built from a CSS blur, a noise canvas, and a carefully timed page swap. Only the shape of the animation follows the diffusion process.
Codex: from a blur to diffusion
The work started in the same Codex session where I had added comments and visitor statistics. The language switch went through seven rounds in about an hour.
1. Keep my place. First, I asked for the switch to keep my reading position. English and Chinese text have different lengths, so the same scroll offset would land somewhere else. Codex anchored the position to the card or section I was reading, plus how far into it I was. An open bookshelf also stays open.
2. A blur. Next, I asked for a softer transition: blur the page, then bring it back into focus. Codex made a half-second version with 150 ms of blur, the page swap, and 320 ms of sharpening. It worked, but it felt more like a loading effect.
3. Real noise. Then I described what I actually wanted. The page should blur until it is almost pure Gaussian noise, then denoise into the other language. Both directions should look the same, and the header should dissolve with the page. I also suggested looking at how Apple animates iOS and macOS. Codex added a full-screen layer with a 48 px backdrop blur and a canvas of Gaussian noise. Each half takes 650 ms, and the page is swapped at the midpoint, under fully opaque noise.
4. One timeline. In that version, the switch’s thumb slid across first and the noise started afterward. I wanted both on one timeline. The thumb should reach the middle of the switch exactly when the page is pure noise, and reach the other end exactly when the page is clear.
5. Nonlinear noise. Next, I noticed that the noise grew too evenly. The page blurred while the thumb was still moving, so the motion was hard to see. I asked for noise that builds slowly at first and quickly near the midpoint, then reverses in mirror image on the way out. Codex gave the noise its own cubic curve. For the first half, with as progress toward the midpoint, the noise opacity is
and for the second half it is . Halfway to the midpoint, the noise is only , so the thumb’s motion stays visible. The thumb follows a separate curve and passes the midpoint without stopping.
6. No reload. Two problems remained. The switch actually reloaded the whole page, and Safari picked up the noise colors and tinted its tab bar. Codex moved the switch to Astro’s client-side router, which swaps the new language into the same document while the noise keeps playing. Components such as comments, the bookshelf, and the Dota card now re-initialize after each swap. Codex also fixed the page and overlay backgrounds to white, which reduces how much Safari’s bar color changes.
7. The first click. Finally, I noticed a small stutter on the first switch after a page load. Codex found two jobs running at click time: setting up the noise canvas and fetching the other language. It moved both into idle time before the click. Then it reached its usage limit before it could finish testing.
Claude Code: finishing the test, then white noise
I continued in Claude Code, which first re-read the project to catch up on Codex’s changes. This was the reverse of my previous post, where Codex took over from Claude Code.
Finishing the test. Claude Code measured the first switch on the live site right after fresh page loads. On desktop and phone widths, in both directions, it found no dropped frames. The only gap was 26–50 ms at the midpoint swap. That pause is hard to see: the screen is fully covered by noise, and the noise only changes every 42 ms anyway. Along the way, Claude Code found that its own automated clicks caused 130 ms hitches, so it switched to clicks triggered inside the page.
White noise. With the transition working, I noticed that the noise looked dark gray against my white site. The cause was the distribution. Gray levels were drawn from , so half the pixels were darker than middle gray. Claude Code showed me four options, from the original mid-gray to almost white, and I chose the faintest: . At the midpoint, the page now turns into a sheet of fine, near-white grain.
Sharp on 4K. On my 4K monitor, the grain looked soft. The canvas had been capped at 960 px on its long side and then stretched, so each grain covered 4×4 physical pixels and was smoothed. Claude Code sized the canvas in physical pixels, capped at 4096² pixels in total, and turned off smoothing. Now each grain is exactly one physical pixel on a 4K display. A frame takes about 5–7 ms to draw, well within the 42 ms budget at 24 frames per second.
How the noise is made
Fresh Gaussian noise across a 4K screen, 24 times a second, sounds expensive. The trick is to split the work:
- Once per page: Box–Muller sampling fills a lookup table with 4,096 values from . This runs while the browser is idle, or as soon as the pointer reaches the switch.
- Every frame, only during a transition: a fast xorshift random number generator draws values from the table to fill a 512×512 tile. The GPU repeats the tile across the screen at a new random offset each frame, so the seams never line up.
- A static fallback: a small pre-generated tile sits under the canvas as a CSS background, in case the canvas has not painted yet.
When the language is not switching, none of this runs.
Looking back
The idea fit in one sentence. Making it feel right took a dozen rounds of feedback, and almost every round came from watching the result and noticing one specific problem: the thumb stopped too early, the noise was too dark, or the grain was blurry on 4K. Both tools were good at turning those notes into changes and then measuring the result, rather than just reporting that the work was done.
The handoff worked in both directions, too. Codex built the diffusion, and Claude Code finished it after Codex reached its limit.
Try it with the EN/中 switch at the top of any page.
Comments
Nickname and comment are required. Email and website are optional. Leave an email to receive reply notifications; it is never displayed publicly. Comments are reviewed before publication and shared between both languages.