DEV Community

Cover image for I built an AI tool that lets you generate pixel-art VFX
HoangLong256
HoangLong256

Posted on

I built an AI tool that lets you generate pixel-art VFX

Slash, explosion, demonic burst, summoning circle, campfire, water splash and hearts, all generated by spritefx

In order to make a pixel VFX art, just typing it in your terminal

> Make a 32 px poison cloud that lingers
Enter fullscreen mode Exit fullscreen mode

…then Claude builds the effect, checks it and corrects it. One effect typically takes 20–45 minutes. The result is a game-ready sprite sheet with frame timings and hit events, for Godot, Unity or GameMaker. Claude made every effect in the GIF above from a JSON file. Nobody painted or edited a pixel by hand.

This is spritefx, an open-source (MIT) pixel-art VFX engine that ships as a Claude Code skill.

Install it in one command

git clone https://github.com/HoangLong256/spritefx ~/.claude/skills/spritefx
Enter fullscreen mode Exit fullscreen mode

Then ask for effects in any project, in your own words:

> A slash, hit and trail set for my greatsword
> A blue portal: opening, idle loop and closing
> Give my healing potion a sparkle effect, 48 px
Enter fullscreen mode Exit fullscreen mode

On first use Claude sets up the skill's Python environment (Python 3.11+ is the only requirement).

Claude doesn't draw pixels. That's the point.

Image models can make images that look like pixel art at a glance. For game VFX, their output usually has off-grid pixels, too many colours and soft edges. Their frames often do not animate together. So spritefx divides the work:

  1. Claude writes a JSON spec. The spec describes the effect as phases and layers. Examples are a dark contrast frame, a white flash, a noise fireball, a shock ring and smoke puffs.
  2. The engine draws it. A deterministic Python renderer simulates each layer tick by tick. It changes each value to a palette colour. The same spec always gives byte-identical PNG files.
  3. 22 pixel-art checks grade the result. Does the flash occur before the peak? Does the smoke last longer than the fire? Do pixels flicker A-B-A? Is the effect visible on a light background? One fault stops the build.
  4. Claude reads the faults and the review sheets, then corrects the spec. Claude never edits the pixels. The loop continues until the build passes.

This loop is the key. An AI cannot measure "make it look good". It can measure "smoke_tail: smoke ends before the fire", and it can correct that fault.

JSON in, pixels out: the explosion spec, with each phase lighting up as the effect plays

Why pixel-art rules, and why check them?

As an indie dev, I find effects painful to make, and most AI image models cannot generate effects that stay consistent from frame to frame. Procedural effects usually show four defects: soft alpha edges, noisy blobs, too many colours, and smoke that fades instead of breaking into pieces.

Pixel artists avoid these defects on purpose. A good hand-drawn explosion follows a known recipe:

  1. a dark contrast frame,
  2. a one-frame white flash,
  3. a hot core with a cool rim,
  4. smoke that erodes into dots.

Same simulation, two looks: the raw simulated values drawn soft with alpha and blur, next to spritefx's palette-locked frame

Left: the raw values of the simulation, drawn the usual way with soft alpha and blur. Right: the same frames after spritefx.

Explosion frames: dark contrast frame, white star flash, fireball, breaking shock ring, smoke eroding to dots

Smoke is the clearest example. The smoke does not fade to transparent. It erodes: blob, then crescent, then dots, then nothing. Each pixel stays fully opaque at all times. So the smoke stays readable at 1× size.

Erode, don't fade: the explosion's smoke at 9x, going from blob to crescent to dots

What Claude writes: the spec

Here is a complete hit spark. Claude writes specs like this for you, or you can write them by hand. The effect is a 4-point star that flares and then erodes, with radial streaks.

{
  "name": "hit_spark", "type": "hit", "canvas": "auto", "anchor": "center", "fps": 30, "seed": 3,
  "ramps": {"spark": {"colors": ["#9e2835", "#f77622", "#feae34", "#fee761", "#ffffff"]}},
  "phases": [
    {"name": "flash", "ticks": 2, "fast": true, "events": ["hit"]},
    {"name": "burst", "ticks": 3},
    {"name": "fade", "ticks": 5}
  ],
  "layers": [
    {"id": "star", "kind": "shape", "ramp": "spark",
     "shape": {"sdf": "star", "points": 4, "r": {"keys": [[0, 7], [0.2, 9], [1, 3]], "ease": "out"}, "inner": 0.3},
     "value_mode": "radial", "edge": 0.0, "break_up": {"after": 0.45, "noise": "angular"}},
    {"id": "streaks", "kind": "particles", "ramp": "spark", "tag": "debris",
     "emit": {"mode": "burst", "count": [6, 7]}, "velocity": {"speed": [1.6, 2.6], "angle": "out"},
     "drag": 0.18, "life": [6, 9], "stamp": {"kind": "streak", "size": {"keys": [[0, 3], [1, 1]]}}}
  ]
}
Enter fullscreen mode Exit fullscreen mode

