/image
Generate a meme by overlaying text on an image. Use when asked to make a meme, caption an image, add meme text, or produce classic Impact macros, TV-subtitle stills, white-bar captions, or multi-label formats (Drake, distracted boyfriend, whiteboard).
$ npx -y skills add bendrucker/claude --skill image --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
- You can call itInvoke it directly when you want it.
- Slash command
/image
Context preview
The summary Claude sees to decide when to auto-load this skill.
Generate a meme by overlaying text on an image. Use when asked to make a meme, caption an image, add meme text, or produce classic Impact macros, TV-subtitle stills, white-bar captions, or multi-label formats (Drake, distracted boyfriend, whiteboard).
SKILL.md
image.SKILL.mdname: image
description: Generate a meme by overlaying text on an image. Use when asked to make a meme, caption an image, add meme text, or produce classic Impact macros, TV-subtitle stills, white-bar captions, or multi-label formats (Drake, distracted boyfriend, whiteboard).
argument-hint: "[image-path] [meme text or idea]"
allowed-tools:
- "Bash(bun ${CLAUDE_SKILL_DIR}/scripts/render.ts:*)"
hooks:
PreToolUse:
- matcher: "Bash(bun ${CLAUDE_SKILL_DIR}/scripts/render.ts:*)"
hooks:
- type: command
command: "bun ${CLAUDE_PLUGIN_ROOT}/skills/image/scripts/setup.ts"
once: trueMeme Image
Overlay meme text on an image with a deterministic renderer. You judge layout, placement, and legibility; the script guarantees the classic look (Impact stroke, subtitle yellow, caption bars).
Workflow
1. Resolve the image. Use the user's path directly. If they name a meme format instead of a path, check the template library first (below). Otherwise WebSearch for the image and download with `curl -L -o tmp/<name>.jpg <url>`. Never pass a URL to the renderer. 2. Read the image. Note the subjects, where faces and action are, contrast, and aspect ratio. 3. Choose a mode:
- Top/bottom macro text: `--top` / `--bottom`
- TV-subtitle quote (yellow, bottom center): `--subtitle`
- White bar above or below the image: `--caption` (optionally `--caption-position top|bottom`, default top)
- Multi-label formats or custom placement: write a JSON spec file. Read [references/spec.md](references/spec.md) first.
4. Pick the output filename. It is part of the joke: witty and meme-relevant, kebab or snake case, `.png`. Never `meme.png` or another generic name. If the user supplies a filename, use theirs. 5. Render, then Read the output PNG. Check legibility, that text does not cover key subjects, line-break placement, and any fit warnings on stderr. Adjust and re-render until it reads well. 6. Deliver: copy the file reference to the clipboard so a paste keeps the filename:
osascript -e 'set the clipboard to POSIX file "<absolute output path>"'
Report the path. If the clipboard copy is declined or fails, reveal the file instead: `open -R <path>`.
Template Library
`${CLAUDE_PLUGIN_DATA}/templates/` holds the user's meme templates, synced outside git. Never copy its images into the repo. Each image may have a sidecar spec of the same basename (`drake.jpg` + `drake.json`): a ready-made layout whose `text` values are `<slot descriptions>` and whose `description` says how the format works.
- User names a format: `ls` the library and match by filename. With a sidecar, copy it to `tmp/`, replace each slot with the actual joke, and render with `--spec`. Panel counts matter: fill every slot the joke needs and drop boxes the format leaves empty.
- No sidecar (new template): Read the image, trace regions, render, iterate. Once the layout looks right, save it back as a sidecar with `<slot description>` placeholders so the next use skips the tracing.
Rendering
bun ${CLAUDE_SKILL_DIR}/scripts/render.ts <image> [flags] -o tmp/<joke-name>.png| Flag | Meaning | |------|---------| | `--top`, `--bottom` | Classic Impact macro text | | `--subtitle` | TV-subtitle text at the bottom | | `--caption` | White-bar caption (expands the canvas); `--caption-position top\|bottom` | | `--spec`, `-s` | JSON layout spec file (mutually exclusive with the text flags) | | `--out`, `-o` | Output PNG path (always required) |
On success the script prints the absolute output path. Fit problems are warnings, not errors: the text still renders at the shrink floor. Treat every warning as a cue to shorten text or enlarge its region.
Gotchas
- Web search returns template *pages*, not image files, and meme-site pages are often JS-rendered so the image URL is not in the static HTML. Imgflip: the direct image is `https://i.imgflip.com/<id36>.jpg` where `<id36>` is the numeric template ID from the page URL in base36 (`(55311130).toString(36)` → `wxica`). Always `file` the download to confirm it is an image before rendering.
- Multi-panel formats: set `"linkFontSizes": true` in the spec so all panels share one font size instead of each fitting independently.
- Images narrower than 400px are auto-upscaled 2x before drawing, so tiny templates still get readable text. Expect output dimensions to differ from the input.
- Spec coordinates and font sizes are normalized (0-1 fractions of image dimensions). Judge placement from the Read image in fractions, not pixels: Read may downscale.
Read more
name: image
description: Generate a meme by overlaying text on an image. Use when asked to make a meme, caption an image, add meme text, or produce classic Impact macros, TV-subtitle stills, white-bar captions, or multi-label formats (Drake, distracted boyfriend, whiteboard).
argument-hint: "[image-path] [meme text or idea]"
allowed-tools:
- "Bash(bun ${CLAUDE_SKILL_DIR}/scripts/render.ts:*)"
hooks:
PreToolUse:
- matcher: "Bash(bun ${CLAUDE_SKILL_DIR}/scripts/render.ts:*)"
hooks:
- type: command
command: "bun ${CLAUDE_PLUGIN_ROOT}/skills/image/scripts/setup.ts"
once: trueMeme Image
Overlay meme text on an image with a deterministic renderer. You judge layout, placement, and legibility; the script guarantees the classic look (Impact stroke, subtitle yellow, caption bars).
Workflow
1. Resolve the image. Use the user's path directly. If they name a meme format instead of a path, check the template library first (below). Otherwise WebSearch for the image and download with `curl -L -o tmp/<name>.jpg <url>`. Never pass a URL to the renderer. 2. Read the image. Note the subjects, where faces and action are, contrast, and aspect ratio. 3. Choose a mode:
- Top/bottom macro text: `--top` / `--bottom`
- TV-subtitle quote (yellow, bottom center): `--subtitle`
- White bar above or below the image: `--caption` (optionally `--caption-position top|bottom`, default top)
- Multi-label formats or custom placement: write a JSON spec file. Read [references/spec.md](references/spec.md) first.
4. Pick the output filename. It is part of the joke: witty and meme-relevant, kebab or snake case, `.png`. Never `meme.png` or another generic name. If the user supplies a filename, use theirs. 5. Render, then Read the output PNG. Check legibility, that text does not cover key subjects, line-break placement, and any fit warnings on stderr. Adjust and re-render until it reads well. 6. Deliver: copy the file reference to the clipboard so a paste keeps the filename:
osascript -e 'set the clipboard to POSIX file "<absolute output path>"'
Report the path. If the clipboard copy is declined or fails, reveal the file instead: `open -R <path>`.
Template Library
`${CLAUDE_PLUGIN_DATA}/templates/` holds the user's meme templates, synced outside git. Never copy its images into the repo. Each image may have a sidecar spec of the same basename (`drake.jpg` + `drake.json`): a ready-made layout whose `text` values are `<slot descriptions>` and whose `description` says how the format works.
- User names a format: `ls` the library and match by filename. With a sidecar, copy it to `tmp/`, replace each slot with the actual joke, and render with `--spec`. Panel counts matter: fill every slot the joke needs and drop boxes the format leaves empty.
- No sidecar (new template): Read the image, trace regions, render, iterate. Once the layout looks right, save it back as a sidecar with `<slot description>` placeholders so the next use skips the tracing.
Rendering
bun ${CLAUDE_SKILL_DIR}/scripts/render.ts <image> [flags] -o tmp/<joke-name>.png| Flag | Meaning | |------|---------| | `--top`, `--bottom` | Classic Impact macro text | | `--subtitle` | TV-subtitle text at the bottom | | `--caption` | White-bar caption (expands the canvas); `--caption-position top\|bottom` | | `--spec`, `-s` | JSON layout spec file (mutually exclusive with the text flags) | | `--out`, `-o` | Output PNG path (always required) |
On success the script prints the absolute output path. Fit problems are warnings, not errors: the text still renders at the shrink floor. Treat every warning as a cue to shorten text or enlarge its region.
Gotchas
- Web search returns template *pages*, not image files, and meme-site pages are often JS-rendered so the image URL is not in the static HTML. Imgflip: the direct image is `https://i.imgflip.com/<id36>.jpg` where `<id36>` is the numeric template ID from the page URL in base36 (`(55311130).toString(36)` → `wxica`). Always `file` the download to confirm it is an image before rendering.
- Multi-panel formats: set `"linkFontSizes": true` in the spec so all panels share one font size instead of each fitting independently.
- Images narrower than 400px are auto-upscaled 2x before drawing, so tiny templates still get readable text. Expect output dimensions to differ from the input.
- Spec coordinates and font sizes are normalized (0-1 fractions of image dimensions). Judge placement from the Read image in fractions, not pixels: Read may downscale.
My personal plugin marketplace for Claude Code, Anthropic's AI coding assistant.
Repo: bendrucker/claude
Other skills on bendrucker-claude.
cleye
Type-safe CLI argument parsing with cleye, the standard parser for this repo's Bun scripts. Use when writing or editing any script that takes arguments (flags,…
activity
Report real device usage from ActivityWatch. Covers per-app time, window titles, and active vs idle spans. Use when asked "what apps did I use", "how long was…
history
Report shell history from atuin's local capture. Covers what commands ran, when, where, and how they exited. Use when asked "what commands did I run", "what…
bun
Bun runtime patterns. Use when running bun commands, working with package.json/bun.lock, writing TypeScript scripts under Bun, or developing Claude Code…
agent-team
Orchestrating Claude Code agent teams. Use when creating teams, spawning teammates, assigning tasks, configuring teammate modes, or setting up team quality…

