Sprite Sheet Maker

Drop a sheet with sprites scattered anywhere on it — each one is traced on its own, seated in a uniform box, and packed into an even sheet, with its atlas JSON a click away.

Drop a sprite sheet, or a set of frame images, or click to choose

PNGWEBPGIFBMPJPEG

Drop several images at once and each one becomes a frame, ordered by filename: walk1, walk2, walk10.

What This Tool Does

Drop a sheet with sprites scattered anywhere on it. Each sprite is traced on its own, the set is put into reading order, every one of them is seated in a single uniform box, and the result downloads as a packed PNG, with a matching atlas JSON that names it a click away. It is the same studio the splitter uses, opened on the sheet export instead of the ZIP.

How Scattered Sprites Are Found

Detection works on the sheet's opacity, one byte per pixel, and grows a region outward from every content pixel it has not already claimed. Neighbours are counted in all eight directions, so two pixels that touch only at a corner belong to the same sprite — counting only the four orthogonal neighbours would split a single diagonal stroke of pixel art into a row of separate boxes.

Three numbers govern the result. The first decides what tracing is allowed to see in the first place; the other two are filters over what it found, and they run in this order:

ControlWhat it doesStarts at
Alpha thresholdOpacity at which a pixel counts as content1
Minimum areaDrops a box whose width times height is below this4
Merge gapRejoins boxes closer than this on both axes at once2

The area filter runs before the merge pass on purpose. A stray speck that was going to be discarded anyway must not first drag a neighbouring sprite's box outward on its way to being dropped.

Which slicer a sheet opens in is decided the same way every time. A lattice measured from the sheet's own gutters wins outright. Failing that, more than one traced sprite whose widths or heights differ by more than 10 percent of the largest means the sprites are separate objects and each is kept on its own. Failing that too, the grid slicer stays in force with numbers you type. The status line states which happened and why, and one button switches.

Sheets With No Transparency

A JPEG has no alpha channel, so what looks like empty space around the sprites is made of real opaque pixels — and both detectors would see one enormous blob covering the whole image. The tool therefore reads the colour that dominates the sheet's outer border, keys it out before tracing runs, and says so. The starting tolerance is 16: a pixel within 16 units of the key colour in RGB counts as background, which is wide enough for the ringing that JPEG compression leaves around a hard magenta edge and narrow enough to leave a saturated sprite colour alone. Both the colour and the tolerance are editable, and the tolerance runs up to 128.

Reading Order, Not Raster Order

Tracing emits sprites in the order their first pixel is met, scanning the sheet top to bottom. That is not the order a person reads a sprite sheet in. A short sprite whose top edge happens to sit above its taller neighbour's would come out first under a plain sort by vertical position, even though the two plainly sit in the same row.

So sprites are grouped into horizontal bands instead. A sprite joins the current band when its vertical span overlaps the band's by at least half of whichever of the two spans is shorter, and the band's span grows as sprites join it — which is what lets one band follow a row of sprites that drifts gradually down the sheet. Only then is each band sorted left to right.

One Box For Every Sprite

Every sprite is seated in the same box, which is what makes the packed sheet a grid a game engine can index. Automatic sizing takes the largest selected sprite on each axis independently — a wide sprite and a tall one together give a box big enough for both — adds the padding on both sides, and rounds up to the next even number, or to the next power of two if you ask for that instead. Manual sizing takes a typed width and height, or one of the presets: 32, 64, 128, 256 and 512.

Padding insets all four sides of the box, and the anchor decides where the sprite sits in whatever room is left. The default is the bottom centre, because feet stay planted: anchoring a taller jump frame at the centre would push the character's feet upward relative to the shorter frames beside it, and the whole character would appear to slide up and down as the animation plays. All nine anchors are one click away.

A sprite too big for the box is reported rather than silently resized. The default names the frames that do not fit in a red badge, spelling out the first six and counting the rest, and then places each of them at its full size anyway — from a negative offset inside its own cell, which on the packed sheet means the sprite runs under its neighbour or off the edge. That is the deliberate half of "warn": nothing is resized behind your back, and the badge names exactly which frames to do something about. Raising the box size, deselecting the frame, or switching to scale-down all resolve it; scale-down shrinks those sprites by the same factor on both axes so their proportions survive. Every placement the tool computes is a whole number of pixels, so the rectangle drawn into the PNG and the rectangle written into the JSON are the same rectangle — including when that rectangle sits outside its own cell.

The Packed Sheet And Its Atlas

The sheet lays the boxes out as a nearly square grid by default, or as a single horizontal strip, or across a column count you choose. A margin is applied to all four edges of the sheet; spacing is applied only between neighbouring cells, never past the last one.

