首页 / 全部技能 / 内容创作 / p5-paint-animation
内容创作 · heygen-com/hyperframes-community-skills

p5-paint-animation

> Turn a text prompt, a photo, or a live video clip into hand-made-looking p5.js animation — pencil handwriting that writes itself, photos repainted as brushstroke art with paint-on animation and camera moves, and short videos (iPhone Live Photos included) repainted frame-by-frame as a living painting. Trigger on: "handwritten text animation", "paint this photo", "painted video", "sketch effect", "turn my live photo into a painting", "write-on effect". Everything is 100% code: p5.brush strokes rendered headlessly, no image models.

风险提醒:黄色 · 留意使用AI 侦查报告
作者 heygen-comGitHub heygen-com/hyperframes-community-skills ↗Stars 156许可 Apache-2.0(仓库根 LICENSE)commit ba7a0bb6d3
agent 宿主通常会约束 skill 执行权限;风险提醒为 AI 侦查观点,不构成质量或安全保证。第三方 skill 仅作拆解与展示,安装使用风险自负,版权归原作者。

1实现原理 · 为什么它能做到

三条流程共用一台引擎,按输入分派:文字→写字母、照片→重绘(静帧或画自己)、视频/Live Photo→逐帧重绘的活画;全部由代码生成,不用任何图像模型。

skills/p5-paint-animation/SKILL.md
Everything is 100% code: p5.brush strokes rendered headlessly, no image models.
注:这里在做什么:把「像手绘」这件事交给笔刷模拟(p5.brush)+ 逐笔绘制的时序,而不是扩散模型;因此可复现(同 seed 同结果)且完全离线。

渲染架构是「生成器 + 逐帧截图」:Node 起无头 Chromium,注入 harness 与 sketch;sketch 的 paint() 是生成器,每次 yield 等于画一笔,Node 侧每步截一张图,最后由 ffmpeg 缝成 MP4。

skills/p5-paint-animation/scripts/render-anim.mjs
// draw() advances the paint() generator spf steps, then pauses; the node side
注:缝合在同一文件:execFileSync("ffmpeg", [ "-y", "-framerate", String(fps), "-i", resolve(framesDir, "%05d.png"), …]);静帧流程(render.mjs)则直接对 canvas 截图。

渲染期网络与文件访问是被**硬隔离**的:每个页面在打开前都装上请求拦截,只放行 about:/blob:/data:,其余一律 abort;照片/视频以 data URL 形式注入(IMG_SRC),所以 sketch 既读不到本地文件也上不了网。

skills/p5-paint-animation/scripts/lib/page-safety.mjs
const allowedProtocols = new Set(["about:", "blob:", "data:"]);
注:拦截实现在同文件:request.abort("blockedbyclient"); 注入口在 render.mjs:'// --image path -> global IMG_SRC data URI (load with loadImage(IMG_SRC) in setup)'。

参数注入是白名单式变量声明,不是任意代码求值:--inject "NAME=value" 只被翻译成 `var NAME = <JSON>`;变量名必须匹配 /^[A-Z][A-Z0-9_]*$/,值只允许 JSON 字符串/数字/布尔,否则直接抛错。

skills/p5-paint-animation/scripts/lib/inject.mjs
const NAME = /^[A-Z][A-Z0-9_]*$/;
注:值解析与报错在同文件:throw new Error("--inject values must be JSON strings, numbers, or booleans"); 以及未闭合字符串的报错 '--inject contains an unterminated string'。

可复现性是显式目标:给了 --seed 就在库载入之前用 mulberry32 覆盖 Math.random(让 p5.brush 内部的噪声与散布可复现),文档把「同 seed 必须逐字节相同」写成验收条件,并要求一次只调一个 dial。

skills/p5-paint-animation/scripts/render.mjs
// Seeded PRNG (mulberry32) installed over Math.random BEFORE libs load, so
注:SKILL.md 的对应纪律:'Same seed = identical render. Iterate one dial at a time against a fixed seed.'

视频流程用「锁定笔触栅格」换稳定:栅格只在第 1 帧建一次并锁死,之后每帧只重采样颜色与朝向——所以主体移动时笔触不会自己抖动。

