Hyaika Blog

Penguin is all you need

技术

当你为三岁小孩写一个画图软件——Mr. Baby Paint 与偶然发现的细胞自动机

当你为三岁小孩写一个画图软件——Mr. Baby Paint 与偶然发现的细胞自动机

当你为三岁小孩写一个画图软件——Mr. Baby Paint 与偶然发现的细胞自动机

目录

  • 一个芬兰程序员,和他坐在腿上的三岁孩子
  • 为幼儿设计软件:核心是「没有任何操作是错的」
  • 边缘即 UI:用屏幕边缘替代菜单栏
  • 意外的细胞自动机:当填充工具开始互相打架
  • 像素膨胀:一个像素引发的算法狂欢
  • 所以这一切值吗

凌晨三点看到 Hacker News 上有个 101 分的帖子,标题叫「Mr. Baby Paint 与偶然发现的细胞自动机」。

点进去之前我以为又是一篇「我用 AI 做了什么」的 Show HN。点进去之后发现,是一个芬兰程序员 Heikki 给自己的三岁孩子写了款画图软件,然后顺手发现了新细胞自动机、写了十几种像素膨胀算法,还把自己做的字体渲染管线开源了。

一篇文章里塞了三条完全独立但每条都很好的故事线。这 101 分是实至名归的。

一个芬兰程序员,和他坐在腿上的三岁孩子

Heikki 的三岁孩子想参与爸爸的一切活动——包括他坐在电脑前做的事。小孩子喜欢按弹簧键盘、晃动鼠标、转滚轮听咔哒声。但按一个三岁小孩的操作精度,在普通软件里什么都做不了。

他们试了 Wordpad 和 MS Paint。结果是什么呢?小孩子随意一甩鼠标就把画图模式切到了菜单栏,或者点歪了工具栏,软件就进入了一个完全不同的「模式」。爸爸得不停地把鼠标从孩子手里拿回来,把模式调回画图状态。

孩子觉得被打断很不爽。爸爸觉得这个流程根本不可持续。

Heikki 的总结很到位:「这孩子不操作电脑的时候对鼠标键盘完全没兴趣。」——意思是,孩子想做的不是「玩鼠标」,而是「玩电脑上的画图」这件事本身。这是一个重要的设计前提。

为幼儿设计软件:核心是「没有任何操作是错的」

Heikki 给自己的要求是:用做正经项目的认真态度来对待这个幼儿画图软件。

MVP 极其简单:全屏空白画布,没有菜单栏,没有工具栏。孩子坐在爸爸腿上操控鼠标,爸爸用左手按键盘快捷键。CMD+S 保存,CMD+E 清空画布。整个屏幕就是画图区域,孩子就算猛砸键盘也不会跳出任何东西。

但 Heikki 不满足于做一个「被阉割的 MS Paint」。他想让每一次操作都有反馈——无论是左键画图、滚轮撒沙、还是右键填充。「没有任何操作是错的」是这个软件的设计哲学。每次点击都产生痕迹、音效、或视觉反馈。

有一个细节让我很喜欢:幼童的鼠标移动速度极快,两帧之间的跨度可能是几十甚至几百像素。普通画图软件用直线连接采样点,但在这种场景下会画出非常生硬的折线。Heikki 用了 Catmull-Rom 样条插值来让曲线保持平滑,然后在曲线上每 1px 盖一个笔刷纹理。

对于一个「给两岁小孩画的画图软件」来说,这个技术投入已经远超「将就一下」的标准了。

边缘即 UI:用屏幕边缘替代菜单栏

Mr. Baby Paint 没有菜单栏,没有工具栏,没有调色板。它的 UI 设计在屏幕的四条边上——光标碰到不同边缘触发不同功能。

左边缘分成 16 段:控制笔刷大小和字体选择
底边缘分成 16 段:16 种颜色
顶边缘分成 64 段:64 个字符(A-Z a-z 0-9 !? 和一些符号)
右边缘分成 2 段:圆形笔刷和方形笔刷

这个设计确实聪明。幼童的鼠标操作是随机的,但屏幕边缘是一个永远可以命中的目标。你不需要精确地点击一个小图标——甩到边缘就能切换功能。而且因为分段较大(每段在 1080p 屏幕上大约 67px 宽),孩子自己乱动鼠标也可能「碰巧」换到新颜色,然后产生一种「我做到了」的兴奋感。

意外的细胞自动机:当填充工具开始互相打架

这是整篇文章里我最喜欢的段落。

Heikki 受《Mario Paint》启发,让填充工具不是瞬间完成的,而是一行一行慢慢扩展——看着颜色从点击位置向四周「生长」的过程本身就有观赏性。但和 Mario Paint 不同,Heikki 不想让填充阻塞其他操作。他允许多个填充同时进行,也允许用户在填充过程中继续画图。

然后有趣的事情发生了。

他意识到,因为所有活动的填充共享同一个「生长预算」(每帧总增长上限 1000px),新填充比旧填充生长得更快(因为它们更小)。小填充开始碰撞旧填充还在生长的边缘。当他把两个同色的填充放在一起时,它们开始互相合并、吞并、振荡——

一个基于洪泛填充的细胞自动机,就这样被撞了出来。

「它半稳定:可以运行几千代才进入稳定的振荡模式,有时候一个填充『赢了』,其他的就消失了。这是一个有趣的 bug,所以我把它留下来了。」

Heikki 试了 GPU 版本的实现——更快,无延迟——但他觉得 GPU 版本更无聊。CPU 版的「固有缓慢」成了设计的一部分。这种选择在性能优先的软件世界里几乎不可想象,但在这个为幼儿画的画图软件里,它确实是对的。

像素膨胀:一个像素引发的算法狂欢

故事还没完。

Mr. Baby Paint 内置了 16 种位图字体,大部分来自老计算机(如初代 Macintosh)。Heikki 想自己画几个,但他的单线矢量字体渲染器只生成 1px 宽的笔画——太细了。他想要更粗的版本。

正常人会怎么做?手动描一遍。

Heikki 做了什么事?他写了12 种不同的像素膨胀算法,从最暴力的「每个黑像素原地放大成方块」开始,一路尝试了跳种距离场、角隙检测、中轴骨架、签名距离场、16 方向射线投射、Meijster 精确欧几里得距离变换、带碰撞检测的迭代膨胀……最终在第 12 个版本后回到了第 4 版,在 Aseprite 里手动清理了一下,作为最终方案。

那 11 个「失败」版本呢?「它们其实也都能用,只是没达到我心里的标准。」

他把这个位图字体管线直接集成到了 Mr. Baby Paint 里。现在可以轻松从同一份源文件生成不同粗细和风格的字重。

所以这一切值吗

Mr. Baby Paint 在 itch.io 上卖 4.99 美元。到目前为止卖了大约 30 份。

30 份 × 4.99 = 149.7 美元。去掉平台抽成,可能到手一百出头。如果按工时算,他写这篇博客的功夫就已经远超这个数了。

但我在想的是另一个问题:这个项目里的每一个产出——幼儿画图软件、细胞自动机发现、12 种像素膨胀算法、位图字体管线——全都是因为「我三岁的孩子想玩鼠标,而 MS Paint 对他来说太难了」这个最开始的原点触发的。

那些算法狂欢不是为了发表论文或拿投资,只是因为一个爸爸想让坐在他腿上的孩子能开心地画画。

这 30 份销量不是指标。孩子画出了什么才是。


封面图:用文生图模型为这篇文章生成了一张极简儿童画风格的概念图。

分享:

评论(0)

暂无评论,来写第一条吧~

发表评论