Skip to content

How to cut a sprite sheet into animation frames

Set frame size, margins and spacing, fix a misaligned grid and export animation frames. Includes a practical sprite-sheet cutting workflow.

By ToolsNow · Published

To cut a regular sprite sheet, match the grid to its frame size, margins and spacing. If a later frame starts a few pixels off, check the gap between frames before changing the frame width.

The sprite sheet cutter previews the grid and animation so you can check the alignment before exporting.

The four numbers, and the formula

Per axis:

  • Margin is the blank border before the first frame.
  • Offset is an extra shift applied after the margin, for sheets whose first frame doesn’t start where the margin implies.
  • Frame is the frame’s own size.
  • Spacing is the gutter between frames. Not around them.

The position of frame n, counting from zero:

position = margin + offset + n × (frame + spacing)

Note what’s multiplied by what. Spacing is multiplied by n, not n+1. A row of four frames has three gutters. Treating it as four is the single most common bug in hand-written sheet cutters, and it has a characteristic symptom: the first frame is correct and each one after it drifts by one more gutter width.

Diagnosing a misaligned grid

Look at how the boxes are wrong and the answer follows.

Every frame shifted by the same amount means margin or offset. The grid’s spacing is right; its starting point isn’t.

Error growing across the row means spacing. Each frame accumulates the mistake, so the last one is worst. If frame 1 is fine and frame 8 is off by 14 pixels with 7 gutters between them, your spacing is 2 pixels out.

Boxes the wrong size entirely means frame width or height. Fix this first, because everything else is measured relative to it.

Correct across, wrong down means the axes have different values. Sheets routinely have spacing on one axis and none on the other.

Work in that order, frame size then spacing then margin then offset, and each step stops interfering with the next.

Why auto-detection is a suggestion

Detection generally works by finding rows and columns that are fully transparent, treating those as gutters, and measuring the content runs in between. When it works it’s exact and saves real time. It fails predictably in four cases:

  • Frames that touch. No gutters means nothing to measure. All detection can see is a single solid block, and it can’t tell whether that’s one frame or twelve.
  • A solid background colour instead of transparency. Detecting by colour sounds more general and is far less reliable, because a sprite that legitimately contains the background colour splits in half. No algorithm can tell that case from a real gutter using pixel data alone.
  • Sprites that don’t fill their cells. A character standing in the middle of a 64-pixel cell with 10 pixels of air on each side reports a 44-pixel frame. The frames are still regular; the content is smaller than the cell.
  • Irregular sheets, where frames come in different sizes. Those need metadata, not detection.

Any tool presenting detection as an answer instead of a starting point is overselling it. Every value should stay editable, and the overlay is the thing to trust, not the numbers.

Sheets that don’t divide evenly

A 100 × 32 sheet of 32 × 32 frames gives you three whole frames and 4 pixels left over. That’s normal, not an error.

Two different situations hide behind a leftover. A remainder smaller than one frame is a trailing margin, so ignore it. A remainder larger than one frame means whole frames are being missed, and your row or column count is wrong.

Frames extending past the edge should be flagged and exported at the declared size with transparent padding, never silently cropped. A frame that comes out 28 pixels wide when every other frame is 32 will misalign every animation drawn from the set, and the failure will surface somewhere a long way from its cause.

Animation timing

Frames per second is how many frames display each second, and frame duration is its reciprocal. Common rates: 12 fps for classic hand-drawn animation, 24 for film, 30 or 60 for games. Pixel-art animation often runs much slower, 6 to 10 fps, because a small number of well-drawn frames reads better than a smooth blur.

One detail surprises people: GIF can’t represent every frame rate. The format stores delays in hundredths of a second, so 12 fps is 8.33 hundredths and rounds to 8, giving you 12.5 fps. A tool that rounds silently leaves you wondering why the exported GIF runs slightly fast. A good one shows you the effective rate.

Another quirk: many browsers historically clamped very short GIF delays, 1 or 2 hundredths, to a minimum, so anything above roughly 50 fps in a GIF is unreliable. When you need precise timing, export frames and control the timing in your engine.

Transparent backgrounds

Keep transparency and export PNG. Two reminders.

JPEG has no alpha channel. Save a sprite as JPEG and transparency gets replaced with a solid colour, usually black, plus compression artefacts around exactly the hard edges sprites are made of.

Semi-transparent edge pixels are anti-aliasing. They look fine on the background the sprite was drawn against and produce a visible halo on any other. For pixel art the usual fix is forcing alpha fully on or off at a threshold.

A checkerboard behind the preview isn’t decoration. It’s the only way to tell transparent from white, and that difference matters a great deal.

Metadata, and what “trimmed” means

Cutting frames out is half the job. Your engine still needs to know where they are. The widely-read shape is the TexturePacker JSON format, which Phaser, PixiJS and others read directly:

{
  "frames": {
    "hero_00.png": {
      "frame": { "x": 0, "y": 0, "w": 32, "h": 32 },
      "rotated": false,
      "trimmed": false,
      "spriteSourceSize": { "x": 0, "y": 0, "w": 32, "h": 32 },
      "sourceSize": { "w": 32, "h": 32 }
    }
  }
}

The fields that catch people out are trimmed, spriteSourceSize and sourceSize. Trimming means stripping surrounding transparent pixels to save atlas space, in which case sourceSize records the original dimensions and spriteSourceSize records where the trimmed content sat inside them, so the engine can put it back in the right place. A tool that only cuts frames has to report trimmed: false with untrimmed values. Claiming otherwise misplaces every sprite drawn from the file.

One more thing: zero-pad your frame numbers. frame_10 sorts before frame_2 in every naive string sort, and that will scramble an animation somewhere down the line.

Repacking

Repacking into a single horizontal or vertical strip makes sense when your engine or your CSS expects one. CSS sprite animation with steps() needs a strip with no gutters at all, because the step function divides the width evenly and any spacing shifts every frame after the first.

Whatever the layout, frames have to be written with no scaling and no smoothing. Any resampling introduces blended edge pixels, and in pixel art that’s immediately visible.

Cut and check a sheet

  1. Load the sheet and let detection have a go.
  2. Check the overlay against the sprites. Trust the boxes, not the numbers.
  3. Fix frame size first, then spacing, then margin, then offset.
  4. Deselect frames you don’t want, and reorder if the sheet order isn’t the animation order.
  5. Preview at your intended frame rate.
  6. Export PNG frames plus JSON, or a GIF if you’re showing someone.
  7. Check the first and last frames specifically. That’s where off-by-one errors surface.

Sources and further reading

Last reviewed 31 July 2026.

More help with this topic

Published by ToolsNow. Read how tools and sources are checked.

Found a mistake or an outdated detail? Send a correction with the article title and the detail to review.