TL;DR
If you point ffmpeg straight at your raw sprite frames, you get a bloated 611KB GIF that shimmers on every frame. You can fix both problems with a two-pass ffmpeg command that builds one shared palette:
# Step 1: Generate a single 64-colour palette across all frames
ffmpeg -i frame-%02d.png -vf "palettegen=max_colors=64:stats_mode=diff" -update 1 palette.png
# Step 2: Render the GIF using that palette with dithering disabled
ffmpeg -framerate 12 -i frame-%02d.png -i palette.png -lavfi "paletteuse=dither=none" run-cycle.gif
These two steps shrink your file size by 78%. But palette work only fixes half your problem. If your animation still jitters, your cut is the reason.
The size ladder: why palettegen matters
A standard GIF supports at most 256 colours. A raw image from a model holds about 48,000 colours across subtle shifts in your background.
When you export without a custom palette, ffmpeg picks a separate 256-colour map for every single frame. That makes your background flicker, and it blows up your file size.
| Export method | File size | What changed |
|---|---|---|
| Even grid cut, default palette | 611,294 bytes | The bloated baseline. |
| Sprite-aligned, default palette | 241,987 bytes | Alignment alone cuts 60% of the byte weight. |
| Sprite-aligned, shared 64-colour palette | 165,345 bytes | The shared palette removes another 31%. |
| 4-frame sheet, aligned, shared palette | 130,635 bytes | The final production build. |
Setting stats_mode=diff tells ffmpeg to build the palette around the pixels that move between frames. Setting dither=none stops speckling on your flat pixel-art surfaces.
The math: why even grid slicing fails
Most developers slice sprite sheets by dividing the canvas into equal boxes. If your image is 1672 pixels wide with 4 columns, you expect each box to be 418 pixels wide.
We tested sheets from the GPT Image 2.5 generator across Flare and Sunburst checkpoints. We asked for a 1280x720 sheet, but every one came back at 1672x941 pixels.
Here's what your actual pixel offsets look like on the raw sheet:
Canvas width: 1672 px
Target columns: 4
Calculated cell width: 1672 / 4 = 418 px
Actual frame start positions (Flare 8-frame):
Frame 1: 145 px
Frame 2: 554 px (gap: 409 px)
Frame 3: 928 px (gap: 374 px)
Frame 4: 1295 px (gap: 367 px)
Drift on frame 4: 418 - 367 = 51 px off-centre
The characters don't sit on an even grid. The gap between frame starts shrinks from 409 pixels down to 367 pixels across the row.
If you slice by fixed 418-pixel steps, your character drifts 104 pixels sideways and bounces 48 pixels up and down. That's half the character's body width in drift.
Align on the baseline, not the cell
To get rid of the bounce, you need to ignore the canvas grid and measure the pixels directly:
- Find each sprite's actual box inside the raw image.
- Crop tightly to your sprite's edges.
- Place each cropped sprite onto one shared canvas.
- Centre the crop from left to right, and line up the bottom edge where the feet land.
| Slicing approach | Horizontal drift | Vertical bounce | Result |
|---|---|---|---|
| Even 4x2 grid | 104 px | 48 px | Character slides and bounces constantly. |
| Bounding box, bottom-aligned (8 frames) | 19 px | 0 px | Vertical bounce drops to zero. |
| Bounding box, bottom-aligned (4 frames) | 1 px | 0 px | Character stays locked in place. |
Lining up the feet eliminates the 48-pixel vertical hop entirely.
Why you should make 4 frames instead of 8
When you prompt for an 8-frame run cycle across two rows, the model duplicates poses.
We measured frame similarity using mean absolute difference (MAD), which scores the average per-pixel difference between two images. A low score means two frames are near copies:
- Top frame 3 vs bottom frame 7: 4.95 MAD
- Top frame 2 vs bottom frame 6: 5.62 MAD
- Top frame 4 vs bottom frame 8: 5.73 MAD
- Top frame 1 vs bottom frame 5: 7.21 MAD
Genuinely distinct animation frames score 12.36 MAD or higher. The second row of an 8-frame grid merely repeats the first row with minor errors.
On Sunburst sheets, the gaps between frame starts measured 400, 366, and 417 pixels. On Flare, the gaps measured 409, 374, and 367 pixels.
A single-row 4-frame sheet keeps your frame starts within 7 pixels of each other. Sprite widths on the 4-frame sheet varied by only 2 pixels (227 to 229 pixels), compared to a 39-pixel spread on the 8-frame layout.
The video model route vs direct cutting
You can also pass your raw sprite sheet into an image-to-video model like Seedance 2.0 Mini.
We tested this with our 4-frame sheet at 480p. The model produced an 864x496 video with 121 frames in 93 seconds.
| Metric | Direct sheet crop | Video generation route |
|---|---|---|
| Generation compute cost | 1x | about 19x |
| Frame control | 4 exact approved frames | 121 generated frames |
| Palette size per frame | 64 colours | 7,433 to 8,194 colours |
| Final GIF size | 130,635 bytes | 530,249 bytes |
| Baseline drift | 0 px | 3 px |
The video route keeps your character stable, with just 3 pixels of drift across five seconds.
But the video model redraws your character on every single frame. You lose your clean pixel outlines, and the colour count multiplies by more than a hundred times.
How to make the raw sheet
To try these steps yourself, open the GPT Image 2.5 tool and pick your settings.
OpenAI positions Flare for fast runs and Sunburst for tasks where you need higher editing precision. The composer lets you set resolution tiers (1K, 2K, 4K) and aspect ratios from one menu.
Use this prompt to make your source frames:
A 2D pixel-art sprite sheet of one character: a green-hooded courier with a brown satchel, side view facing right, running cycle, 4 frames in a single horizontal row, evenly spaced with equal margins on a flat mid-grey background, identical character height, proportions and colour palette in every frame, crisp 1px outlines, limited 16-colour palette, no text, no frame numbers, no grid lines, no drop shadows, no background scenery.
Three limits will stay on the raw output:
- Background colour values shift slightly between runs (such as RGB 122,121,122 vs 127,126,125), so don't use hardcoded hex keys.
- Files download as JPGs without an alpha layer.
- Two-row requests duplicate poses across rows.
You can run your prompts directly on GPT Image 2.5 on Seadanse with starter credits upon sign-up.
Workflow checklist
Apply this checklist when you build your next sprite animation:
- Ask for 4 frames in a single horizontal row, not 8 frames in a grid.
- Read your image dimensions from the file header instead of assuming your requested canvas size.
- Crop around individual sprite boxes rather than using equal grid cuts.
- Align each cropped frame to a common bottom baseline where the feet land.
- Make a shared 64-colour palette with
palettegen=stats_mode=diff. - Build your final GIF using
paletteuse=dither=none.
We work on Seadanse, which runs GPT Image 2.5, and the tests in this post were run there.


Top comments (0)