How to split a sprite sheet into individual PNG images
A sprite sheet packs every frame of an animation into one image. Engines like it that way; you usually don't, at least while you're editing. Here is how to get the frames back out cleanly, and what to check so the cut lands on the right pixel.
1. Find the grid before you cut anything
Most sheets are a plain grid: every frame occupies a cell of identical size, laid out left to right, top to bottom. If you know the column and row count, the cell size falls out of the sheet dimensions:
cellWidth = sheetWidth / columnscellHeight = sheetHeight / rows
A 256×128 sheet holding eight frames in two rows gives 64×64 cells. If the division doesn't come out to a whole number, your column count is wrong — or the sheet has padding, which is the next section.
Working the other way round is just as common. You know the art is 32×32 because that is what you drew, and you want to know how many frames fit. Divide the sheet dimensions by the cell size instead, and use the tool's fixed cell size mode.
2. Watch out for offset and spacing
Exporters frequently add a margin around the sheet, a gap between cells, or both — usually to stop neighbouring frames bleeding into each other when the GPU samples a texture. That padding breaks the naive division above. The real formula is:
cellWidth = (sheetWidth − offsetX − spacingX × (columns − 1)) / columns
In practice you don't compute this by hand: set the offset and spacing values and watch the overlay. When the outlines sit exactly on the frame edges with no sliver of the neighbouring frame showing, the numbers are right. A one-pixel error is obvious at 4× zoom and invisible at fit-to-width, so zoom in before you trust it.
3. Grid or auto-detect?
Use grid mode when the sheet came out of an animation tool. Every frame shares a cell, the pivot is consistent, and cutting on cell boundaries keeps that consistency — which is what your engine's animation player expects.
Use auto-detect when the sheet is a packed atlas: frames of different sizes, arranged to waste as little space as possible. There is no grid to find, so the tool flood-fills islands of non-transparent pixels and takes the bounding box of each one.
Auto-detect has one setting worth understanding: the merge gap. A character holding a sword at arm's length may be two separate islands of pixels. With a merge gap of a few pixels, islands that sit close together are treated as one frame. Raise it if single frames are being split apart; lower it if neighbouring frames are being glued together.
4. Trim, or don't
Trimming shrinks each frame to the bounding box of its visible pixels. It produces the smallest files and is what an atlas packer would do, but it changes each frame's dimensions — and with them, where the frame's origin sits.
Trim when you are going to repack the frames into a new atlas, or when the engine stores a per-frame offset. Don't trim when the frames feed a simple animation player that assumes every frame is the same size; otherwise the sprite will appear to jitter as the animation plays, because each frame is centred differently.
The related setting is the alpha threshold. Pixels at or below it count as empty. Zero is the strict reading, but sheets exported with a faint background wash or lossy compression carry alpha values of 1–3 across the whole image, which defeats both trimming and empty-frame detection. Nudging the threshold up to around 8 fixes those without eating genuine soft edges.
5. Name the frames so they sort correctly
Zero-pad the index. frame_2.png and frame_10.png sort in the wrong order in almost every file browser and in a fair number of asset importers; frame_02.png and frame_10.png do not. The cutter's {i0} token pads to the width of the largest index for you.
If the sheet holds several animations — a walk cycle in row 0, an attack in row 1 — the {row} and {col} tokens let you name frames by their grid position instead, which keeps each animation grouped.
6. Import the frames into your engine
Godot: drop the PNGs into the project, then add them as frames of a SpriteFrames resource on an AnimatedSprite2D. In the import dock, set the filter to Nearest — the default linear filter blurs pixel art the moment the sprite is scaled.
Unity: set Texture Type to Sprite (2D and UI), Filter Mode to Point (no filter), and Compression to None for pixel art. Selecting the frames and dragging them into the scene creates an Animation Clip automatically.
Phaser: individual frames load with this.load.image(). If you would rather keep the single sheet, the cutter also exports a frames.json manifest in atlas format that this.load.atlas() reads directly.
Common problems
Every frame has a sliver of the next frame along one edge
The cell size is a fraction too large, or the sheet has spacing you haven't accounted for. Set spacing to 1–2px and re-check at 4× zoom.
The last row or column is cut off
Your row or column count is one too high, or an offset is pushing the grid past the sheet edge.
Auto-detect returns hundreds of tiny frames
Anti-aliased or dithered pixels are being read as separate islands. Raise the minimum frame size, and raise the merge gap.
Exported frames look blurry
The blur was added on import, not on export — PNG output here is lossless. Set your engine's texture filter to nearest-neighbour.