Pack frames into a sheet with an atlas that names each sprite's own rectangle, or slice a sheet back apart with margin and spacing read off the pixels. The page checks every frame comes back out of the sheet byte for byte.
Initializing in your browser…
Twelve procedural patterns that tile exactly, verified live with a measured wrap error and a 3 by 3 repeat view. Perlin, fractal, ridged, Voronoi, marble, wood and geometric grids.
Caption a picture with text that is measured, wrapped and kept inside the frame. The preview canvas is the exported file, pixel for pixel.
Chord diagrams whose fingerings are found by searching the fretboard, so the notes under the dots really are the chord's. Ten tunings, sixteen qualities, and the notes named on the page.
Eight 32 px frames packed with 4 px of padding, then sliced back out of the sheet the same tool just wrote.
Input
sprite-0.png .. sprite-7.png · 4 columns · 4 px padding
Frames that came back unchanged
before after no padding 8 of 8 8 of 8 4 px padding 0 of 8 8 of 8 a 25 px frame in a 32 cell (67.5) (68) its atlas rectangle 32 x 32 25 x 25
The slicer computed its frame size as floor(sheetWidth / columns), so a sheet packed with 4 px of padding sliced into 35 by 34 frames and not one frame survived. Two smaller faults sat beside it: a 25 px frame centred in a 32 px cell landed on a half pixel, where drawImage resamples, and the atlas reported the CELL rather than the sprite, so an engine drew it three and a half pixels off centre.
Pack frames into one sheet with a TexturePacker, JSON array or CSS atlas, or slice an existing sheet back into frames. Every frame is placed on a whole pixel, the atlas names each sprite's own rectangle, and the page cuts every frame back out of the sheet it just packed to prove it comes back unchanged.
Upload the frames, reorder them so they play in sequence, set Columns to your frames per row and a few pixels of padding, then download the PNG plus the JSON Hash atlas. The verdict above the preview says all frames come back from the sheet byte for byte.
Switch to Slice mode and upload the sheet. The margin, spacing and frame size are read off the separating rows and columns, the grid is drawn over the image, and any rectangle that runs off the sheet is red.
A sprite sheet packer carries its own reference inside it: pack the frames, slice the sheet back, and every frame must come back byte for byte. This page runs that check on itself. After it packs, it cuts each frame back out of the sheet at exactly the rectangle its atlas names, compares it with the file that went in, and prints the verdict above the preview. Three things have to be right for that to pass, and all three were wrong before.
First, every frame is placed on a WHOLE pixel. Centring a 25 pixel sprite in a 32 pixel cell puts it at 3.5, and `drawImage` at a half pixel resamples: the frame comes out blurred and is no longer the file that went in. Offsets are rounded here, and the page says how many frames needed it. You can also switch the alignment to the cell's top left, where nothing ever needs rounding.
Second, the atlas names the SPRITE, not the cell. The old metadata reported x 64, y 64, w 32, h 32 for a 25 by 25 sprite drawn at (67.5, 67.5), so a game engine reading the atlas drew it three and a half pixels off centre with 399 pixels of empty cell attached to it. Every entry now carries the sprite's own rectangle plus the TexturePacker fields a real atlas has: `sourceSize`, `spriteSourceSize`, `rotated` and `trimmed`. Three formats are written from one plan: TexturePacker JSON Hash (which Phaser 3 and PixiJS both read), JSON Array, and CSS with a class and a background-position per frame. Two files with the same name no longer overwrite each other in the atlas: the names are made unique and the page says how many collided.
Third, a sheet with padding can be sliced. The old slicer had no padding, margin or spacing control at all and computed its frame size as floor(sheetWidth / columns), so a sheet this same tool had just packed with 4 px of padding sliced into 35 by 34 frames and 0 of 8 came back. Slice mode now takes margin and spacing, and reads them off the sheet for you: it finds the separating rows and columns, measures the STRIDE from where each frame's content starts, and fills the grid in. Where the frames have transparent edges of their own, the split between frame and gap genuinely cannot be read off the pixels (a 32 px frame with a 4 px transparent border looks exactly like a 28 px frame with 4 px more padding), and the page says so rather than guessing. The grid that will actually be cut is drawn over the source image at the sheet's own coordinates, with any rectangle that runs off the sheet in red, and the leftover on each axis is stated in pixels.
Packing offers a uniform grid, where every cell is the largest frame, and packed rows, where each row is only as tall as its own tallest frame. There is a margin around the sheet as well as padding between cells, an option to round the sheet up to a power of two for a GPU texture, and the classic magenta debug background. Everything runs in the browser canvas; nothing is uploaded.
After packing, the page cuts each frame back out of the sheet at exactly the rectangle the atlas names and compares it with the file that went in. All frames unchanged means the sheet and the atlas agree with the sources; anything else names the frame and the number of pixels.
Because an atlas that reports the CELL rather than the sprite puts the sprite wherever it happens to sit inside that cell. The entries here are the sprite's own rectangle, so a 25 by 25 sprite reads as 25 by 25 at the exact pixel it was drawn.
Yes. Enter it as Spacing, and the margin around the sheet as Margin. Uploading a sheet reads both off the separating rows and columns for you.
Because your frames have transparent edges of their own, and no reading of the pixels can separate a 32 px frame with a 4 px transparent border from a 28 px frame with 4 px more padding. The stride, which is the frame plus the gap, is always right, so the frames are cut in the right places; adjust the frame size if you want the empty border included.
Combining many small images into one file reduces the number of HTTP requests and lets a GPU keep one texture bound, which is why every 2D engine wants one.
Consistent sizes work best on the uniform grid, because every cell is the size of the largest frame. Packed rows waste less on mixed sizes. Either way the atlas records each sprite's real rectangle.
Images are decoded, edited, and exported entirely inside this browser tab. No originals, exports, or metadata are uploaded.