Nodes/Krea 2 and MiniMax H3 Pixel Art Refiners/Pixel Art Animation Pose Compositor
ComfyUI Node

Pixel Art Animation Pose Compositor

One clean sprite per pose, not sixty jittery frames

By envy-ai·Created 3 days ago·Updated about 18 hours ago· 9
Pixel Art Animation Pose Compositor
  • images
  • IMAGE
pose_count0
transition_threshold7.0

Here's the wall everyone hits making pixel animation with a video model. Your six-pose walk cycle comes back as a 56-frame clip where each pose is held for several frames - and every one of those frames is slightly different. A pixel of crawl in the outline, the palette breathing, the eye pixel moving by one. That's what reads as AI up close, and no amount of grid snapping fixes it, because it isn't a grid problem. The model just re-rolled the same drawing sixty times.

This node takes the whole batch, works out where each held pose begins and ends, and replaces every run of near-identical frames with a single clean composite of them. You get one image per distinct pose, in animation order.

It's the one node in this pack that doesn't touch the grid or the palette - which is why it belongs after a refiner, not instead of one.

How it works

Three passes, all deterministic:

  1. Round to 8-bit. Channels are snapped to their 8-bit values so differences are measured in real colour levels, not float noise.
  2. Find the holds. For each consecutive pair of frames it computes the mean absolute RGB difference over the whole image. Any diff at or above transition_threshold starts a new held pose. The README's calibration from the original 56-frame test is unusually clean: within-pose differences measured 0.044–1.978, real transitions 13.182–29.786, and the default threshold of 7.0 sits between them.
  3. Composite each pose. Holds are assigned to poses in cycle order - with six poses, holds 1, 7, 13 belong to pose 1 and so on - and then, per pixel, the exact RGB value that occurs most often across all frames in the matching holds wins. Ties break to the lowest Rec.709 luma, then the lowest packed RGB value.

That per-pixel mode is the whole trick. Frame-to-frame jitter is random, so it never repeats at a given pixel; the art does. Mode throws the noise away and keeps the drawing - and because it's an exact value rather than an average, you don't invent a colour that wasn't in the image.

The inputs

  • images - the ordered animation frames at a common resolution.
  • pose_count - defaults to 0, meaning autodetect. Set it explicitly when you know (six for a six-pose walk). Autodetection picks a medoid frame per hold, tests possible cycle lengths, and accepts the shortest period where every hold in the later cycles is at least twice as close to its matching first-cycle pose as to any other. It needs at least two complete cycles, and if there's no unambiguous period it raises instead of guessing.
  • transition_threshold - default 7.0, in mean-absolute-8-bit-RGB levels. Raise it if a subtle in-pose change reads as a new pose; lower it if two genuinely different poses are merging.

Output is a single IMAGE batch in animation order - to Create Video, or Save Image if you want the sprites individually.

Installing

One install covers all four nodes in the pack. Manager: search ComfyUI-Krea2-Pixel-Art-Refiner. Manually:

cd ComfyUI/custom_nodes
git clone https://github.com/envy-ai/ComfyUI-Krea2-Pixel-Art-Refiner.git krea2_pixel_art_refiner

Restart ComfyUI and find it under image/animation. No models, no keys. The pack's only third-party dependency, opencv-python-headless>=4.8, is for the autorefiner sibling; this node is pure torch.

Wiring it up

The intended chain, straight from the README:

VAE Decode → Pixel Art Refiner → Animation Pose Compositor → Create Video

Refine first, composite second. Composite first and the modal pass is choosing among colours that get remapped afterwards, losing the guarantee that the composite is palette-clean.

Where people get burned

The hard mismatch error. The number of detected holds must be a positive multiple of pose_count, or the node refuses:

Detected 13 held poses; expected a positive multiple of pose_count=6. Adjust transition_threshold or trim the input batch.

That's a feature - the alternative is silently welding pose 3 onto pose 5 and shipping you a broken cycle. It usually means the clip has a partial cycle at the end, a dropped frame at the start, or a spot where the threshold caught a mid-pose wobble. Trim the batch to whole cycles, or nudge the threshold.

Mode only fixes noise, not drift. If your held frames differ because the model is consistently changing something - the sprite slides one pixel right across the hold, a limb creeps - there's no dominant value to pick and the composite just looks like one intermediate frame. That's a generation problem; the fix is fewer, cleaner held frames, not a threshold tweak. It's the one failure this node can't absorb.

One cycle isn't enough for autodetect. A single 12-frame loop can't be resolved into six poses by period testing, because there's nothing to compare against. Set pose_count explicitly - the explicit path doesn't need repetition, only that the hold count divides evenly.

Frames at different resolutions. The difference metric is a whole-image mean, so a batch that isn't uniformly sized gives you nonsense boundaries. Refiner output is uniform by construction, which is another argument for keeping it upstream.

Categoryimage/animation

Inputs (3)

NameTypeDefaultDescription
imagesIMAGE
pose_countINT00–256Number of distinct poses in one animation cycle. Set to 0 to autodetect from repeated cycles.
transition_thresholdFLOAT7.00–255Minimum mean absolute 8-bit RGB difference between consecutive frames that starts a new held pose.

Outputs (1)

NameTypeDescription
IMAGEIMAGE