叶序:一块随声音起舞的 LED 灯板
文章摘要
Jagi Natarajan 在 2026 年 9 月 28 日发布的一篇制作记录,讲他如何把”叶序”(phyllotaxis)这种自然图案做成一块挂在墙上、会随房间里的声音律动的 LED 灯板。他一直着迷于用代码理解自然中的图案,”与宇宙中固有的涌现规律交流”。
从几行代码开始。 叶序就是向日葵花盘、多肉植物中心那种向外扩展的双螺旋。生成它的代码出奇简单:在圆的一条半径上等距取一串点,再把第 i 个点旋转 i 倍黄金比例(乘以一整圈),就得到一团已经很像向日葵的点云。他对点云做 Voronoi 剖分,得到的图案像种荚一样有机。玩了一阵之后他想到:如果把它做成实物,每个格子里放一颗可单独寻址的 RGB LED 呢?一种”细胞状”的 LED 矩阵。他高中时做过一块方形的 NeoPixel 矩阵,一直在等一个更有意思的形状来做下一块。
从数字到实物。 他把 Processing 草图里的格子边界数据导出,导入用 CadQuery 库写的 Python 脚本:先把所有 Voronoi 格子合并成一个整体,再把每个格子向内收缩后的体积挖掉,留下薄壁,并在每格中央挖出 LED 安装孔。整个形状按 3D 打印机的打印床大小切成四块。之后在 FreeCAD 里加上螺丝孔,又做了一块很薄的”面板”版本,在它背面粘上纸,再用螺丝固定到格子墙上。他找到一种透光很好的桑皮纸,纤维纹理很漂亮,光从后面透出来显得有机又俏皮。焊 89 颗 LED 很枯燥,但他进入心流后反而觉得放松。主控是手边一块闲置的 STM32 “blackpill” 板,他用 SPI 写了一个 NeoPixel 驱动。
像写着色器一样写灯效。 他搭了一个简单的草图框架,用查找表存下每颗 LED 在单位圆上的浮点坐标,于是可以写类似片元着色器的代码:遍历每个格子,转换成极坐标,用角度和半径组合出一个随时间移动的正弦螺旋来控制亮度。他很快发现自己最喜欢的是一种从中心向外脉动的径向正弦波,后来它成了声音响应程序的主要特征。
加入声音。 他希望灯板能对房间里的声音做出反应,”感觉是活的”。STM32 有浮点单元和 ARM DSP 指令,足以跑傅里叶变换。他加了一颗数字 I2S 麦克风 INMP441,数字信号接线简单,也不用担心模拟噪声。借助 ARM CMSIS 库做信号处理,加上自动增益控制和多频段拆分,就有了编写声音响应效果的基础。经过大量调参,以及一份”满是全局计时器和混乱状态”的草图,他得到了至今仍在板子上运行的那个程序。它第一次”活过来”时,放的是 Jon Hopkins 的《Neon Pattern Drum》。
收尾。 他把一块竹砧板用雕刻机修成圆形当背板,竹子的天然纹理和纸张很搭。最初的控制电路焊在洞洞板上、塞进一个带吉他效果器开关的 3D 打印盒子里,但接线不稳,盒子一动 LED 就闪。于是他学着画 PCB,做了一块正式的电路板,安放 blackpill、麦克风、按钮,还有一个电平转换芯片,好给 LED 输出稳定的 5V 信号。画 PCB 比他预想的容易。
第二版。 2026 年新年派对上大家都很喜欢,还有朋友说想要一个,他就开始做第二版,目标是更好组装、更结实。既然刚学会画 PCB,他决定直接用 PCB 当 LED 的背板,并把形状改成五重旋转对称:PCB 一次最少订 5 块,与其 3D 打印四块各不相同的部件,不如订 5 块一模一样的板子拼成一圈。结果用烙铁手焊 NeoPixel 更是噩梦,因为焊盘藏在灯珠底下,本来是要加热整块板子来焊的,项目因此搁置了一阵。2026 年夏天他去 Recurse Center 参加编程驻留,在那里认识的一位朋友很会用热风枪,两人一起焊完了所有灯珠,他又设计打印了能卡在 PCB 上的新格子墙。完成后,他把这件作品留在了 Recurse Center 做装置:主控换成 ESP32 以便接入 Wi-Fi,固件用 Rust 重写,控制器跑一个本地网站,RC 的参与者可以通过一个小型 WebAssembly 后端和极简 API 上传自己写的灯效和小游戏。
下一步。 他对项目仍不完全满意。喜欢它的人够多,他很想多做几个送朋友甚至出售,但两次焊接都很痛苦,贴纸工序麻烦,纸本身也脆、坏了难修。他列出了一些想试的改进:换更好手焊的 LED、直接让 PCB 厂贴片、加一层透明亚克力保护纸面、用磁铁固定纸面板。他说项目先歇一歇,时机到了就做第三版。
HN 评论精华
帖子 354 分、50 条评论,几乎全是正面反馈,作者 jagiii 也在评论区回答了几个技术问题。
五重对称怎么做的。 petsfed 很想自己做一个,翻到了作者的硬件仓库,希望作者补上许可证,并想知道五重对称是怎么实现的。作者回复说已加上 MIT 许可证;做法是把前面描述的点生成过程绕自身旋转 5 次,他也试过其他对称数,5 恰好最好看,又正好方便订 PCB。之后他让 AI 加了一个功能:把一个”五分之一扇区”的外接矩形绕形状旋转,找出使面积最小的格子划分方式,因为 PCB 价格随面积增长。throwaway219450 称赞用五重对称”用满打板配额”的思路很巧。
音频处理。 hajjamixcv2012 指出,基于黄金角的叶序螺旋是做径向音频可视化的理想坐标系,因为它避免了同心圆环那种内密外疏的失真;这类作品最难的是在瞬态响应和时间平滑之间取得平衡,免得频段繁忙时 LED 变成一片乱闪的噪点。他问作者是对 FFT 每个频段做衰减滤波,还是在上游做动态增益归一化。作者坦言自己对声音响应还不完全满意:两种方法都试过,最后把”按全局能量归一化的频段能量”和”按各频段最大能量归一化”混在一起,用了一个”非常随意但好像管用”的系数;他还为每个频段保留几份衰减速度不同的电平副本,这样灯效里对瞬态敏感的部分和依赖缓慢变化的部分可以分别取用。他说自己完全不是这方面的专家,靠的是大量试错。
焊接与生产。 XRG 建议直接让 PCB 厂贴片,物料这么简单几乎不加钱,还能省大量时间、减少过热和静电损伤;scoot 提醒文章”下一步”里已经写了这一条;asdfasdfe 则觉得这块板子手焊并不难。throwaway219450 分享了一个穷玩家早早学会的技巧:给贴片元件画稍大一点的焊盘,好有地方吃锡;NeoPixel(5050 封装)的焊盘延伸到侧面,其实还算好手焊,QFN 封装也能靠在中心焊盘下打一个大过孔从背面焊。Ccecil 补充 KiCad 对部分元件提供手焊封装,而且设计时就要考虑烙铁能不能伸进去,有时”容易返修”和”必须拆掉其他元件才能返修”之间只差一个 90 度旋转;他还提到 KiCad 现在可以直接导入 DXF 和 SVG 作为板框。petsfed 说手焊一块还行,多做几块简直是折磨,他得注意饮食、少喝咖啡来减少手抖。
纸怎么贴。 asdfasdfe 和 UncleOxidant 好奇纸是怎么固定的,担心胶水渗进纸里导致透光不均。作者说他用很薄的 3D 打印格子轮廓板在桑皮纸上描形、裁剪,再粘在轮廓板底面,然后把轮廓板拧到主体上;用普通胶棒就够了,而且纸被夹在两层塑料之间,不需要大面积上胶。他承认这是设计上最需要改进的地方,太脆弱了;好处是纸够薄,从上面压住能防止光在格子之间串色。
其他。 lukeify 说这和 Voria Labs 的产品 Lumanoi 非常像,也许是趋同演化;pbronez 指出共同点在于都用 Voronoi 剖分把散布的 LED 点映射成格子,asdfasdfe 则认为只是表面相似、内部完全不同。Already__Taken 认为文章低调带过了一个很有力的模式:在嵌入式 Rust 里用 wasmi 运行时加载第三方灯效,并用”燃料”(fuel)机制来计量加载和每帧耗时,比随意设超时合理得多;rishabhaiover 也很喜欢别人可以上传 Wasm 草图来贡献图案。yeehawjared 建议刷 WLED 固件做二维映射,h-c-c 推荐用(已停产的)Fadecandy 板,说它的抖动算法能做出最顺滑的渐变。shevy-java 不喜欢”phyllotaxis”这个名字,认为叶序是叶片为了采光生长出的排列,LED 跟这些无关,名字用错了;rglullis 回敬说”没有比命名上的吹毛求疵更糟糕的自行车棚讨论”,pierrec 则指出维基百科本身就把更宽泛的图案也称为”呈现叶序”。realo 提到他买过 Evil Genius Labs 的 Fibonacci128 小灯板,jan_Sate 说这让他想起小时候看旧版 Windows Media Player 可视化效果的时光,simlevesque 说”卖给发烧友能赚几百万”。Vakaiser 称赞演示曲目选得好。