Nothing had failed. The source video was four seconds long, and the workflow sampled it at eight frames per second. Thirty-two was exactly the right answer—just not the finished answer suggested by the compact sprite sheet in the project demo. That small surprise exposed the real work between “an AI made my character move” and “this animation belongs in a game.”
Our verdict: the open-source Character Animation Skill is a promising V1 for prototypes. It automates extraction, rough alignment, and packing, but it does not yet deliver a game-ready loop on its own. Frame selection, background removal, root stability, timing, and engine playback still need deliberate decisions from the developer.
The moment the shortcut stopped being simple
The original post from @danny489642 presents a genuinely useful shift in thinking. Instead of treating an image-to-video result as the final media, it treats the video as raw material. A Codex Skill can carry the job forward: extract stills, normalize their canvases, and arrange them into a sheet that a 2D workflow can inspect.
That sequence belongs in a Skill because the value comes from remembering the same production steps every time. OpenAI's current Skill documentation describes the format as a directory built around SKILL.md, with optional scripts, references, and assets. The reviewed repository follows that shape and keeps its video provider behind a replaceable adapter.
The adapter is intentionally unfinished. Its checked-in configuration uses placeholder endpoints and a placeholder model name. On a clean machine, generate_video.py stopped with FIRST-RUN SETUP REQUIRED until a provider was configured. That is a sensible failure mode. It also means the repository is a workflow framework rather than an instant image-to-sprite generator.
Provider setup is more than entering an API key. The custom adapter base64-encodes the reference image and sends it to the configured upload endpoint. Before the first call, name the service, confirm that the character may be animated there, review its retention and commercial-use terms, and decide where the returned video will be stored. An environment variable keeps the secret out of source control; it does not answer those ownership and transfer questions.
A four-second video became 32 candidates
The repository includes one authorized four-second MP4 and an eight-frame transparent example. FFprobe identified the video as 768 by 1120, 24 fps, H.264, yuv420p, and exactly 4.000 seconds long. We ran the three documented offline scripts without changing their defaults:
python scripts/extract_frames.py \
--video examples/source_4s.mp4 \
--output-dir output/raw_frames \
--fps 8
python scripts/align_frames.py \
--input-dir output/raw_frames \
--output-dir output/frames
python scripts/build_spritesheet.py \
--input-dir output/frames \
--output output/spritesheet.png
| Stage | Observed result | What remained |
|---|---|---|
| Video generation | Not run; provider setup stopped cleanly | Choose and authorize a real provider |
| Extraction at 8 fps | 32 opaque PNG candidates | Choose useful poses |
| Bottom-center alignment | 32 canvases at 684 × 944 | Remove the interior matte and verify the root |
| Sheet assembly | Failed intermediate: 4104 × 5664, arranged 6 × 6 | Reject near-duplicates, ignore four empty cells, and preserve frame order |
| Eight-frame loop | Not reproduced from the retained scripts | Curate frames and clean the background |
| Engine playback | Not run | Slice, set the pivot, time the loop, and play it |
This was a failed intermediate, not 32 usable animation frames. The source crops were all pixel-unique, but that did not make them pose-unique. After reducing each cell to the same 24 × 32 comparison grid, the mean absolute RGB difference between frames 1 and 2 was only 0.06 on a 0–255 scale; frames 2 and 3 differed by 0.08. They were separate files with tiny changes, not three useful animation decisions. The final row also contained two candidates followed by four empty cells because the builder chose a roughly square grid. We retained that exact replay in the review record, but removed it from the reader-facing media rather than letting a technically valid PNG masquerade as a useful sprite sheet.
The replacement below makes the acceptance bar visible. It is a synthetic, art-directed example—not an output from the repository replay. Its four rows spend eight frames each on walking, running, jumping, and an unarmed attack. That makes every cell responsible for a different animation decision instead of filling a grid with near-duplicates.

We generated the atlas from the repository's MIT-licensed eight-frame character sheet as an identity reference, then split the result into its 8 × 4 cells for inspection. All 32 cells are occupied and pixel-unique. On the same 0–255 downsampled RGB comparison scale, the closest pair measured 6.69 rather than 0.06; the mean difference between adjacent cells was 24.07. Those measurements do not prove animation quality, but they catch the specific failure that made the earlier image meaningless. The warm review background is intentionally opaque, so this illustration is a pose-planning target—not a file to import directly into an engine.
The FFmpeg fps filter controls sampling rate. It has no concept of contact poses, readable silhouettes, duplicate frames, melted hands, or a foot that slides along the floor. Selecting every fourth file would produce eight frames, but it would turn arithmetic into art direction. The repository does not include a key-pose selector or a retained record showing how its supplied eight frames were chosen; advanced selection is listed as future work.
Transparent padding is not background removal
Every extracted PNG was fully opaque because the H.264 source has no alpha channel. The alignment script handles that situation by sampling the top-left pixel as an estimated background color. It converts the color difference to grayscale, treats values above 12 as content, crops the resulting rectangle, and places that crop on a transparent canvas.
This gives every frame transparent padding around its rectangular crop. Pixels inside the rectangle remain untouched, including the black video matte between the character's limbs, scarf, tail, and body. The replay therefore looks transparent at the cell edges while still carrying a large opaque background.
To make the measurement reproducible, we counted a pixel as fully opaque near-black only when alpha equaled 255 and all three RGB channels were below 18. Under that deliberately simple test, the replayed 32-frame sheet contained 9,408,576 such pixels—40.48% of the entire image. The repository's supplied eight-frame sheet contained 9,644, or 0.19%, under the same rule.
The count is a diagnostic, not a background-segmentation score. Dark outlines, gloves, and boots can also meet the threshold. The size of the difference, together with direct visual inspection, shows that the replay retained the matte; it does not tell us which cleanup method produced the supplied example.