skills/p5-paint-animation/SKILL.md
The stroke lattice is built ONCE from frame 1 and locked; only colors and orientations resample per frame. Strokes hold still — the painting never "boils" while the subject moves.
注:对照:本仓的 day-in-my-life 故意让笔迹以 8 fps「沸腾」,这里则相反——因为活画要的是「一张画在动」,不是「每秒换一张画」。

输入质量被当成成败前提写进文档:照片要先裁到主体(脸在画面里的大小决定成败)、Live Photo 先做运动分诊只画平静窗口、暗素材只做 gamma 而不加饱和。

skills/p5-paint-animation/SKILL.md
**Crop to the subject first.** Face size in frame is destiny: selfie-distance faces resolve beautifully; small faces stay figures.
注:另两条同段:'**Motion triage first.** Live Photos include the phone swinging.' 与 'Dark footage: grade with **gamma only** (`eq=gamma=1.2`). Never boost saturation before painting — shadow chroma noise becomes literal teal paint.'

写盘范围被限制在调用方给的位置并自动清理:只写 out.png / out.mp4(外加同级 .track.json;仅当 --keep-frames 1 时保留 _frames_* 目录)。

skills/p5-paint-animation/SKILL.md
Writes only inside paths you pass (`out.png` / `out.mp4`, plus a sibling `.track.json` and, with `--keep-frames 1`, a `_frames_*` directory).
注:清理实现在 render-anim.mjs:if (flags["keep-frames"] !== "1") await rm(framesDir, { recursive: true, force: true }); 视频流程另有 out/_src_*/_dst_* 中转目录,收尾时同样删除。

2核心能力

01Flow A:文字 → 手写动画。自带完整字母表(小写/大写/数字/标点),自动换行,笔迹一笔一笔写出来
02Flow A 的可调旋钮:字号、墨色、笔种(2B 铅笔/crayon/charcoal)、空白纸,全部经 --inject 传入
03镜头跟随配方:渲染会写出每帧笔尖位置(out.track.json),据此裁切做「镜头跟着笔走」的运动
04Flow B:照片 → 笔触画。可出静帧(PNG),也可出「画自己」的影片(由粗到细、按画家顺序铺,主体优先)
05Flow B 的构图纪律与镜头配方:先裁到主体(脸的大小决定成败),再用 SUBJECT 椭圆让主体走完所有 pass 并留下 subject-done 标记,随后沿标记做拉远
06Flow C:视频(含 iPhone Live Photo)→ 活画。锁栅格保持稳定,WORLD=1 可跟踪相机运动,MASKED=1 只画主体(配合先抠像的帧)
07三种输出形态由三个 harness 承担:静帧 PNG(render.mjs)、绘制过程 MP4(render-anim.mjs)、视频重绘 MP4(render-video.mjs)
08QC 门槛:必须看真实输出帧(末帧要画完、无空洞、首中末连贯)、同 seed 必须逐字节一致、并片时接缝要按运动纪律数值验证

3外部依赖

类型依赖
packagepuppeteer 25.9.0(Apache-2.0)、p5 2.3.2(LGPL-2.1)、p5.brush 2.2.1(MIT),由 lockfile + package.json 双钉
cliffmpeg(渲染链路:把逐帧 PNG 缝成 MP4、视频流程抽帧与拼回)
packageNode 22.12+ 与约 500MB 磁盘(Chromium)
cli可选:npx hyperframes remove-background(仅 Flow C 主体抠像用;CLI 从 npm 取)
network无运行期外发:渲染时页面被请求拦截(只放行 about:/blob:/data:),素材以 data URL 注入
package素材来源:调用方自备的文字/照片/视频(Live Photo 需其配对的 .mov)
clisetup.sh 的一次性下载(npm ci + Puppeteer 拉 Chrome for Testing),自述无凭证、无成本、不上传

4风险提醒 风险提醒:黄色 · 留意使用

