纸上的一方池塘A Pond on Paper
Translated from the Chinese original by AI. Some nuance may be lost — .
昨天把站搭起来了,今天聊聊主页上藏着的那些东西。
说来好笑,起因是期末的 Web 大作业,要求里写着「至少一个交互元素」。我想,既然要做,那就做点好玩、又和这个站气质相融洽的东西。于是又拉着小克(Claude)折腾了一整天。
主页上的小机关
如果你是用电脑打开的主页,把鼠标在页面上晃一晃;页面,是我们的「纸面」。
灯。 鼠标所到之处,纸面上会亮起一团暖光,像提着一盏灯在纸上走。灯照到我的名字时,字会从深褐变成暖暖的琥珀色。另外,纸面的空白处藏着几行手写的体字,只在灯光扫过时浮现。
池塘。 这张「纸」其实是一方静水。水面上漂着一些淡淡的线条图形,正向四面八方缓慢地漂;漂出去的被回收,新的又慢慢一个个漂进来。
这些图形不是随手乱画的。小克给它们写了个生成器,每个图形都出自一个「母题」:中点相连的套方、等轴测的线框立方体、按黄金分割切开的矩形、一层层缩小又旋转的同心多边形……图形内部的角度都在 15° 的倍数上,比例只用 1、0.618 和 ½,辅助线比轮廓线更淡;这使得它们看着复杂,却不凌乱。
涟漪。 在空白处点一下(手机则轻点屏幕),一圈涟漪会慢慢荡开。涟漪经过的地方,那些直线会被折弯、打碎,等水面平静下来,又重新合拢。
为什么一定要用直线、不用曲线?因为曲线被水波一扭区,根本看不出变化;直线一旦被打断,那种破碎感才明显。为了找到我心里想要的效果,我和小克磨了好几版。
还有个细节:我的名字和那三个手写标签,其实是「浮」在水面上的。涟漪扩散到哪个字,哪个字就轻轻浮起、前倾,再落回原处。你也可以玩玩看。
这个站的设计理念
一句话概括:克制、温暖、有质感。
- 颜色:米黄色的纸,深褐色的墨,外加一点点陶土色,只用在链接下划线、引用线这几个地方。点缀不宜多而乱杂。
- 字体:每种字体只服务一件事。Cormorant 只负责大字,比如名字和标题;英文正文用 Newsreader,中文正文用思源宋体,界面上的小字用 Inter 和思源黑体,手写标签用 Shadows Into Light。
- 双语:中英两种语言放在同一个网址里,右上角「中 / EN」随时切换,不用刷新,也不会先闪一下另一种语言。第一次打开时,网站会看你浏览器的首选语言:是中文(不管简繁、哪个地区)则显示中文,否则显示英文。
- 昼夜:主题有「跟随系统 / 夜 / 日」三档。一个意外之喜:Calendar 页里嵌着的 Calendar,如果它自己设成「跟随系统」,也会跟着主站一起变色。
- 动效:所有动画都是慢的、轻的。如果你的系统开了「减少动态效果」,这些动画会自动停下来。
看不见的优化
好看之外,我更在意的是「不卡」,尤其是国内访问。
字体。 最开始字体是从 Google Fonts 加载的,但在国内,加载的效果不好。现在字体全部都放在自己的域名下,由 Cloudflare 分发;中文字体切成了一百来个小分片,浏览器只下载当前页面用得到的那几片。原本必须先加载完才能显示页面的 CSS,从 84 KB 压到了 5 KB。
池塘。 背景是小克用 WebGL 画的,分两步:先把所有几何图形的边画到一张图上,再由一个全屏的着色器「隔着水」去读这张图。所以图形再多,每个像素也只要读一次。涟漪是用公式直接算的,不做物理模拟,大概长这样:
float x = dist - age * SPEED; // 离波前有多远
float env = exp(-x * x / (w * w)); // 波包:只有一两圈
float amp = strength * exp(-age * DECAY);
slope += dir * amp * env * k * cos(k * x);
其他的一些小讲究:
- 涟漪最多同时存在 4 圈,旧的会被新的顶掉,连续狂点也只会每 0.45 秒算一次;
- 没有涟漪的时候,背景每秒只画 30 帧,页面切到后台就完全暂停;
- 鼠标移动时,不再重绘任何全屏图层:灯光直接画进 WebGL,藏字的「窗口」只做位移,交给 GPU 去合成;
- 整套交互的 JS,压缩后只有 6 KB 左右,没有引入任何库。
在 Edge 和 Chrome 上,它丝滑得让我很满意。Firefox 上略卡的话,唉……不怪我(也不怪小克)。
无障碍。 这也算作业的一部分:页面结构都用了对应的语义标签,可以用键盘完整操作;读屏软件读我的名字时,会读出完整的「Earzu Chan」,而不是一个字母一个字母地念。小克用 axe 把全站检查了一遍,没有任何问题。
最后
这些机关,大概率没几个人会注意到。不过做的时候我挺开心的,这种微妙的交互,给人一种无聊日子里的惊喜感。
如果你找到了灯下那几行字,可以写信告诉我,你看到的是哪一句。你喜欢那句话吗?
Yesterday I got the site up. Today let’s talk about the things hidden on the homepage.
Funny to say, it started with the final Web homework — the requirements said “at least one interactive element.” I figured if I was going to do it, I might as well make something fun, something that matches the temperament of this site. So I dragged Claude along for another whole day of tinkering.
The little mechanisms on the homepage
If you’re on a computer, move your mouse around the homepage; the page is our “paper.”
The lamp. Wherever the mouse goes, a warm glow lights up on the paper, like carrying a lantern across the page. When the light reaches my name, the letters shift from deep brown to a warm amber. Also, a few lines of handwriting hide in the blank spaces of the paper, surfacing only when the light sweeps over them.
The pond. This “paper” is actually a patch of still water. Faint line drawings drift slowly across its surface in all directions; those that drift out are recycled, and new ones slowly drift in one by one.
These shapes aren’t drawn at random. Claude wrote a generator for them — each shape comes from a “motif”: nested squares joined at their midpoints, an isometric wireframe cube, a rectangle cut by the golden ratio, concentric polygons shrinking and rotating layer by layer… The internal angles are all multiples of 15°, the proportions use only 1, 0.618 and ½, and the guidelines are fainter than the outlines. This makes them look complex without being messy.
The ripples. Click on a blank area (or tap the screen on mobile), and a ring of ripples slowly spreads out. Wherever the ripples pass, those straight lines bend and shatter, then come back together once the water settles.
Why straight lines and not curves? Because when a curve gets twisted by the water, you can’t see the change at all; but once a straight line is broken, that sense of fracture is obvious. To find the effect I had in mind, Claude and I went through several versions.
One more detail: my name and those three handwritten labels are actually “floating” on the water. Wherever the ripples reach a character, it gently lifts and tilts forward, then settles back. You can try it yourself.
The design philosophy of this site
In one sentence: restrained, warm, textured.
- Color: cream-colored paper, deep brown ink, plus a touch of terracotta used only for link underlines and quote rules. Accents shouldn’t be many or messy.
- Typography: each typeface serves exactly one purpose. Cormorant handles only large text — names and headings; Newsreader for English body text, Source Han Serif for Chinese body text, Inter and Source Han Sans for small UI text, Shadows Into Light for handwritten labels.
- Bilingual: Chinese and English live under the same URL, switchable anytime via “中 / EN” in the top right — no refresh, and no flash of the other language first. On first visit, the site checks your browser’s preferred language: if it’s Chinese (simplified or traditional, any region), it shows Chinese; otherwise English.
- Day and night: three theme options — “follow system / night / day.” A happy accident: the Calendar embedded in the Calendar page, if it’s set to “follow system,” changes color along with the main site.
- Motion: all animations are slow and gentle. If your system has “reduce motion” enabled, they stop automatically.
Invisible optimizations
Beyond looking good, what I care about more is “no lag” — especially for visitors in China.
Fonts. At first the fonts loaded from Google Fonts, but in China the loading wasn’t good. Now all fonts are hosted on my own domain and distributed by Cloudflare; the Chinese typeface is split into a hundred-odd small slices, and the browser downloads only the few needed for the current page. The CSS that previously had to load before the page could display went from 84 KB down to 5 KB.
The pond. The background is drawn by Claude with WebGL, in two passes: first all the geometric edges are drawn into a texture, then a full-screen shader reads that texture “through the water.” So no matter how many shapes there are, each pixel is read only once. The ripples are calculated directly with a formula, no physics simulation — roughly like this:
float x = dist - age * SPEED; // how far from the wavefront
float env = exp(-x * x / (w * w)); // wave packet: only a ring or two
float amp = strength * exp(-age * DECAY);
slope += dir * amp * env * k * cos(k * x);
A few other small considerations:
- At most 4 ripples exist at once; old ones get pushed out by new ones, and frantic clicking only triggers a calculation every 0.45 seconds;
- When there are no ripples, the background renders at only 30 fps, and pauses entirely when the page goes to the background;
- On mouse move, no full-screen layer is redrawn: the lamp is drawn directly into WebGL, and the “window” that hides the handwriting only moves, composited by the GPU;
- The entire interactive JS is only about 6 KB minified, with no libraries imported.
On Edge and Chrome, it’s so smooth I’m quite pleased. A little laggy on Firefox — well… not my fault (and not Claude’s either).
Accessibility. This was part of the assignment too: the page structure uses proper semantic tags and is fully operable by keyboard; when a screen reader reads my name, it says the full “Earzu Chan” rather than spelling it out letter by letter. Claude ran the whole site through axe and found no issues at all.
Finally
These little mechanisms will most likely go unnoticed by almost everyone. But I enjoyed making them — this kind of subtle interaction gives a feeling of surprise in otherwise dull days.
If you find those lines under the lamp, you can write to tell me which one you saw. Did you like that line?