Generating or drawing a good sequence is only half the job. A sprite sheet stops jittering when every frame obeys the same fixed-cell contract: identical canvas size, one semantic pivot, stable baseline, explicit reading order, and preserved trim offsets. Treat those values as runtime data, not as visual cleanup.
Disclosure: I work on FrameSprite. The method below is tool-independent, and the measurements come from a public, downloadable FrameSprite test strip.
Why equal-looking frames can still jitter
A renderer places a sprite rectangle around an origin. If every frame is cropped to its visible pixels, that rectangle changes as arms, capes, wings, or weapons move. Centering each new rectangle at the same transform does not center the original character. It centers a different crop on every frame.
That creates two separate problems that are often confused:
- Artwork drift: the character moves relative to the logical cell.
- Runtime drift: the engine changes placement through pivots, offsets, transforms, camera motion, or texture sampling.
Filtering can soften shimmer, but it cannot repair a moving pivot. Repacking can reduce atlas size, but it cannot reconstruct discarded source offsets.
The fixed-cell contract
Write these fields down before export:
| Field | Contract |
|---|---|
| Cell width and height | Identical for every frame in the action |
| Reading order | Explicit: row-major, column-major, or named frames |
| Pivot meaning | A gameplay point, such as ground contact or torso root |
| Pivot coordinates | Measured in the same source canvas |
| Baseline | Shared for grounded actions |
| Alpha policy | Real transparency, not a painted checkerboard |
| Trim metadata | Original size plus X/Y offset if cells are packed |
| Playback | Frame order, duration, and loop policy |
The key word is semantic. “Center” is not automatically a useful pivot. A grounded walk cycle often wants bottom-center or a designed foot-contact point. A flying creature may use a torso root. An attack may keep the body root stable while the weapon extends far outside the idle silhouette.
A measured example
We measured four frames from a public 4×4 strip inside identical 360×360 cells. Their visible widths changed from 145 px to 189 px, while the lowest alpha-bound varied by only 1 px.
That result matters because it separates silhouette change from root drift:
- the moving limbs legitimately change the visible width;
- the outer cell remains 360×360;
- the canvas center remains 180×180;
- the shared bottom region stays coherent.
If those four frames were independently trimmed and re-centered, the apparent character position would move even though the original fixed-cell sequence was stable.
You can inspect the full method and download the reference measurements in the sprite pivot, anchor and origin guide.
A renderer-neutral playback model
For a fixed grid, frame selection should change only the source rectangle:
type SpriteContract = {
cellWidth: number;
cellHeight: number;
columns: number;
pivotX: number;
pivotY: number;
};
function frameRect(index: number, c: SpriteContract) {
const column = index % c.columns;
const row = Math.floor(index / c.columns);
return {
x: column * c.cellWidth,
y: row * c.cellHeight,
width: c.cellWidth,
height: c.cellHeight,
pivotX: c.pivotX,
pivotY: c.pivotY,
};
}
The world transform does not move when the frame changes. Only the sampled rectangle changes.
Engine terminology differs:
- Unity stores a Sprite pivot in source-texture pixels.
- Phaser commonly exposes a normalized origin from 0 to 1.
- Godot AnimatedSprite2D combines centered drawing with a pixel offset.
The names and units differ, but the contract is the same: convert one semantic source point into each engine’s representation without redefining it per frame.
What about trimmed atlases?
Trimming is safe when the atlas keeps enough data to reconstruct the fixed source cell:
- untrimmed source width and height;
- packed rectangle width and height;
- trim offset X and Y;
- pivot in source coordinates;
- rotation flag, if packing allows rotated regions.
A normalized origin of 0.5, 0.5 is not sufficient by itself. If each trimmed rectangle has a different size, its normalized center refers to a different place in the original cell.
If the exporter drops source size and trim offsets, keep the untrimmed fixed cells instead. A larger texture is often cheaper than debugging intermittent visual drift across several engines.
A five-minute diagnostic
Use this order before changing import settings:
- Freeze the character at one world position and stop the camera.
- Overlay all animation frames at the same top-left coordinate.
- Draw the logical cell border, pivot, and gameplay baseline.
- Confirm every frame uses the same outer canvas or valid trim offsets.
- Test frame swapping before testing movement.
- Move the character and camera separately.
- Only then inspect filtering, mipmaps, pixel snapping, and compression.
If jitter remains while both the character and camera are stationary, start with the art, cell, pivot, or atlas metadata. If it appears only during camera movement, investigate fractional transforms and pixel scaling.
Where this contract should not be over-applied
Not every opaque pixel should remain fixed. Jumps, recoil, squash and stretch, flying motion, and deliberate root translation can move the center of mass. The contract stabilizes the gameplay anchor; it does not erase intentional motion.
Likewise, one pivot cannot represent every gameplay attachment. Weapon sockets, hitboxes, effect origins, and collision shapes may need separate metadata or child transforms.
The handoff test
Give the exported sheet to a developer who did not create it. If they cannot identify the cell size, reading order, pivot meaning, loop behavior, and trim reconstruction without asking the artist, the asset package is incomplete.
For a visual overlay workflow, failure table, and downloadable alignment strip, see How to fix sprite animation jitter and pivot drift. You can also test an existing sheet in FrameSprite’s free browser sprite-sheet tools; those tools run without generation credits.
A stable sprite animation is not defined by how tightly each drawing is cropped. It is defined by whether every frame can be placed from the same contract.
Top comments (0)