风险提醒:黄色 · 留意使用
  • setup 的第三方拉取构成供应链面 — npm ci 拉 puppeteer/p5/p5.brush(版本双钉可复现),Puppeteer 再从 Google 的下载服务拉取其钉定的 Chrome for Testing;这是本 skill 唯一的大块外发,且需数百 MB 磁盘。作者已把它做成手动的一次性步骤。
  • sketch 由调用方提供并注入执行 — 三个 harness 的设计就是 addScriptTag 注入调用方的 sketch JS,所以「谁写 sketch」等于「谁能在本机无头浏览器里跑代码」。对本 skill 的正常用法这是能力面,但它意味着信任边界不止于本目录。
  • 单帧渲染成本高 — SKILL.md 自述渲染成本约每帧 5–20 s,因此素材长度被划在 ~10s 内;长片或大批量帧会显著吃 CPU/时间,且无 GPU 时依赖 --enable-unsafe-swiftshader 的软件渲染。
  • 可选项引入本仓库外的 CLI 与平台假设 — Flow C 主体抠像建议 npx hyperframes remove-background,该 CLI 与所谓『local CoreML』后端都不在本仓库内,非 macOS 平台的实际行为无法在包内核实(见 second_pass 的 unlocatable)。
  • 素材权利由用户自负 — 输入是照片/视频(常见的是真人),输出是可发布的 MP4/PNG;SKILL.md 只谈技术配方,没有版权、肖像或商标方面的提醒条款。
  • 不解决整片语法 — 文档明说本 skill 只产出素材('this skill produces material, not final grammar'),接缝与节奏要按调用方自己的运动纪律另行数值验证,误当成品用会出问题。
风险提醒:黄色,留意使用。判黄而非蓝/绿的依据是 setup 阶段的一次性第三方拉取:npm ci 会从 registry.npmjs.org 取下 puppeteer/p5/p5.brush(版本双钉,但仍是第三方包),Puppeteer 还会从 Google 的下载服务拉取其钉定的 Chrome for Testing;另有可选的 npx hyperframes remove-background(从 npm 取 CLI 做主体抠像)。这与同批判黄的 embedded-captions(首跑拉第三方包 + 本仓模型权重)同型。之所以不升橙:全目录**无任何凭证读取**(无 env/cookie/keychain),运行期**主动断网**——渲染页被请求拦截到只剩 about:/blob:/data:,素材以 data URL 注入,页面既读不到本地文件也上不了网,且 setup 自述 no credentials, no cost, nothing is uploaded。之所以不升红:三个 launcher 的 Chromium 参数只有 --enable-unsafe-swiftshader(无 GPU 环境的软件渲染阀),未发现 --no-sandbox、--disable-web-security 一类安全降级,也没有反爬绕过或任意代码执行面(--inject 不是 eval)。需要用户知情的是 sketch 由调用方提供并注入执行——本 skill 的信任边界里包含「写 sketch 的那一方」。

5第二遍独立确认

  • [ok] 『渲染期网络与本地文件访问被挡住』是否真成立 — render.mjs / render-anim.mjs / render-video.mjs 都在 const page = await browser.newPage(); 之后立即 await hardenPage(page);page-safety.mjs 用 page.setRequestInterception(true) 后只放行 about:/blob:/data:,其余 request.abort('blockedbyclient')。素材以 `var IMG_SRC = "data:…;base64,…"` 注入,故 sketch 拿不到 file:// 也发不出请求。
  • [ok] 有没有关闭沙箱或安全降级的 flag(红档的关键判据) — 三个 launcher 的 launch 参数只有 headless: "new" 与 args: ["--enable-unsafe-swiftshader"];无 --no-sandbox、--disable-web-security、--allow-file-access-from-files、--single-process、--ignore-certificate-errors。--enable-unsafe-swiftshader 是为无 GPU 环境启用软件渲染,不属安全降级面。
  • [ok] 『无凭证』是否成立 — 全目录 grep process.env / os.environ 零命中;三个 harness 无 token/key 参数;setup.sh 自述 'No credentials, no cost, nothing is uploaded.';无 cookie/keychain/.env 访问。
  • [ok] --inject 是否等于任意代码执行 — lib/inject.mjs 的 buildInjectDeclarations 只产出 `var NAME = <JSON.stringify(value)>;`;NAME 必须匹配 /^[A-Z][A-Z0-9_]*$/(否则 'invalid --inject name'),值非 JSON 字符串/数字/布尔即抛错,未闭合字符串亦抛错。全文件无 eval / new Function。
  • [ok] seed 可复现是否真落在代码(而非文档口号) — render.mjs 与 render-anim.mjs 都构造 seedShim,并在 addScriptTag 注入 p5.min.js / p5.brush.js **之前**先注入 mulberry32 覆盖 Math.random(注释:'Seeded PRNG (mulberry32) installed over Math.random BEFORE libs load')。
  • [ok] 文档声明的旋钮是否与 sketch 实现一致 — handwriting.anim.js 有 typeof PHRASE / INK / BRUSH / PAPER 守卫(const TEXT = typeof PHRASE !== "undefined" ? PHRASE : "hello world"; 等);repaint.anim.js 有 STYLE / DETAIL / LOOSE / SUBJECT(subject-first 段);repaint-video.js 有 WORLD / MASKED / BG 且 render-video.mjs 支持 --frames DIR 传已抠像帧。与 SKILL.md 的 dial 列表逐项对得上。
  • [discrepancy] 『Requires: Node 22.12+』是否被运行时校验 — SKILL.md 的 Requirements 写 'Requires: Node 22.12+, ffmpeg on PATH, ~500MB disk (Chromium).',但 scripts/package.json 没有 engines 字段,setup.sh 与三个 harness 也都不做 Node 版本检查;低版本 Node 会照常尝试执行,只在运行期以别的错误暴露。因此该版本要求是文档级而非技术门槛(对 pin commit 有效的真实出入)。
  • [unlocatable] Flow C 抠像『local CoreML inference, no data leaves the machine』 — remove-background 属于 HyperFrames CLI,该 CLI 不在本仓库内,其后端实现(是否 CoreML、是否在非 macOS 平台改走别的推理路径、是否会下载权重)无法在本 skill 目录内复核;本次为只读侦查且不运行该命令,故按文档声明记录,不作为已核事实。

