npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

gif2asciiart

v1.0.0

Published

Zero-dependency GIF89a parser + GIF to ASCII animation library for browser and Node.js

Readme

gif2asciiart

GIF 转 ASCIIART 动画库

特性

  • 完整帧合成 — 正确处理 none / background / previous 三种 disposal 模式
  • LZW 解压 — 变长编码 + clear/EOI + 链表字典(O(k) 展开)
  • ASCII 映射 — 可配置字符池 + 灰度 LUT 预计算 + 亮度阈值过滤
  • 双格式产物 — esbuild 打包 ESM + IIFE(浏览器全局),含 sourcemap

安装

npm install gif2asciiart

或直接使用构建产物(不经过 npm):

git clone <repo>
cd gif2asciiart
npm install
npm run build   # 产出 dist/

用法

Node.js / ESM

import { gifToAscii } from 'gif2asciiart';
import { readFileSync } from 'fs';

const buf = readFileSync('animation.gif');
const ab = buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);

const data = gifToAscii(ab, {
  sampleX: 4,
  sampleY: 4,
  brightnessCutoff: 50,
  asciiChars: { dark: ['0', '1'], gray: ['.', '-'], light: [' '] },
});

for (const frame of data.frames) {
  console.log(frame.c); // ASCII 字符串,长度 w*h
  console.log(`延迟 ${frame.d}ms`);
}

浏览器(IIFE 全局)

<script src="gif2asciiart.browser.js"></script>
<script>
  // 通过 input[type=file] / 拖拽 / 粘贴获取 File 对象
  const reader = new FileReader();
  reader.onload = () => {
    const data = gif2asciiart.gifToAscii(reader.result, { sampleX: 4, sampleY: 4 });
    console.log(data.w, data.h, data.total);
  };
  reader.readAsArrayBuffer(file);
</script>

注意:file:// 协议下 fetch('x.gif') 会失败,务必使用 FileReader 读取本地文件。

浏览器(ESM)

<script type="module">
  import { gifToAscii } from './gif2asciiart.esm.js';
</script>

构建

npm run build

产出(dist/):

| 文件 | 格式 | 全局变量 | |------|------|----------| | gif2asciiart.esm.js | ESM | — | | gif2asciiart.browser.js | IIFE(压缩) | gif2asciiart | | *.js.map | sourcemap | — |


API

gifToAscii(arrayBuffer, opts?)

核心函数。Node.js 与浏览器通用。

| 参数 | 类型 | 默认 | 说明 | |------|------|------|------| | arrayBuffer | ArrayBuffer | — | 原始 GIF 二进制数据 | | opts.sampleX | number | 2 | 水平采样间隔 (1-1000) | | opts.sampleY | number | 2 | 垂直采样间隔 (1-1000) | | opts.brightnessCutoff | number | 0 | 亮度阈值 (0-255),灰度低于此值渲染为空格 | | opts.asciiChars | {dark, gray, light} | 见下 | 按亮度分类的字符数组 | | opts.grayscaleMap | Array<{max, pool}> | 见下 | 灰度区间 → 字符池映射 | | opts.invertChars | boolean | false | 反转字符集,暗部用 light 池,亮部用 dark 池 |

默认字符池:

{ dark: ['0', '1'], gray: ['.', '-'], light: [' '] }

默认灰度映射:

[
  { max: 85,  pool: 'dark'  },  // 0-85 → dark 池
  { max: 170, pool: 'gray'  },  // 86-170 → gray 池
  { max: 255, pool: 'light' },  // 171-255 → light 池
]

返回值:

{
  w: number;       // 采样后宽度(字符列数)
  h: number;       // 采样后高度(字符行数)
  ow: number;      // 原始像素宽度
  oh: number;      // 原始像素高度
  total: number;   // 总帧数
  loop: number;    // 循环次数 (0=无限)
  frames: Array<{
    c: string;     // 扁平 ASCII 字符串,长度 w*h,按 c[y*w + x] 索引
    d: number;     // 帧延迟(毫秒)
  }>;
}

过滤参数

brightnessCutoff — 灰度值(0.299R + 0.587G + 0.114B)低于此阈值的像素渲染为空格,用于提亮画面、去除噪点。


技术细节

Disposal Method(处理方式)

GIF 动画的每一帧可能不是完整的图像,而是一个需要绘制到特定位置的"补丁"。disposalType 决定了在绘制下一帧之前如何处理当前画布:

| 值 | 含义 | 处理方式 | |----|------|----------| | 0 | 未指定 | 不做任何处理 | | 1 | 保留 | 下一帧直接覆盖绘制到当前画布上(最常见) | | 2 | 恢复背景 | 绘制完当前帧后,将画布恢复为背景色 | | 3 | 恢复前一帧 | 绘制完当前帧后,将画布恢复为前一帧的状态 |

本库正确处理所有 disposal 类型,确保动画显示正确。

Transparency(透明度)

GIF 支持通过透明索引指定某种颜色为透明。当帧的 transparentIndex 被定义时,像素数据中匹配该索引的像素不会被绘制。在转换为 ASCII 时,透明像素会被保留为空白或背景字符。

Interlacing(隔行扫描)

GIF 支持隔行扫描存储,像素数据按 4-pass 方式排列:

Pass 1: 行 0, 8, 16, 24... (起点 0,步长 8)
Pass 2: 行 4, 12, 20, 28... (起点 4,步长 8)
Pass 3: 行 2, 6, 10, 14... (起点 2,步长 4)
Pass 4: 行 1, 3, 5, 7...  (起点 1,步长 2)

本库自动检测并处理隔行扫描的 GIF,输出时已还原为正常行顺序。

LZW 解压

GIF 使用 LZW (Lempel-Ziv-Welch) 压缩算法。本库实现了完整的 LZW 解压器:

  • 变长编码(从 minCodeSize+1 开始,最大 12 位)
  • Clear Code 和 End-of-Information Code 处理
  • 链表字典实现,O(k) 时间展开每个码字

字符映射原理

像素 RGB → gray = 0.299R + 0.587G + 0.114B → [0, 255]
                                      ↓ LUT 查表 (256项预计算)
暗 ───────── 灰 ─────────── 亮
 0           85           171          255
[0, 1]     [., -]         [ ]

buildMapper 在调用时一次性构建 256 项查找表,后续每个采样点 O(1) 映射。


项目结构

gif2asciiart/
├── src/
│   └── converter.js       核心:GIF解析 + LZW + 去隔行 + 帧合成 + ASCII 映射
├── dist/                  构建产物(ESM + IIFE + sourcemap)
├── build.js               esbuild 构建脚本
├── package.json
└── LICENSE