Alloxyme代码 / 光
alloxy.me 首屏:由代码字符颗粒组成的照片

全部文章/ 2026年10月28日

在浏览器里搭一间暗房

这个网站上没有一张“直接显示”的照片。它们都由字符颗粒拼成,在红光下浮现,再显影成彩色。

日期
2026-10-28
阅读
6 分钟
标签
WebGL · three.js · Design

这个网站没有一张“直接显示”的照片。你看到的每一张图,都由几万个字符颗粒拼成:它们先在安全灯下以红色潜影出现,再像相纸在显影液里那样,一点一点变成彩色。这篇记录它是怎么做的。

颗粒是字符

页面背后只有一个固定的 WebGL 画布,上面铺着一张网格:桌面端每 3.6 像素一个点,大约十万个。每个点在着色器里从照片取色,同时记着自己在一段代码里的位置——首页用的是一行“显影”的代码。显影之前,点被画成那个字符;显影之后,它变成一颗圆形的银盐颗粒。

float shape = mix(glyph, disc, vDev);vec3 latent = uSafe * (0.18 + vLuma * 1.25);gl_FragColor = vec4(mix(latent, vColor, vDev), shape * vAlpha);

暗部先显影

真实的相纸在显影液里,最先出现的是阴影:银盐最密的地方先变黑。所以每个颗粒的显影进度都减去了它自己的亮度,再加一点随机。画面从暗处开始浮出来,而不是整张一起淡入。

float dev = clamp((uDevelop * 1.7 - luma * 0.5 - aRand * 0.35) / 0.85, 0.0, 1.0);

药水是一个波动方程

鼠标划过时的涟漪,是在一张四分之一分辨率的浮点纹理上解二维波动方程:每一帧,新的高度等于四个邻居之和的一半,减去上一帧的高度,再乘一点阻尼。颗粒按液面的坡度偏移,看起来就像隔着一层晃动的药水看照片。没人碰的时候,它也会每隔几秒自己滴下一滴。

float h = (sum * 0.5 - s.g) * 0.982;h += uDrop.z * exp(-dot(d, d) / (uRadius * uRadius));

反相与转场

影像区用的不是 WebGL,而是 CSS:invert() 把照片反相,再叠一层安全灯的红色。离屏幕中心越近,反相越少,滑到正中间时就显影出原本的颜色。页面之间的转场是两条电影遮幅黑条,中间留一道安全灯的红缝。

克制

所有的花样都放在画面里,文字反而很小:界面 11 像素,正文 15 像素。慢的设备会被自动降级:前 60 帧平均超过 24 毫秒,颗粒就变少、变大;系统开了“减弱动效”,所有位移都会关掉,内容依然完整。

J下一篇 · 15 篇 ↗用 C 和 EasyX 写植物大战僵尸一份课程设计的完整记录:数据结构、登录页、动画、碰撞判定、内存释放,一直到积分存档和 Release 编译。

10联系

有项目,或者只是想聊聊某支镜头

hello@alloxy.me

北京时间 --:--:--

图片与视频来源
  • Travel photos taken by Alloxyme on trips (photography is a hobby)
  • Project screenshots: Aivory, FlowMuse and Plants vs. Zombies are the author’s own projects
  • “Photographic lenses and tubes 2025” by Carlos Valenzuela Montuy, CC BY 4.0, Wikimedia Commons

© 2026 Alloxyme — 代码与文字保留所有权利