1703 Star 的 anidoodle:把画画写成可复现的代码,我在它的确定性契约里看到了工程之美

导读alexgreensh/anidoodle 是一个 Apache-2.0 的 Agent 技能/插件,把插画、循环动画、互动网页艺术、产品发布片和配乐全部用代码生成,提供 32 种手绘风格与 21 种代码合成曲风,适配 Claude Code、Codex、Grok Build。本文基于仓库源码实测:引擎含 1139 个 TS 文件、约 8.1 万行、84 个工具脚本和 89 个测试,四个后端(playwright/html-player/remotion/hyperframes)共用一个不关心调用方的纯函数绘图核心。重点拆解它最硬核的确定性契约——renderFrame 必须是纯函数、随机数只允许来自带种子的 rng、核心禁用 Math.random/Date/ctx.filter/DOM、缓存键必须命名像素依赖的全部输入、三份外部资产(three.js/字体/旁白)全部哈希锁定,并以 PSNR>45dB 与帧哈希做跨后端校验;以及它如何用一份源码同时产出 PNG/MP4/GIF/透明 WebM/自包含 HTML,让 1410 帧的 47 秒短片首遍之后重渲染零成本。同时如实说清它是插件而非独立 App、需要 Node20+ 与 ffmpeg、照片重绘是代码笔触而非照片级写实、Apache-2.0 的 NOTICE 署名义务,以及那份罕见的、主动记录首跑 0/12 失败事故的诚实工程文化。

生成式 AI 画图有个让人不安的特性:同一套提示词跑两次,得到的是两张不同的图。做单张海报也许能接受,但如果你要做一部 1410 帧的短片、一套需要风格统一的品牌插画,这种"每次都重新掷骰子"的不确定性就是工程灾难。最近我读到一个 1703 Star 的开源项目,它从根上拒绝了这种不确定性——anidoodle,口号是"手绘的艺术,用代码写成"。

它不是又一个画图模型,而是一个装进 Claude Code、Codex、Grok 的 Agent 插件,让 AI 用写代码的方式作画:每一笔是一个函数,每一个音符是一次算术运算,于是同一份源码在任何机器、任何尺寸下都会重新画出同一张画,永远如此。我把它 8 万多行的引擎和那份极其较真的确定性契约读了一遍,这篇讲讲它是怎么做到"分毫不差"的,以及这种思路为什么值得每个做内容工程的人了解。

一、先定性:它是 Agent 技能,不是独立软件

和传统绘图工具不同,anidoodle 的交付物是一个标准 skill 文件夹(skills/anidoodle/SKILL.md)加上一套插件清单,分别提供 .claude-plugin、.codex-plugin、.grok-plugin 三种适配。它本身不训练模型,真正写代码的是你的编码 Agent,anidoodle 提供的是方法论、绘图引擎和质量门禁。

我统计了引擎规模:engine/ 下 1139 个 TypeScript 文件、约 8.1 万行代码,tools/ 里有 84 个工具脚本,测试目录里有 89 个测试文件、共 104 个测试相关文件。这不是个玩具,而是一个有完整构建、渲染、校验、音乐合成链路的生产级引擎。安装也很直接,以 Claude Code 为例:

/plugin marketplace add alexgreensh/anidoodle
/plugin install anidoodle@alexgreensh-anidoodle

之后用大白话说需求即可,比如"给招聘页面画一张孔版印刷风格的灯塔,1600×900",它会自动路由到合适的工作流和风格配方。

二、核心思想:每一帧都是纯函数的一次调用

理解 anidoodle 的钥匙,是它把"生成艺术"和"生成式模型"做了彻底切割。

代码作画与传统图像模型的确定性对比

用图像模型做视频,每渲染一帧都要调一次模型、烧一次 token,而且帧与帧之间风格、角色容易漂移。anidoodle 的路线是让 Agent 一次性写出一个纯函数绘图引擎,之后所有渲染都在本地跑、零模型成本。README 里那个 47 秒、1410 帧的示例短片就是一个程序:第一遍大约每帧花 60 个 token 的代码量,之后在任何机器、任何尺寸下重渲染全部画面和立体声配乐都是免费的,而且每次分毫不差。一张静态插画不过几百行代码,一整个系列的图像,本质上只是一份种子列表。

它把三种产物统一到了同一个抽象:成品插画是纯函数被调用一次,循环贴纸是同一个函数循环调用,短片则是这个函数配上故事和配乐。三者背后是同一套确定性保证。

三、确定性契约:把"可复现"写成强制规则,而不是口号

这是整个项目最硬核、也最值得程序员细读的部分。references/determinism-and-contract.md 开篇就写道:“确定性,否则它不是一部电影,只是一场现场表演。“我把契约条款梳理出来:

  1. renderFrame(frame, env) 必须是纯函数。帧之间不携带状态,模块级不许有触碰像素的可变状态;音频同理,采样是采样率的纯函数。
  2. 随机数只能来自 rng(seed)。核心里永远不许出现 Math.random,也不许用 Date、performance.now——时间属于宿主,不属于绘图核心。
  3. 受限的画布能力。只允许路径、填充、描边、裁剪、变换、合成、渐变、预加载字体的 fillText 等,任何形式的 ctx.filter 都被禁止(因为 Safari 不支持,会破坏跨端一致),也不许碰 DOM、window、new Image,画布表面只能由 env.canvas() 提供。
  4. 仓库里不放素材,只有三个具名例外。普通图像、字体、音频都不进仓库——文字是笔画、纹理是带种子的噪声、音乐是算术,这让整个仓库就是整部片子,没有素材授权问题。三个例外(vendored 的 three.js、一个 base64 内嵌字体、烘焙的旁白音频)全部用哈希锁定,构建不可能在影片脚下悄悄变化。
  5. 缓存键必须命名像素依赖的每一个输入。缓存帧和冷启动渲染同一帧号必须像素一致。文档还老实交代了两个靠机器扫描(而非肉眼)发现的违规:一个标注层的键漏了曲柄角度、一个漏了姿势。
  6. 位图永远只许缩小不许放大。近景要在该视角重新着墨,避免插值带来的不一致。

