Pixel Art Skill for Hermes Agent: Retro Image and Video Guide
Pixel art w/ era palettes (NES, Game Boy, PICO-8).
Written by Neura Market from the official Hermes Agent documentation for Pixel Art. Commands, paths, and version numbers are reproduced from the source unchanged.
Read the official documentationThe Pixel Art skill turns any source image into a retro pixel-art PNG, then optionally animates that result into a short MP4 or GIF with era-appropriate effects like rain, fireflies, snow, or embers. It is an optional skill for Hermes Agent, so you install it when you need it. Reach for it when a user asks for NES, Game Boy, PICO-8, C64, arcade, or SNES styling, or when a poster, album cover, social post, sprite, character, or avatar would benefit from a deliberate low-resolution look.
What it does
Two scripts do the work. scripts/pixel_art.py handles the photo-to-pixel-art conversion and applies Floyd-Steinberg dithering. scripts/pixel_art_video.py takes a pixel-art PNG and produces an animated MP4, with an optional GIF. Both scripts can be imported as Python modules or run directly from the command line.
The conversion supports two families of color treatment. Presets can snap to hardware palettes for era-accurate colors, or you can use adaptive N-color quantization for arcade and SNES-style looks. The skill ships with 14 presets and 28 named palettes in total, and any preset can be overridden with a different palette or block size.
Before you start
The skill is optional and installed on demand. Its path is optional-skills/creative/pixel-art, version 2.0.0, authored by dodo-reach under the MIT license. It runs on linux, macos, and windows.
Dependencies are minimal:
- Python 3.9+
- Pillow (
pip install Pillow) - ffmpeg on PATH, needed only for video. Hermes installs this package.
If you only need still images, ffmpeg is not required. If you plan to animate, confirm ffmpeg is available before starting, because the video step depends on it.
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; do not dump all 14. The default menu when intent is unclear is:
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, such as "80s arcade" or "Gameboy", skip clarify and use the matching preset directly.
Step 2, Offer animation (optional)
If the user asked for a video or GIF, or if 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 call 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
| Preset | Era | Palette | Block | Best for |
|---|---|---|---|---|
arcade | 80s arcade | adaptive 16 | 8px | Bold posters, hero art |
snes | 16-bit | adaptive 32 | 4px | Characters, detailed scenes |
nes | 8-bit | NES (54) | 8px | True NES look |
gameboy | DMG handheld | 4 green shades | 8px | Monochrome Game Boy |
gameboy_pocket | Pocket handheld | 4 grey shades | 8px | Mono GB Pocket |
pico8 | PICO-8 | 16 fixed | 6px | Fantasy-console look |
c64 | Commodore 64 | 16 fixed | 8px | 8-bit home computer |
apple2 | Apple II hi-res | 6 fixed | 10px | Extreme retro, 6 colors |
teletext | BBC Teletext | 8 pure | 10px | Chunky primary colors |
mspaint | Windows MS Paint | 24 fixed | 8px | Nostalgic desktop |
mono_green | CRT phosphor | 2 green | 6px | Terminal/CRT aesthetic |
mono_amber | CRT amber | 2 amber | 6px | Amber monitor look |
neon | Cyberpunk | 10 neons | 6px | Vaporwave/cyber |
pastel | Soft pastel | 10 pastels | 6px | Kawaii / gentle |
Named palettes live in scripts/palettes.py. The complete list is in references/palettes.md, which documents 28 named palettes in total. Any preset can be overridden:
pixel_art("in.png", "out.png", preset="snes", palette="PICO_8", block=6)
Scene catalog for video
| Scene | Effects |
|---|---|
night | Twinkling stars + fireflies + drifting leaves |
dusk | Fireflies + sparkles |
tavern | Dust motes + warm sparkles |
indoor | Dust motes |
urban | Rain + neon pulse |
nature | Leaves + fireflies |
magic | Sparkles + fireflies |
storm | Rain + lightning |
underwater | Bubbles + light sparkles |
fire | Embers + sparkles |
snow | Snowflakes + sparkles |
desert | Heat shimmer + dust |
Invocation patterns
Python (import)
import sys
import os
sys.path.insert(0, os.path.expanduser("~/.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
out = os.path.expanduser("~/.hermes/cache/scratch")
pixel_art("/path/to/photo.jpg", f"{out}/pixel.png", preset="nes")
# 2. Animate (optional)
pixel_art_video(
f"{out}/pixel.png",
f"{out}/pixel.mp4",
scene="night",
duration=6,
fps=15,
seed=42,
export_gif=True,
)
This pattern adds the skill's scripts directory to sys.path, imports both functions, converts a photo to a pixel-art PNG using the nes preset, and then animates that PNG with the night scene. The video call sets duration, fps, seed, and requests a GIF export.
CLI
cd ~/.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
The first command converts an image with the gameboy preset. The second overrides the preset's palette and block size. The third animates an existing pixel-art PNG into an MP4 and also produces a GIF.
Pipeline rationale
Pixel conversion follows five steps:
- Boost contrast, color, and sharpness. The boost is stronger for smaller palettes.
- Posterize to simplify tonal regions before quantization.
- Downscale by
blockwithImage.NEAREST, which keeps hard pixels and avoids interpolation. - Quantize with Floyd-Steinberg dithering against either an adaptive N-color palette or a named hardware palette.
- 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 works differently. Each tick copies the base frame, so the background stays static. Stateless-per-frame particle draws are overlaid, with one function per effect. Encoding uses ffmpeg with libx264 -pix_fmt yuv420p -crf 18. The optional GIF is produced via palettegen and paletteuse.
Verification
After a run, check these points:
- The PNG is created at the output path.
- Clear square pixel blocks are visible at the preset's block size.
- The color count matches the preset. You can eyeball the image or run
Image.open(p).getcolors(). - The video is a valid MP4 that
ffprobecan open, with non-zero size.
When not to use it
The source does not describe alternatives or exclusions. It does, however, make clear that regenerating is costly, so confirm the style before generating. If the user has already named a style and scene, skip the clarification step and go straight to generation.
Limits and gotchas
- Palette keys are case-sensitive. Use
"NES","PICO_8","GAMEBOY_ORIGINAL"exactly. - Very small sources, under 100px wide, collapse under 8-10px blocks. Upscale the source first if it is tiny.
- Fractional
blockorpalettevalues break quantization. Keep them positive integers. - Animation particle counts are tuned for roughly 640x480 canvases. On very large images, you may want a second pass with a different seed for density.
mono_greenandmono_amberforcecolor=0.0, which desaturates. If you override that and keep chroma, the 2-color palette can produce stripes on smooth regions.- The
clarifyloop should be called at most twice per turn: once for style, once for scene. Do not pepper the user with more picks.
Related
Named hardware palettes and the procedural animation loops in pixel_art_video.py are ported from pixel-art-studio under the MIT license. See ATTRIBUTION.md in this skill directory for details.