<!-- 0ab6f78b-28ea-4bf7-8a7d-0e9a7d9c1cf7 -->
---
todos:
  - id: "init-project"
    content: "Create project at ~/projects/thumbgen with package.json, tsconfig.json, .gitignore"
    status: pending
  - id: "implement-extract"
    content: "Create extract.ts with ffmpeg/ffprobe frame extraction"
    status: pending
  - id: "implement-overlay"
    content: "Create overlay.ts with Sharp SVG composite text overlay"
    status: pending
  - id: "implement-cli"
    content: "Create index.ts with CLI using commander"
    status: pending
  - id: "test-tool"
    content: "Test thumbgen with a sample video"
    status: pending
isProject: false
---
# Thumbgen - Instagram Reel Thumbnail Generator (TypeScript)

## Overview

A Bun CLI tool that extracts a frame from a video and overlays styled text (title + artist) to create Instagram reel thumbnails.

## Project Structure

```
~/projects/thumbgen/
├── package.json
├── tsconfig.json
├── README.md
├── .gitignore
└── src/
    ├── index.ts        # CLI entry point
    ├── extract.ts      # FFmpeg frame extraction
    └── overlay.ts      # Sharp + SVG text overlay
```

## Dependencies

- `sharp` - Fast image processing, composites SVG text overlay
- `commander` or `@clack/prompts` - CLI framework
- FFmpeg required as system dependency

## CLI Interface

```bash
# Basic usage
thumbgen video.mp4 -t "Castle of Glass" -a "Linkin Park"
# Output: video_thumb.png

# With options
thumbgen video.mp4 -t "Numb" -a "Linkin Park" -o cover.png --time 10
```

**Arguments/Options:**
- `video` - Input video file (required)
- `-t, --title` - Song title (required)
- `-a, --artist` - Artist name (required)
- `-o, --output` - Output path (default: `{video_stem}_thumb.png`)
- `--time` - Frame timestamp in seconds (default: middle of video)

## Technical Approach

### Frame Extraction (`extract.ts`)
- Use `Bun.spawn` to call ffprobe for video duration
- Use ffmpeg to extract single frame as PNG buffer
- Default to middle of video if no time specified

### Text Overlay (`overlay.ts`)
Use Sharp's SVG composite approach:

```typescript
const textSvg = `
<svg width="${width}" height="${height}">
  <defs>
    <linearGradient id="fade" x1="0" y1="0" x2="0" y2="1">
      <stop offset="0%" stop-color="transparent"/>
      <stop offset="100%" stop-color="rgba(0,0,0,0.7)"/>
    </linearGradient>
  </defs>
  <rect y="${height - 300}" width="100%" height="300" fill="url(#fade)"/>
  <text x="40" y="${height - 80}" font-size="48" fill="white" 
        font-family="system-ui" font-weight="bold">${title}</text>
  <text x="40" y="${height - 30}" font-size="28" fill="rgba(255,255,255,0.8)"
        font-family="system-ui">${artist}</text>
</svg>
`;

await sharp(frameBuffer)
  .composite([{ input: Buffer.from(textSvg), top: 0, left: 0 }])
  .toFile(output);
```

### Text Styling
- **Gradient overlay**: Bottom fade from transparent to dark
- **Title**: Large, bold, white
- **Artist**: Smaller, slightly transparent
- **Position**: Lower third, left-aligned with padding
- **Font**: System UI font (cross-platform)

## npm Publishing

Package will be published to npm for easy installation:
```bash
bunx thumbgen video.mp4 -t "Title" -a "Artist"
# or
bun add -g thumbgen
```