校验手段也很硬:在不同页面正向、反向哈希若干探针帧,冷帧对比缓存帧,标准是视觉一致且 PSNR 高于 45dB,优先用免费的哈希相等性判断。它甚至提醒你半色调(halftone)对亚像素位移极度敏感、一次错位就掉 17dB——这种踩坑细节,是真做过片子才写得出来的。

四、32 种画风 + 21 种曲风,而且都"自己会画出来”

anidoodle 的 32 种手绘风格分类

styles.gen.json 里确实是 32 套完整风格配方,从铅笔水彩、马克笔漫画、孔版印刷、木刻浮世绘、水墨、刮画,到像素画、玩具积木、低多边形 3D、橡皮管动画。每种风格模拟的不只是观感,还有这种媒介的真实落笔顺序:水彩先打铅笔稿再由浅到深铺色,木刻先印主版再逐一套色版,像素画先搭纯色形状再加阴影。每种风格都配一段延时影片,逐笔展示画面如何诞生,而且影片最后一帧和静态图逐像素一致,没有用淡入淡出糊弄。

音乐侧同样是代码合成、没有采样和录音:21 种曲风(低保真电子、House、合成器浪潮、管弦、爵士、民谣等)提供音色、律动与和声词汇,但和弦、动机、曲式每次重新创作。它建模了施坦威钢琴、Rhodes/Wurlitzer 电钢、弦乐铜管、吉他贝斯和多套鼓组,还有 12 位采样器、磁带晃动、黑胶噼啪这些低保真质感。node tools/music.mjs check 会在任何人试听前审核调性、响度、掩蔽、声部平衡和新颖度——听起来像已有曲子的配乐不会通过。

五、工作流与工具链:先画后动,校验先行

anidoodle 从需求到成片的五步工作流

它的工作方法(working-method.md)强调先用一张静态图验证效果,再一路做到底,把审批留给出错代价高的环节。84 个工具覆盖了完整链路:scaffold 搭骨架、still 出静态图、render 渲染 GIF/MP4/带透明通道的 WebM、music 作曲、gate 一次性检查确定性/契约/静音、verify-export 解码校验最终交付文件、emit 打出一个约 100KB、双击就能离线播放且自带声音的 HTML。

四个后端——playwright、html-player、remotion、hyperframes——共用同一个绘图核心,核心根本不关心是谁在调用它。运行环境需要 Node 20+,涉及动态内容还要 ffmpeg。它还内置了产品发布片工作流(launch.mjs),一条时间线输出 16:9、1:1、4:5、9:16 四种画幅并各自重新构图,自动生成封面帧和 srt/vtt 字幕,检查音画同步和峰值电平,确保没有一帧的文字被边缘切掉。

六、祛魅:边界,以及难得的工程诚实

anidoodle 的适用场景、能力边界与工程文化

先说边界。第一,它是 Agent 插件不是开箱 App,你得有 Claude Code/Codex/Grok 这类环境,最终审美上限仍取决于你的引导和底层模型。第二,8 万行引擎的深度定制有学习曲线,动态渲染依赖 Node、ffmpeg 和浏览器。第三,它的照片重绘是把照片测量成明暗和方向后用代码笔触重画,是风格化作品而非照片级写实。第四,许可是 Apache-2.0 而非 MIT,带有 NOTICE 和署名义务,商用和再分发要保留归属声明。

但我最想讲的是它的工程文化。确定性文档里专门记录了一次"无法解释的首跑失败”:全新工作区第一次 gate 报 0/12 帧全部不一致,之后七次复测又全部 12/12 通过、无法复现。换作很多项目,重跑到绿灯就当无事发生;但作者写下了整段经过,并断言"一次失败随后通过是最危险的结果,重跑直到变绿然后闭嘴是不可接受的,一份诚实的’无法复现,这是我尝试过的方法’本身就是结论"。文档里还留着大量"每条规则都是一次画砸换来的"的伤疤注释,比如行注释 // 曾吞掉语句后半段、三次搞挂构建,所以密集单行代码库里强制用 /* */。

我的判断

anidoodle 真正的价值,不在"AI 能画画"这个已经不新鲜的结论,而在于它把生成艺术拉回了软件工程的轨道:用纯函数、种子随机、哈希锁定和帧级校验,把"灵感"变成可复现、可版本管理、可团队协作、可零成本重渲染的资产。对追求品牌视觉一致性的团队、做产品发布片和讲解视频的开发者、以及相信"过程可审计"的人,这套思路比又一个黑箱绘图模型有价值得多。

它当然不是万能的:照片级写实、零门槛开箱、脱离编码 Agent 独立运行,都不是它的目标。但如果你认同"一件作品应该能被一份源码完整重建",那 anidoodle 示范的,正是 AI 创作里稀缺的那种确定性与诚实。

项目地址:https://github.com/alexgreensh/anidoodle