ascii-animation
This skill should be used when the user asks to "make an ASCII animation", "build a terminal/CLI intro or loader", "convert an image or video to ASCII art", "add an ASCII shader/post-effect to a canvas or Three.js scene", "create retro/hacker text-character motion", or "animate text characters with a brightness ramp". Covers generative ASCII fields, image/video/3D-to-ASCII, and animated character art for both web and terminal.
1How It Works & Mechanism
The foundation of the entire implementation is the brightness ramp: mapping sRGB perceived luminance (0..1) to character indices matched by ink density.
Map luminance (0..1) to a character whose ink density matches. Order characters dark-to-light.
Identifies the character cell aspect ratio of ~0.5 as the #1 pitfall: sampled rows must be fewer than columns, or the image will stretch vertically.
Correct by sampling **fewer rows than columns**: for a target of `cols` characters wide, use `rows = Math.round(cols * (imgH / imgW) * fontAspect)` where `fontAspect ≈ 0.5`.
Tiered rendering pipelines based on performance cost: <pre>+textContent (simple), canvas fillText (per-character coloring/large grids/60fps), and Three.js AsciiEffect (full-scene ASCII conversion).
**Canvas `fillText`**: needed for per-character color, larger grids, or 60fps.
Terminal-side uses ANSI escape codes for a self-drawn frame loop: hide cursor, home cursor, throttle to 12–24fps, restore upon exit, noting that homing the cursor is cheaper than clearing the screen.
Use `ESC + '2J'` to clear the whole screen, `ESC + 'H'` for home (cheaper per frame than clearing).
Reproducibility: frames are driven by injectable time plus a fixed seed, where ?t=N renders only a single frame, turning screenshots into deterministic verification artifacts.
Drive frames from an **injectable time** (not `Date.now()`/`performance.now()` directly) and seed any randomness, so a frame is reproducible.
2Core Capabilities
3External Dependencies
| Kind | Dependency / Endpoint |
|---|---|
| package | sharp(自带脚本的依赖:解码 PNG/JPG/WebP 并缩放到字符网格) |
| package | three(可选:AsciiEffect 走 three/addons,交付页可从 CDN 取) |
| api | 浏览器与终端原语:canvas 2D getImageData、requestAnimationFrame、ANSI 转义序列、process.stdout |
| cli | npx / Playwright(宿主侧截图工具;SKILL.md 在 verify 段示范) |
| cli | scripts/seek-shot.sh + scripts/contact-sheet.sh + ffmpeg(仓库根打包助手,被 SKILL.md 的 Packaged helper 段点名) |
4Recommended Governance & Evidence
- scripts/: Execute local script or command: node skills/ascii-animation/scripts/img-to-ascii.mjs <image> [--cols N] [--
- 网络端点: Declare external network access: The script itself makes zero outbound calls; npm i sharp (fetching from the npm registry during installation) and optional three CDN loading in delivery pages occur at host or artifact runtime and are not factored into the rating.
- 本地工作目录: File I/O and artifact output: The built-in script writes no files (stdout only).
5Second-Pass Verification
- [ok] 自带脚本是否真的没有网络、凭证与写盘面 — img-to-ascii.mjs 全文 64 行逐行读过:import 只有 sharp;用 process.argv 解析参数;sharp(file).metadata() 与 .resize().removeAlpha().raw().toBuffer() 读像素;结果只经 process.stdout.write 输出。无 fetch、无 http 模块、无 fs 写、无 process.env,也无 shell 调用。
- [discrepancy] 坡道「16 级中等坡道被截断」的指引是否自洽 — SKILL.md 的 Common ramps 段在 Medium (16) 之后写 truncated — see table below,但该段下方最近的表是 Quick reference(无坡道表);完整 16 级坡道实际在 references/rendering.md 的 ### 16-level extended (balance)。指向错位,照抄 SKILL.md 拿不到完整坡道。
- [discrepancy] Packaged helper 的路径基准与适用性 — SKILL.md 写 scripts/seek-shot.sh anim.html 0 1.5 3,scripts/ 实际在仓库根;且本 skill 的终端形态本就不适用 ?t=N 冻结(SKILL.md 自己注明终端件需另法验收),该 helper 只覆盖浏览器形态。
- [ok] willReadFrequently 与 AsciiEffect 的取舍是否与参考文件一致 — SKILL.md 的 <pre> 与 canvas 取舍、willReadFrequently 用法、AsciiEffect 必须 effect.render 的注意事项,在 references/rendering.md 的 #### <pre> vs canvas 与 Three.js AsciiEffect 两节均可定位,前后无矛盾。
6Verdict & Scenarios
b1d5e8b21e57db26…