6结论

  • 渲染期主动收窄执行面:请求拦截只放行 about:/blob:/data:,素材以 data URL 注入,页面既无网络也无本地文件访问
  • 全本地代码生成、无图像模型,因此同 seed 可逐字节复现(seed 在库载入前覆盖 Math.random)
  • --inject 被收紧成白名单变量声明而非 eval,把「调参」与「执行任意代码」分开
  • 把失败模式写进文档而不是留给调试:先裁主体、先做运动分诊、暗素材只做 gamma、视频锁栅格
  • 依赖版本双钉并给出可回归的 smoke test 命令;产物写盘范围小且自动清理临时帧
  • 适合:适合:需要「手作感」素材的创作者与前端/图形工程师——把一句短语变成自己写出来的手写动画、把照片变成笔触画(静帧或自绘过程)、把一段短素材(含 iPhone Live Photo)变成逐帧重绘的活画;也适合作为更大片子里的素材环节(输出 MP4/PNG 交给下游剪辑)。因为全程本地代码生成、渲染期断网、同 seed 可复现,它特别适合对可重复性有要求的流水线。
    不适合:不适合:① 需要清晰排版或品牌精确字形的场景(应使用真实字体);② 追求写实的项目;③ 长于约 10 秒的素材(渲染成本按帧线性放大);④ 实时/交互网页(本 skill 只出离线 MP4/PNG);⑤ 想用一个 prompt 直接出成品(这里是三条明确流程 + 旋钮,需要按配方操作);⑥ 不愿承担 npm 包与浏览器下载、或磁盘不足 ~500MB 的环境。
    安装 agent 直装可复制
    ① 本站镜像 更新 2026-09-28
    方式 A · 人下载镜像包下载 p5-paint-animation.tar.gz
    sha256: 1f0478d5e5ca2ab0…
    方式 B · JSON 格式安装指南,复制给 agent
    安装指南
    agent 读 JSON 指南后会自动从本站下载安装,无需更多说明。
    ② 上游 GitHub · 原始来源
    能访问 GitHub?直接去上游安装(实时版,可能已更新)GitHub 原始 ↗
    本页镜像锁定 commit ba7a0bb6d3;上游为实时仓库。
    来源信息 GitHub 原始
    作者 / 仓库heygen-com / heygen-com/hyperframes-community-skills
    Stars156
    最近推送2026-09-24
    本 skill commitba7a0bb6d3
    许可Apache-2.0(仓库根 LICENSE)
    本站信息
    收录日期2026-09-06
    分类内容创作
    侦查报告AI 侦查 · 2 遍 · 2026-09-06
    本站镜像与 GitHub 原始是不同来源:本站锁定 commit 快照经 /r2 分发;GitHub 为实时上游,内容可能已更新。
    同分类邻近