The atlas ships as TexturePacker JSON, in the hash flavour keyed by file name or the array flavour with a file name inside each entry. Each entry states where the sprite's pixels sit on the packed sheet, where those same pixels sit inside their own declared box, how big that box is, and whether the sprite was trimmed or offset within it — the last three are how an engine restores a sprite's original placement after trimming, and getting them wrong shows up far away, inside your game. The metadata names the exact PNG the sheet export writes, so the pair always describes itself.

"Download sheet, atlas and GIF" wraps all three into one ZIP under a shared name. The ZIP of individual frames is still there too, two tabs to the left, and on this page it starts with every frame seated in the shared box — so its PNGs all come out at the box size, each sprite sitting exactly where it sits in the corresponding cell of the packed sheet. A switch on that tab gives one PNG at each frame's own size as sliced instead — that setting takes the frame rect itself, so with trimming on it still ships the full untrimmed slice.

Frequently asked questions

Is my sprite sheet uploaded anywhere?
No. The image is decoded in your browser, traced, packed and written to a PNG and a JSON file by JavaScript running in the same tab. Your file never travels over the network, which is also why the tool keeps working offline once the page has loaded.
My sprites are scattered, not on a grid. Will this still work?
That is what this page is for. When no regular lattice can be measured off the sheet, every sprite is traced on its own instead, using 8-connected labelling over the sheet's opacity — two pixels that touch only at a corner belong to the same sprite, so a diagonal stroke of pixel art is not split in half. Tracing is chosen automatically when more than one sprite is found and their widths or heights differ by more than 10 percent of the largest; if everything is the same size the grid slicer is kept instead, because its column and row numbers are editable. Whichever is chosen, the tool says which one and why, and a single button switches to the other.
What do the three detection sliders do?
Alpha threshold is the opacity at which a pixel starts counting as part of a sprite, from 1 to 255, and it starts at 1 so that any non-transparent pixel counts. Minimum area drops a box whose width times height falls below it, from 1 to 1024 square pixels, starting at 4 to clear stray specks without eating a small item sprite. Merge gap rejoins two boxes when the empty space between them is smaller than the gap on both axes at once, from 0 to 64 pixels, starting at 2 — enough to reunite a sword or a spark that sits one pixel clear of its owner. Specks are dropped before anything is merged, so a speck can never drag a neighbouring sprite's box outward.
My sheet is a JPEG with a magenta background. Can it still be split?
Yes, and it is set up for you. A sheet with no alpha channel has a background made of real opaque pixels, so the tool takes the colour that dominates the sheet's outer border and treats everything near it as empty when it traces sprites, with a starting tolerance of 16. The tolerance is a radius in colour space, so a pixel within 16 units of the key colour counts as background — wide enough for the ringing JPEG compression leaves around a hard magenta edge, narrow enough to leave a saturated sprite colour alone. The colour and the tolerance are both shown and both editable, and the tolerance slider runs to 128 for a noisier sheet.
Why does the box anchor default to the bottom centre?
Because feet stay planted. Frames in a walk or a jump are rarely the same height, and anchoring a taller frame at the centre of the box pushes the character's feet upward relative to its shorter neighbours — during playback the whole character appears to slide up and down. Anchoring at the bottom centre keeps the ground line where it was. All nine anchors are available for sprites that hang from a ceiling or line up on a left edge instead.
What happens to a sprite that is bigger than the box?
It is reported rather than silently resized — but under the default it is not made to fit either. Warn puts a red badge under the box controls naming the frames that do not fit, spelling out the first six and counting the rest, and leaves each of them at its full size. In the packed sheet that means the sprite is drawn from a negative offset inside its own cell, so it runs under the neighbouring cell or off the edge of the PNG; the atlas JSON records the same out-of-cell rectangle, so the two files still agree with each other. The boxed ZIP — this page's default — is the one output that loses the overhang rather than recording it, because each frame is its own file and the file ends at the box's edge; the Raw crop setting on that tab keeps the whole sprite instead. The badge is the only warning, and it is there so you can act: scale it down shrinks the offending sprite by the same factor on both axes so its proportions survive, and a sprite that already fits is never enlarged to fill the box. A bigger box works too, and so does deselecting the oversized frame, because the automatic box size is measured from the selected frames only.
What is in the atlas JSON, and which engines read it?
It is the TexturePacker format, in either the hash flavour keyed by file name or the array flavour with a file name field in each entry. Pixi reads the hash flavour and Phaser 3 reads both. Each entry carries where the sprite's pixels sit on the packed sheet, where they sit inside their own declared box, that box's full size, whether the sprite was trimmed or offset within it, and a rotation flag that is always false because nothing here rotates a sprite before packing it. The metadata names the exact PNG the sheet export writes, so the two files always describe each other.