Skip to content
Back to skills

Text Rain Generator

ASecurity

Generate artistic text rain animation with p5.js. Input poem/text + scene description, automatically generates background image via ChatGPT (through andy-relay), outputs complete runnable p5.js project with configurable rain physics (speed, tilt, collision).

  • 2 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added October 19, 2026
content-marketingjavascriptpythonjavabashnode

Works with

  • claude code
  • cli
  • mcp

Security analysis

A96/100
  • mediumInstalls packages at runtime which could introduce malicious dependencies

Pro scans all 18 files and shows the line behind each finding

Scanned October 19, 2026

npx -y skills add AIPMAndy/Andy-Video-Skill --skill text-rain-generator --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Text Rain Generator?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Text Rain Generator
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/aipmandy-text-rain-generator/badge)](https://www.skillsdirectory.com/skills/aipmandy-text-rain-generator)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: text-rain-generator
description: Generate artistic text rain animation with p5.js. Input poem/text + scene description, automatically generates background image via ChatGPT (through andy-relay), outputs complete runnable p5.js project with configurable rain physics (speed, tilt, collision).
---

# Text Rain Generator

将诗句/文字转换成艺术化的"文字雨"动效,参考景桐的 Vibe Coding 风格。

## 核心能力

1. **智能生图**:通过 `/andy-relay` 调度 ChatGPT 生成黑白老照片风格的背景图
2. **物理引擎**:p5.js 粒子系统模拟真实雨滴效果(重力、风、碰撞)
3. **参数化控制**:可调节雨速、倾斜、颜色、布局等
4. **即开即用**:输出完整 HTML 文件,浏览器直接运行

## 使用方式

### 基础模式(最快)
```
/text-rain-generator "床前明月光,疑是地上霜" --scene "江南老宅屋檐"
```

### 高级模式(精细调参)
```
/text-rain-generator "山重水复疑无路,柳暗花明又一村" \
  --scene "苏州园林青瓦屋檐,斑驳墙壁" \
  --speed 3 \
  --tilt 15 \
  --color "#e8d5b7" \
  --no-preview
```

## 参数说明

| 参数 | 默认值 | 说明 |
|------|--------|------|
| `--scene` | "中式老宅屋檐" | 背景场景描述,传递给 ChatGPT 生图 |
| `--speed` | 2.5 | 雨滴下落速度(1-5) |
| `--tilt` | 10 | 最大倾斜角度(度) |
| `--color` | "#d4c5a9" | 文字颜色(十六进制) |
| `--font-size` | 24 | 字体大小(px) |
| `--canvas-width` | 1080 | 画布宽度 |
| `--canvas-height` | 1920 | 画布高度(默认竖屏) |
| `--no-preview` | false | 不自动打开浏览器预览 |
| `--output` | `./text-rain-output/` | 输出目录 |

## 工作流程

### 第1步:解析输入
- 拆解诗句为字符数组
- 验证参数合法性
- 生成项目文件夹结构

### 第2步:生成背景图(通过 andy-relay)

调用 `/andy-relay` 发送任务到 ChatGPT:

```json
{
  "objective": "Generate background image for text rain animation",
  "deliverable": "One PNG image, 1080x1920, black and white old photo style",
  "context": {
    "scene": "{用户提供的场景描述}",
    "style": "monochrome, vintage, film grain, high contrast",
    "composition": "bottom 2/3 shows roof tiles/eaves, top 1/3 empty for rain"
  },
  "prompt": "Generate a black and white vintage photograph: {scene}. Composition: traditional Chinese roof tiles occupy bottom 60%, weathered texture with visible gaps between tiles. Top 40% is empty sky/wall for text overlay. High contrast, film grain, 1980s photo quality. Vertical format 1080x1920.",
  "acceptance": [
    "Image resolution exactly 1080x1920",
    "Monochrome (no color)",
    "Clear roof/eaves structure in bottom portion",
    "Sufficient empty space at top"
  ]
}
```

**重要**:
- 使用 ChatGPT 的 DALL-E 3 生图能力
- 下载生成的图片到项目 `assets/` 目录
- 验证图片尺寸和风格

### 第3步:生成 p5.js 代码

创建核心文件:

#### `index.html`
```html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Text Rain - {首句诗}</title>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.7.0/p5.min.js"></script>
  <script src="sketch.js"></script>
  <style>
    body {
      margin: 0;
      padding: 0;
      display: flex;
      justify-content: center;
      align-items: center;
      min-height: 100vh;
      background: #1a1a1a;
    }
    canvas {
      box-shadow: 0 4px 20px rgba(0,0,0,0.5);
    }
  </style>
</head>
<body></body>
</html>
```

#### `sketch.js`
核心逻辑:

```javascript
// 全局变量
let particles = [];
let backgroundImg;
let poem = []; // 诗句字符数组
let config = {
  speed: {SPEED},
  tilt: {TILT},
  color: '{COLOR}',
  fontSize: {FONT_SIZE},
  canvasWidth: {CANVAS_WIDTH},
  canvasHeight: {CANVAS_HEIGHT}
};

// p5.js 生命周期
function preload() {
  backgroundImg = loadImage('assets/background.png');
}

function setup() {
  createCanvas(config.canvasWidth, config.canvasHeight);
  textFont('STKaiti'); // 楷体,适合中文诗词
  textSize(config.fontSize);
  textAlign(CENTER, CENTER);
  
  // 初始化粒子
  poem = '{POEM_TEXT}'.split('');
  for (let i = 0; i < poem.length * 3; i++) {
    particles.push(new RainDrop(random(poem)));
  }
}

function draw() {
  // 渲染背景
  image(backgroundImg, 0, 0, width, height);
  
  // 更新和渲染粒子
  for (let p of particles) {
    p.update();
    p.display();
    
    // 循环利用粒子
    if (p.y > height) {
      p.reset();
    }
  }
}

// 雨滴类
class RainDrop {
  constructor(char) {
    this.char = char;
    this.reset();
  }
  
  reset() {
    this.x = random(width * 0.1, width * 0.9); // 左右留白
    this.y = random(-500, -50);
    this.speed = random(config.speed * 0.8, config.speed * 1.2);
    this.rotation = random(-config.tilt, config.tilt);
    this.wobble = random(-0.5, 0.5); // 左右摆动
    this.alpha = random(150, 255);
  }
  
  update() {
    // 重力加速
    this.y += this.speed;
    this.x += sin(this.y * 0.01) * this.wobble;
    
    // 碰撞检测:屋檐区域(底部 60%)
    if (this.y > height * 0.4) {
      let imgColor = backgroundImg.get(this.x, this.y);
      let brightness = (imgColor[0] + imgColor[1] + imgColor[2]) / 3;
      
      // 碰到深色区域(瓦片)改变轨迹
      if (brightness < 100) {
        this.speed *= 0.95; // 减速
        this.x += random(-2, 2); // 随机溅开
        this.rotation += random(-5, 5);
      }
    }
  }
  
  display() {
    push();
    translate(this.x, this.y);
    rotate(radians(this.rotation));
    fill(config.color + hex(this.alpha, 2));
    noStroke();
    text(this.char, 0, 0);
    pop();
  }
}
```

### 第4步:输出和验证

生成文件结构:
```
text-rain-output/
├── index.html
├── sketch.js
├── assets/
│   └── background.png
├── README.md
└── preview.sh  # 快速启动脚本
```

#### `README.md`
```markdown
# 文字雨动效 - {诗句首句}

## 快速运行

### 方法1:直接打开
双击 `index.html` 在浏览器中打开

### 方法2:本地服务器(推荐)
```bash
# Python 3
python -m http.server 8000

# 或使用 Node.js
npx serve .
```

访问 http://localhost:8000

## 录屏导出

### macOS
⌘ + Shift + 5 选择录制区域

### Windows
Win + G 打开 Xbox Game Bar

### 通用方案
使用 OBS Studio 录制浏览器窗口

## 参数调整

编辑 `sketch.js` 中的 `config` 对象:

- `speed`: 雨速(1-5)
- `tilt`: 倾斜角度(0-30)
- `color`: 文字颜色
- `fontSize`: 字号

## 技术栈

- p5.js 1.7.0
- 纯前端,无需构建
```

#### `preview.sh`
```bash
#!/bin/bash
# 自动启动本地服务器并打开浏览器

echo "🌧️  启动文字雨预览..."

# 检测可用工具
if command -v python3 &> /dev/null; then
    echo "使用 Python 服务器 (http://localhost:8000)"
    python3 -m http.server 8000 &
    SERVER_PID=$!
    sleep 2
    open http://localhost:8000
elif command -v npx &> /dev/null; then
    echo "使用 Node.js serve"
    npx serve . -l 8000 &
    SERVER_PID=$!
    sleep 2
    open http://localhost:8000
else
    echo "⚠️  未找到 Python 或 Node.js,请手动打开 index.html"
    open index.html
    exit 0
fi

echo "✅ 预览已启动!按 Ctrl+C 停止服务器"
trap "kill $SERVER_PID" EXIT
wait $SERVER_PID
```

## 验证清单

自动执行以下检查:

- [ ] 背景图已下载且尺寸正确(1080x1920)
- [ ] 诗句文字已正确拆分
- [ ] `sketch.js` 语法无错误
- [ ] 浏览器控制台无报错
- [ ] 粒子系统正常运行(至少100帧无卡顿)
- [ ] 碰撞效果触发正常

如果 `--no-preview` 未设置,自动通过 WebBridge 打开预览。

## 故障排查

### 图片生成失败
- **原因**:ChatGPT 限流/登录过期
- **解决**:手动上传 1080x1920 黑白屋檐图到 `assets/background.png`

### 中文乱码
- **原因**:字体未加载
- **解决**:在 `setup()` 中指定系统字体或使用 Web Font

### 性能卡顿
- **原因**:粒子数过多
- **解决**:减少 `particles` 初始化数量(默认 `poem.length * 3`)

## 进阶扩展

### 添加音效
```javascript
let rainSound;
function preload() {
  backgroundImg = loadImage('assets/background.png');
  rainSound = loadSound('assets/rain.mp3');
}
function setup() {
  // ...
  rainSound.loop();
}
```

### 导出视频(Node.js)
使用 `p5.js` + `canvas` 库逐帧导出:
```bash
npm install canvas p5
node export-frames.js  # 生成帧序列
ffmpeg -i frame_%04d.png -c:v libx264 output.mp4
```

## Claude Code 执行流程

当用户调用 `/text-rain-generator` 时,按以下步骤执行:

### Step 1: 解析参数并生成项目

```bash
cd ~/.claude/skills/text-rain-generator
node cli.ts "{用户提供的诗句}" {其他参数}
```

CLI 会输出 JSON 格式的结果,包含:
- `outputDir`: 项目输出目录
- `taskPacketPath`: ChatGPT 任务包路径
- `backgroundPath`: 背景图目标路径
- `indexPath`: HTML 入口文件路径
- `config`: 完整配置对象

### Step 2: 调用 andy-relay 生成背景图

读取 `taskPacketPath` 中的任务包,通过 `/andy-relay` 调度 ChatGPT:

```javascript
// 伪代码示例
const taskPacket = JSON.parse(fs.readFileSync(taskPacketPath));

// 调用 andy-relay
await invokeSkill('andy-relay', {
  provider: 'chatgpt',
  task: taskPacket,
  downloadTo: backgroundPath,
});
```

**重要**: 
- 使用 WebBridge 工具与 ChatGPT 交互
- 等待图片生成完成(可能需要 10-30 秒)
- 从 ChatGPT 界面下载图片到 `backgroundPath`
- 验证图片尺寸和格式

### Step 3: 验证背景图

```bash
# 检查文件是否存在
ls -lh {backgroundPath}

# 验证图片尺寸(需要 ImageMagick 或用 Node.js)
identify {backgroundPath}  # 或用 sharp 库
```

确认:
- 文件存在且大小 > 100KB
- 尺寸符合配置(默认 1080×1920)
- 格式为 PNG 或 JPG

### Step 4: 启动预览(如果未设置 --no-preview)

```bash
cd {outputDir}
./preview.sh
```

或通过 WebBridge 打开:

```javascript
// 使用 mcp__webbridge__navigate
await webbridge.navigate({
  url: 'http://localhost:8000/index.html',
  session: 'text-rain-preview',
});

// 等待 2 秒让页面加载
await sleep(2000);

// 截图验证
await webbridge.screenshot({
  session: 'text-rain-preview',
  path: `{outputDir}/preview.png`,
});
```

### Step 5: 报告结果

向用户报告:
- ✅ 项目已生成到: `{outputDir}`
- ✅ 背景图已生成: `{backgroundPath}`
- ✅ 预览已启动: `http://localhost:8000`
- 📝 使用说明: 查看 `{outputDir}/README.md`

如果某步失败,提供清晰的错误信息和解决方案。

## 错误处理

### ChatGPT 生图失败
- **症状**: 任务包已发送,但 ChatGPT 返回错误或超时
- **解决**: 
  1. 检查 ChatGPT 账号是否有 DALL-E 3 权限
  2. 简化场景描述(去掉复杂细节)
  3. 让用户手动上传背景图到 `assets/background.png`

### 图片尺寸不匹配
- **症状**: 下载的图片尺寸与配置不符
- **解决**: 使用 ImageMagick 或 sharp 库调整尺寸
  ```bash
  convert {backgroundPath} -resize 1080x1920! {backgroundPath}
  ```

### WebBridge 连接失败
- **症状**: `mcp__webbridge__*` 工具报错
- **解决**: 
  1. 确认 Kimi WebBridge 服务运行中
  2. 运行 `~/.kimi-webbridge/bin/kimi-webbridge start`
  3. 如果仍失败,降级为手动打开浏览器

### 预览页面空白
- **症状**: 浏览器打开但只看到黑屏
- **解决**:
  1. 打开浏览器控制台查看错误
  2. 检查 `assets/background.png` 是否存在
  3. 验证 `sketch.js` 语法无错误
  4. 确认本地服务器正常运行(非直接打开 HTML)

## 灵感来源

- 景桐 Vibe Coding 文字雨案例
- p5.js 官方粒子系统示例
- 日本枯山水美学 + 中式诗词意境

Files in this skill

  • CHANGELOG.md7.5 KB
  • CHEATSHEET.md7.3 KB
  • DEVELOPER.md8.3 KB
  • README.md8.5 KB
  • SKILL.md11.7 KB
  • USAGE.md8.9 KB
  • batch-generate.ts8.4 KB
  • bun.lock4.4 KB
  • cli.ts9.6 KB
  • export-video.ts8.7 KB
  • generate.ts17.1 KB
  • package-lock.json20.2 KB
  • package.json905 B
  • presets.ts9.3 KB
  • quick-start.sh2.6 KB
  • sound-effects.ts5.5 KB
  • test.ts4.7 KB
  • tsconfig.json475 B

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.