コンテンツ制作 · heygen-com/hyperframes-community-skills

p5-paint-animation

> テキストプロンプト、写真、または実写動画クリップを手描き風の p5.js アニメーションに変換します。自動で描かれる鉛筆の手書き文字、筆致アートとしてペイントオンアニメーションやカメラワークを伴って再描画される写真、そして生きている絵画のように1コマずつ再描画されるショート動画(iPhoneのLive Photosを含む)を生成します。トリガーワード: "handwritten text animation", "paint this photo", "painted video", "sketch effect", "turn my live photo into a painting", "write-on effect"。すべてが100%コードベースであり、p5.brush の筆致をヘッドレスでレンダリングし、画像モデルは一切使用しません。

L4 高権限 · 段階的認可と監査V1 コミット固定コマンド実行ネットワーク通信ファイル書き込みDual-pass Audit
Author heygen-comGitHub heygen-com/hyperframes-community-skills ↗Stars 156License Apache-2.0(仓库根 LICENSE)Commit ba7a0bb6d3
管理レベルは Skill の能力と推奨統制措置を示すものであり、品質や安全性の認証、利用可否の判定ではありません。使用の可否はユーザーおよび管理者が決定します。Agent 環境によって制約が異なるため、自動保護を仮定しないでください。第三者製 Skill の著作権は原著作者に帰属し、利用リスクは利用者が負います。

1仕組みと動作原理

3つのワークフローが単一のエンジンを共有し、入力に応じて振り分けられます:テキスト→文字の手書き、写真→再描画(静止画または自動描画動画)、動画/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 で完全一致(バイト単位)」を受入条件として明記し、一度に1つのダイヤルのみ調整することを要求しています。

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 であえて「沸騰(ボイル)」させていますが、ここではその逆です。動く絵画に求められるのは「1枚の絵が動くこと」であり、「毎秒別の絵に切り替わること」ではないためです。

入力品質は成否の前提条件としてドキュメントに記載されています:写真は事前に被写体へクロップ(画面内の顔のサイズが成否を分ける)、Live Photo は事前の動きトリアージで静かな区間のみを描画、暗い素材は彩度を上げずにガンマ調整のみを行います。

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鉛筆/クレヨン/木炭)、無地用紙テクスチャ、すべて --inject 経由で指定可能
03カメラ追従レシピ:レンダリング時に各フレームのペン先座標(out.track.json)を出力し、それに基づきクロップして「ペン先を追うカメラ」の動きを実現
04Flow B:写真 → 筆致アート。静止画(PNG)および「自身を描く」動画(粗から密へ、画家の順序で塗り重ね、被写体優先)を出力可能
05Flow B の構図ルールとカメラレシピ:まず被写体にクロップ(顔のサイズが成否を決定)、SUBJECT 楕円で被写体の全パスを完了させ subject-done マーカーを残し、マーカーに沿ってズームアウト
06Flow C:動画(iPhone Live Photo 含む)→ 動く絵画。グリッド固定で安定性を維持、WORLD=1 でカメラの動きをトラッキング、MASKED=1 で被写体のみを描画(事前切り抜きフレームと併用)
073種類の出力形態を3つの harness が担当:静止画 PNG(render.mjs)、描画プロセス MP4(render-anim.mjs)、動画再描画 MP4(render-video.mjs)
08QC 基準:実際の出力フレームの確認が必須(最終フレームが描き切られているか、抜けがないか、始点・中間・終点の連続性)、同一 seed での完全一致、動画結合時の継ぎ目は動きの規律に沿って数値検証

3外部依存関係

KindDependency / Endpoint
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推奨統制措置と判定根拠

