Generate an ordered frame set
Use frames when one asset is a short sequence of still renders controlled by
one changing workflow input. This is intended for pose images, expression
labels, or another explicit per-frame control. It is not video diffusion.
{
"styles": {
"character-motion": {
"generator": "frames",
"outDir": "assets/generated/motion",
"seed": 1200,
"quality": {
"outDir": "assets/final/motion",
"palette": ["#161321", "#49374f", "#c16c5b", "#f3d6b3"],
"fps": 12
},
"providerOptions": {
"comfyui": {
"workflowFile": "workflows/pose-api.json",
"outputNodeId": "9",
"numImages": 1,
"frames": { "vary": "pose", "seedStep": 0 },
"bindings": {
"prompt": { "nodeId": "6", "input": "text" },
"width": { "nodeId": "5", "input": "width" },
"height": { "nodeId": "5", "input": "height" },
"seed": { "nodeId": "3", "input": "seed" },
"pose": { "nodeId": "19", "input": "image" }
}
}
}
}
},
"assets": {
"hero-idle": {
"prompt": "the same hero breathing in place",
"width": 832,
"height": 1216,
"providerInputs": {
"pose": ["poses/idle-00.png", "poses/idle-01.png", "poses/idle-02.png"]
}
}
}
}
frames.vary names one custom binding. Its asset value must be an ordered
array of 2–64 matching scalars or manifest-relative PNG/JPEG paths. Every other
binding remains fixed. seedStep defaults to zero; a nonzero value uses
style.seed + frameIndex × seedStep and therefore requires a seed binding.
The frame count comes from the array, so it cannot disagree with a separate
count field.
PixelKiln submits one still workflow per value but tracks the result as one
asset. After ComfyUI accepts a prompt, PixelKiln saves its id before submitting
the next frame. An interrupted retry continues at the first missing frame. A
changed spec starts a fresh set. Incomplete sets cannot be polled into review,
and one failed render rejects the complete set while retaining every submitted
prompt id for diagnosis. The review page shows an animated loop and ordered
strip; accepting any frame accepts the whole set.
Fetch writes hero-idle-frame-00.png, hero-idle-frame-01.png, and so on.
pack turns those role-stable files into a sprite sheet whose atlas lists
the set with its fps; pack --format aseprite makes it a frameTag with
per-frame durations, and pack --format godot a looping SpriteFrames
animation an AnimatedSprite2D plays by the asset's id.
With a quality profile, refine applies one palette to every frame and writes
one approval record for the set. It rejects the entire set when source size,
native dimensions, detected pixel step, or detected grid phase differs. The
pinned Pixel Art Fixer reports phase in source-pixel coordinates; PixelKiln
compares it modulo the shared step. A passing grid check still cannot judge
character identity, motion arcs, foot sliding, or loop timing. Review the
animation at 1× before recording approval.