The repository's eight-frame example is visibly cleaner and substantially transparent. Reaching that state needs an additional operation before final alignment. A simple solid matte may respond to a color-distance mask, followed by manual edge review. Fur, hair, motion blur, smoke, and translucent accessories usually need a better matting tool or hand cleanup. Whichever method you choose, inspect the alpha channel itself and preview every frame over both a light and a dark background. A checkerboard alone can hide black wedges and pale halos.
Choose poses by function, not interval
A usable walk cycle is a small sequence of jobs. Start with left contact, a passing pose, right contact, and the opposite passing pose. Add breakdowns only where they improve weight, leg clarity, or cadence. Seven strong poses are more useful than eight chosen to satisfy a round number.
| Moment | What earns a place | Common reason to reject it |
|---|---|---|
| Contact | A clear planted foot and readable stride | Both feet float or the leading leg changes shape |
| Down / recoil | Visible weight settling after contact | The whole character merely scales or sinks |
| Passing | One leg passes the other without merging | Feet, hands, or clothing fuse together |
| Up / high point | A useful lift that carries momentum forward | The body jumps while the ground point drifts |
| Optional breakdown | Smoother weight or a clearer secondary motion | A near-duplicate that only lengthens the loop |
Place the 32 candidates side by side, keep their filenames or timestamps visible, and record four fields for every decision: frame, intended role, keep/reject, and reason. That small ledger makes uneven sampling defensible. It also reveals when the source video never produced a usable right-foot contact, which is a reason to regenerate the clip rather than disguise the gap in post-production.
The generation brief should make rejection cheap. This is the production prompt we would start from; it is original to this article rather than copied from the post or repository:
Outcome
Create one left-facing walk cycle for a small 2D prototype.
Preserve
Exact silhouette, face, scarf, belt, boots, palette, and proportions.
Motion
Begin on a clear left-foot contact. Transfer weight through a readable
passing pose, reach the opposite contact, and return toward the opening
pose. Keep the feet near one ground line. Let the tail follow with a
restrained delay instead of turning into a second action.
Camera and scene
Full body visible. Fixed camera. Motion in place. No cuts, zoom, props,
new limbs, costume changes, text, particles, or background animation.
Acceptance
Return one short candidate video for review. Stop if identity, feet,
silhouette, or camera stability fails.
If the face changes, a foot disappears, or the camera drifts, make another video. Cleaning 32 frames from a failed source is usually slower than rejecting four seconds of bad motion.
Stabilize the loop before engine import
Equal canvas sizes solve only one part of alignment. The character also needs a stable root. Bottom-center is a reasonable default for a standing figure, yet the lowest visible pixel may belong to a tail tip, scarf, shadow, or matte speck. When that pixel changes, a bounding-box anchor makes the body bounce even though every frame has the same dimensions.
Choose a root point that represents the character's ground contact or pelvis, record it relative to the canvas, and preview a guide line through it. Check scale drift separately from translation; recentering a character that grows and shrinks can make the sheet look orderly while the animation still pulses. If horizontal flipping is part of the game, inspect asymmetric clothing, props, and hand dominance as well.
Then play last → first → second repeatedly at the intended speed. The final frame should not duplicate the first if both will be held for a full frame duration, because that creates a pause at the seam. Extraction fps and playback fps are separate choices. Eight selected poses can play at 6, 8, 10, or 12 fps, and some actions benefit from unequal holds.
The animation must also agree with movement in the game. A clean in-place cycle can still moonwalk when its cadence and the character's world displacement disagree. Test it while moving at the real walk speed, then pause on each frame and inspect the transition into and out of the state.
Unity's Sprite Editor can slice an equal-cell sheet by cell size or count and assign a pivot. Godot's AnimatedSprite2D plays textures through a SpriteFrames resource. For this replay, the cells are 684 × 944 in a 6 × 6 grid, and the last four cells are empty. Do not import those blanks as animation frames. Neither engine can infer the intended foot plant, frame order, loop speed, or collision relationship.
Where this Skill fits in a real project
The reviewed repository is most useful as the mechanical middle of a larger workflow. Pin the revision you inspected, create an isolated Python environment, keep provider credentials outside the repository, and record the dependency versions installed because the project specifies minimum versions rather than a lockfile.
- Freeze an owned or properly licensed full-body character reference.
- Configure one named provider and approve the image transfer, cost, duration, and storage location.
- Generate one short in-place motion clip and reject it immediately if identity or motion fails.
- Extract candidates at a documented rate, then choose poses by their role in the action.
- Remove the matte, lock the root, and inspect edges before packing the sheet.
- Preview the seam and choose playback timing independently from extraction speed.
- Import the selected cells into the target engine and test the animation while the character moves.
For an early prototype, one owned character, one readable action, stable feet, acceptable edges, and a convincing loop in a minimal scene may be enough. A shipping asset needs art direction, animation review, collision and memory checks, state readability, provider terms, and provenance for every source.
The attractive part of this idea is still intact: a video model can create motion candidates that would be expensive to draw from scratch. The replay simply shows where the shortcut ends. Use the Skill to extract, align, and pack. Add an explicit background-removal step, keep a record of why each pose survived, and reserve “game-ready” for the moment the loop actually works inside the game.