安全運用のための推奨統制措置
Agent 向け
段階的・都度認可、サンドボックス推奨
管理者向け
承認フロー、ロールバック可能、全履歴監査
判定根拠(ファイルパスと抜粋)
  • scripts/: ローカルスクリプトまたはコマンドの実行: node scripts/render.mjs <sketch.js> [out.png] [--size WxH] [--seed N] [--im
  • 网络端点: 外部ネットワークアクセスの宣言: registry.npmjs.org:npm ci で puppeteer 25.9.0 / p5 2.3.2 / p5.brush 2.2.1 をインストール(p
  • 本地工作目录: ファイル読み書きとアーティファクト出力: 呼び出し元が指定した出力パス:out.png / out.mp4。render-anim は同階層に <out>.track.json(フレームごとのペン先位置と mark)も出力
監査ノート: 青/緑ではなく黄判定とした理由は、セットアップ段階における1回限りのサードパーティ取得です:npm ci により registry.npmjs.org から puppeteer/p5/p5.brush を取得し(バージョンは固定されているもののサードパーティ製)、Puppeteer は Google のダウンロードサービスから固定された Chrome for Testing を取得します。また任意の npx hyperframes remove-background(被写体切り抜き用 CLI を npm から取得)もあります。これは同時期に黄判定となった embedded-captions(初回実行時にサードパーティ製パッケージ+リポジトリ内モデル重みを取得)と同パターンです。橙に上げない理由:ディレクトリ全体で**認証情報の読み込みが一切ない**(env/cookie/keychain なし)、実行時は**能動的にネットワーク切断**(レンダリングページは about:/blob:/data: のみに制限され、素材は data URL 注入、ローカルファイルもネットもアクセス不可、setup 記載「no credentials, no cost, nothing is uploaded」)。赤に上げない理由:3つの launcher の Chromium 引数は --enable-unsafe-swiftshader(GPU なし環境向けソフトウェアレンダリング)のみで、--no-sandbox や --disable-web-security などのセキュリティダウングレード、スクレイピング回避、任意コード実行面(--inject は eval ではない)は見当たりません。ユーザーが留意すべき点は、sketch は呼び出し元から提供され注入・実行されるため、本スキルの信頼境界には「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 ではなくホワイトリストによる変数宣言に制限され、「パラメータ調整」と「任意コードの実行」を明確に分離
  • 失敗パターンをデバッグ任せにせずドキュメント化:被写体の事前クロップ、動きのトリアージ、暗所素材のガンマ限定調整、動画グリッドの固定
  • 依存関係のバージョンを固定し再現可能なスモークテストコマンドを提供。成果物のディスク書き込み範囲が小さく、一時フレームを自動クリーンアップ
  • 最適な用途: 最適:手作り感のある素材を求めるクリエイターやフロントエンド/グラフィックスエンジニア(フレーズを自動で描かれる手書きアニメーションにする、写真を筆致アートの静止画や描画過程動画にする、ショート動画や iPhone Live Photo をコマ送り再描画の動く絵画にするなど)。より大規模な映像作品の素材制作工程(下流の編集ツールに渡す MP4/PNG の出力)にも適しています。全工程がローカルコード生成、レンダリング時オフライン、同一 seed で再現可能なため、再現性が求められるパイプラインに最適です。
    不向きな用途: 不向き:① 明瞭なタイポグラフィやブランド厳密なフォント形状が必要な場面(実フォントを使用すべき);② フォトリアリズムを追求するプロジェクト;③ 約10秒を超える長尺素材(フレーム単位でレンダリングコストが線形増加);④ リアルタイム/インタラクティブなWebページ(本スキルはオフライン MP4/PNG のみを出力);⑤ 単一のプロンプトで完成品を直接求める用途(3つの明確なフローとダイヤルがあり、レシピに沿った操作が必要);⑥ npm パッケージやブラウザのダウンロードを許容しない、またはディスク空き容量が約 500MB 未満の環境。
    Install Agent-Ready
    ① Verified Mirror 2026-09-28
    Option A · Direct DownloadDownload p5-paint-animation.tar.gz
    sha256: 1f0478d5e5ca2ab0…
    Option B · Copy Guide for Agent
    install.json
    Agent はこの JSON ガイドから自動的にダウンロードして検証します。
    ② Upstream GitHub
    GitHub Repo ↗
    Pinned to commit ba7a0bb6d3.