A few ideas carry the whole design:

  1. Phases run on a 30 fps tick clock. You write flash → burst → fade, not "frame 1, frame 2…".
  2. Parameters use keys and easing. The radius of the star increases from 7 to 9 px. Then it decreases to 3 px with an out ease. The radius does not change linearly.
  3. You select a colour ramp, never a colour per pixel. Each layer maps its value to a ramp from dark to light. So each pixel is a palette colour, and no pixel has soft alpha.

The engine has ten layer kinds. They include SDF shapes, particles, loopable noise fields, lightning bolts, Doom-style cellular fire, a grid smoke simulation and a FLIP water solver. At the end, the engine changes the output of each layer to palette colours.

One spec, many looks

Claude selects colour ramps, not colours. So a new colour scheme for an effect is a small change. These four explosions use the same spec. Only its two ramps (blast and soot) are different. Timing, shapes and smoke are the same, and all four pass every check.

One spec, four elements: the same explosion in fire, ice, poison and arcane colours

So a request like "make ice, poison and arcane versions of my explosion" needs little work. The motion stays the same, and only the palette changes.

The checks that keep Claude honest

Pixels are the easy half. The hard half is to know when an effect is wrong. So each build runs 22 craft checks. fx build writes the sheets only when all of them pass. Some examples:

Check What it enforces
envelope Fast rise, slow decay. Coverage peaks early, and the decay lasts at least 1.5× the rise
flash_order The brightest frame occurs first, before the effect grows
smoke_tail Smoke lasts longer than fire
flicker No pixel blinks A-B-A across frames
orphans No single stray pixels, except pixels that you tag as sparks
colors At most 12 colours per sheet and 6 per layer
seam A loop has no jump where it starts again
residue The last frame of a one-shot is empty

Each build also writes an envelope chart. It shows coverage, lightness and colour use over time, with shaded phases.

The explosion's envelope: coverage peaks in the first third, lightness spikes on the flash, warm colours give way to smoke

One rule keeps the project honest. Correct a failed check in the spec. Never edit the pixels, and never loosen the limit.

Calibration against hand-drawn effects by pixel artists set four of the limits. The other limits are first proposals, and tuning continues. Also, a pass is necessary but not sufficient. Some effects pass every check and still look weaker than hand-drawn work. So I also examine each effect by eye.

Output for your game engine

A build gives you:

  • a one-row sprite strip with cells of equal size,
  • an Aseprite-style JSON with frame durations, phase tags, events and the anchor,
  • a base sheet and an additive sheet, for glow with additive blending,
  • an emissive sheet for bloom or lights,
  • an 8-bit indexed PNG for palette swaps.

The documentation shows how to connect the sheets in Godot, Unity and GameMaker.

Demo: the witch summons a devil… and gets a bunny

A chibi witch performs a dark ritual in a graveyard; red smoke and horns rise from a demonic circle, then a bunny appears and hearts float up

This 12-second scene uses only generated sheets. spritefx specs make all its effects: the demonic circle, the staff charge, the smoke column, the burst, the reveal pop and the hearts.

Prefer to skip the AI?

The engine also operates without Claude. It needs no GPU and no game engine. It needs only Python 3.11 and three dependencies (numpy, Pillow, jsonschema):

git clone https://github.com/HoangLong256/spritefx && cd spritefx
python3.11 -m venv .venv && .venv/bin/pip install -e ".[test]"
.venv/bin/python -m fx build examples/explosion.json --out out
Enter fullscreen mode Exit fullscreen mode

👉 github.com/HoangLong256/spritefx

New effect specs are the most useful contribution. Add one to examples/ that builds with zero faults.

Please tell me two things in the comments:

  1. Which effect do you want next?
  2. Which craft rule does the audit not check yet?

Top comments (0)