Skip to main content

Pixel Art

Convert images into retro pixel art with hardware-accurate palettes (NES, Game Boy, PICO-8, C64, etc.), and animate them into short videos. Presets cover arcade, SNES, and 10+ era-correct looks. Use clarify to let the user pick a style before generating.

Skill metadata

SourceBundled (installed by default)
Pathskills/creative/pixel-art
Version2.0.0
Authordodo-reach
LicenseMIT
Tagscreative, pixel-art, arcade, snes, nes, gameboy, retro, image, video

Reference: full SKILL.md

info

The following is the complete skill definition that Hermes loads when this skill is triggered. This is what the agent sees as instructions when the skill is active.

Pixel Art

Convert any image into retro pixel art, then optionally animate it into a short MP4 or GIF with era-appropriate effects (rain, fireflies, snow, embers).

Two scripts ship with this skill:

  • scripts/pixel_art.py — photo → pixel-art PNG (Floyd-Steinberg dithering)
  • scripts/pixel_art_video.py — pixel-art PNG → animated MP4 (+ optional GIF)

Each is importable or runnable directly. Presets snap to hardware palettes when you want era-accurate colors (NES, Game Boy, PICO-8, etc.), or use adaptive N-color quantization for arcade/SNES-style looks.

When to Use

  • User wants retro pixel art from a source image
  • User asks for NES / Game Boy / PICO-8 / C64 / arcade / SNES styling
  • User wants a short looping animation (rain scene, night sky, snow, etc.)
  • Posters, album covers, social posts, sprites, characters, avatars

Workflow

Before generating, confirm the style with the user. Different presets produce very different outputs and regenerating is costly.

Step 1 — Offer a style

Call clarify with 4 representative presets. Pick the set based on what the user asked for — don't just dump all 14.

Default menu when the user's intent is unclear:

clarify(
question="Which pixel-art style do you want?",
choices=[
"arcade — bold, chunky 80s cabinet feel (16 colors, 8px)",
"nes — Nintendo 8-bit hardware palette (54 colors, 8px)",
"gameboy — 4-shade green Game Boy DMG",
"snes — cleaner 16-bit look (32 colors, 4px)",
],
)

When the user already named an era (e.g. "80s arcade", "Gameboy"), skip clarify and use the matching preset directly.

Step 2 — Offer animation (optional)

If the user asked for a video/GIF, or the output might benefit from motion, ask which scene:

clarify(
question="Want to animate it? Pick a scene or skip.",
choices=[
"night — stars + fireflies + leaves",
"urban — rain + neon pulse",
"snow — falling snowflakes",
"skip — just the image",
],
)

Do NOT call clarify more than twice in a row. One for style, one for scene if animation is on the table. If the user explicitly asked for a specific style and scene in their message, skip clarify entirely.

Step 3 — Generate

Run pixel_art() first; if animation was requested, chain into pixel_art_video() on the result.

Preset Catalog

PresetEraPaletteBlockBest for
arcade80s arcadeadaptive 168pxBold posters, hero art
snes16-bitadaptive 324pxCharacters, detailed scenes
nes8-bitNES (54)8pxTrue NES look
gameboyDMG handheld4 green shades8pxMonochrome Game Boy
gameboy_pocketPocket handheld4 grey shades8pxMono GB Pocket
pico8PICO-816 fixed6pxFantasy-console look
c64Commodore 6416 fixed8px8-bit home computer
apple2Apple II hi-res6 fixed10pxExtreme retro, 6 colors
teletextBBC Teletext8 pure10pxChunky primary colors
mspaintWindows MS Paint24 fixed8pxNostalgic desktop
mono_greenCRT phosphor2 green6pxTerminal/CRT aesthetic
mono_amberCRT amber2 amber6pxAmber monitor look
neonCyberpunk10 neons6pxVaporwave/cyber
pastelSoft pastel10 pastels6pxKawaii / gentle

Named palettes live in scripts/palettes.py (see references/palettes.md for the complete list — 28 named palettes total). Any preset can be overridden:

pixel_art("in.png", "out.png", preset="snes", palette="PICO_8", block=6)

Scene Catalog (for video)

SceneEffects
nightTwinkling stars + fireflies + drifting leaves
duskFireflies + sparkles
tavernDust motes + warm sparkles
indoorDust motes
urbanRain + neon pulse
natureLeaves + fireflies
magicSparkles + fireflies
stormRain + lightning
underwaterBubbles + light sparkles
fireEmbers + sparkles
snowSnowflakes + sparkles
desertHeat shimmer + dust

Invocation Patterns

Python (import)

import sys
sys.path.insert(0, "/home/teknium/.hermes/skills/creative/pixel-art/scripts")
from pixel_art import pixel_art
from pixel_art_video import pixel_art_video

# 1. Convert to pixel art
pixel_art("/path/to/photo.jpg", "/tmp/pixel.png", preset="nes")

# 2. Animate (optional)
pixel_art_video(
"/tmp/pixel.png",
"/tmp/pixel.mp4",
scene="night",
duration=6,
fps=15,
seed=42,
export_gif=True,
)

CLI

cd /home/teknium/.hermes/skills/creative/pixel-art/scripts

python pixel_art.py in.jpg out.png --preset gameboy
python pixel_art.py in.jpg out.png --preset snes --palette PICO_8 --block 6

python pixel_art_video.py out.png out.mp4 --scene night --duration 6 --gif

Pipeline Rationale

Pixel conversion:

  1. Boost contrast/color/sharpness (stronger for smaller palettes)
  2. Posterize to simplify tonal regions before quantization
  3. Downscale by block with Image.NEAREST (hard pixels, no interpolation)
  4. Quantize with Floyd-Steinberg dithering — against either an adaptive N-color palette OR a named hardware palette
  5. Upscale back with Image.NEAREST

Quantizing AFTER downscale keeps dithering aligned with the final pixel grid. Quantizing before would waste error-diffusion on detail that disappears.

Video overlay:

  • Copies the base frame each tick (static background)
  • Overlays stateless-per-frame particle draws (one function per effect)
  • Encodes via ffmpeg libx264 -pix_fmt yuv420p -crf 18
  • Optional GIF via palettegen + paletteuse

Dependencies

  • Python 3.9+
  • Pillow (pip install Pillow)
  • ffmpeg on PATH (only needed for video — Hermes installs package this)

Pitfalls

  • Pallet keys are case-sensitive ("NES", "PICO_8", "GAMEBOY_ORIGINAL").
  • Very small sources (<100px wide) collapse under 8-10px blocks. Upscale the source first if it's tiny.
  • Fractional block or palette will break quantization — keep them positive ints.
  • Animation particle counts are tuned for ~640x480 canvases. On very large images you may want a second pass with a different seed for density.
  • mono_green / mono_amber force color=0.0 (desaturate). If you override and keep chroma, the 2-color palette can produce stripes on smooth regions.
  • clarify loop: call it at most twice per turn (style, then scene). Don't pepper the user with more picks.

Verification

  • PNG is created at the output path
  • Clear square pixel blocks visible at the preset's block size
  • Color count matches preset (eyeball the image or run Image.open(p).getcolors())
  • Video is a valid MP4 (ffprobe can open it) with non-zero size

Attribution

Named hardware palettes and the procedural animation loops in pixel_art_video.py are ported from pixel-art-studio (MIT). See ATTRIBUTION.md in this skill directory for details.