Free · Private · In-browser

Sprite sheet maker

Pack your animation frames into one sprite sheet on a uniform grid, with the padding that stops frames bleeding into each other. Download the PNG and a JSON atlas that your engine can load straight away.

Your frames never leave your device Live animation preview PNG + JSON in one ZIP
Add your animation frames click, drop, or paste — select them all at once

How it works

How to make a sprite sheet

A sprite sheet is one image holding every frame of an animation on a regular grid. Making one is mostly bookkeeping: keep the cells identical, keep the order right, and leave a gap between them. This maker does all three and hands you the coordinates.

1

Drop in every frame

Select them all at once — click, drag the folder contents in, or paste. Nothing uploads. The frames are sorted into natural filename order straight away, so run_2 lands before run_10 instead of after it.

2

Set the grid, watch it move

Choose the columns and the padding. The animation preview plays your frames in sheet order at a speed you pick, so a frame that is out of sequence or facing the wrong way shows up here rather than in your engine an hour later.

3

Take the PNG and the JSON

One click gives you a ZIP with the sheet and a JSON atlas listing every cell rectangle by filename. If your engine slices a uniform grid on its own, ignore the JSON and use the cell size printed under the preview.

The reason it is worth doing

Why one sheet beats fifty PNGs

You could load every frame as its own texture and animate by swapping them. It works, and on a small project nobody will notice. It stops being free the moment there is a lot on screen.

Fewer draw calls

A renderer can bundle a run of sprites into one batch as long as they share a texture. Change the texture and the batch has to be flushed and a new one started. Fifty separate frame files means the renderer is forced to break the batch every time the animation ticks; one sheet lets an entire crowd of enemies go out together.

Fewer requests, less overhead

On the web, fifty files is fifty round trips before anything can play. Every texture also carries its own memory overhead beyond the pixels themselves. Packing them into one sheet turns that into a single fetch and a single upload to the GPU.

Frames stay together

A sheet is self-documenting in a way a folder of files is not. The order is visible, a missing frame is a hole you can see, and there is no chance of shipping the sheet without one of its frames because there is only one file to ship.

The engine expects it

Sprite animation in most 2D engines is built around sheets: you hand over one image plus a cell size, or one image plus an atlas, and ask for frames by index or name. Feeding the pipeline what it is designed for means less glue code of your own to maintain.

The hard requirement

Every cell the same size

This is the one rule a sprite sheet cannot bend, and it is what separates a sprite sheet from an ordinary photo collage. If all the cells are identical, frame number n is at (n % columns, n / columns) and no lookup table is needed. The moment cells vary, that arithmetic is dead and every frame has to be described individually.

Your frames do not have to be identical to start with. The maker sizes the cell to hold the largest one, then places each frame inside its cell at its own native pixel size — nothing is scaled, nothing is softened. What you choose is the anchor: where in the cell the smaller frames sit.

Anchor to the bottom for anything that stands on the ground

A walk cycle where the frames differ by a pixel or two in height will bob up and down if the frames are centred, because the character's feet move relative to the cell. Anchor to bottom centre and the feet stay put while the head does the moving, which is what they do in real life.

Centre for effects and projectiles

An explosion, a spell burst or a spinning coin grows outward from a point. Centre those, and the sprite's origin stays at the middle of the cell no matter which frame is showing.

Natural sort is not the same as alphabetical

Sorted as plain text, frame_10.png comes before frame_2.png, which quietly reorders your animation. This maker compares the numbers inside the names as numbers, so ten lands after two. It is worth zero-padding your exports anyway — frame_02 sorts correctly everywhere.

Set the cell by hand when the sheet has to match something

If you are replacing an existing sheet, or your engine already has a frame size baked into it, switch cell size to Custom and type the numbers. Frames larger than the cell are scaled down to fit; everything smaller is left alone.

Why the gaps

Padding, and the thin line of the next frame

Pack the cells edge to edge and sooner or later a sliver of the neighbouring frame appears along the edge of your sprite — usually only when the camera moves, or at certain zoom levels, which makes it maddening to reproduce.

The cause is filtering. When a sprite lands on a fractional pixel or is drawn at a scale that is not exactly 1:1, the GPU blends between neighbouring texels to work out each output pixel. At the boundary of a frame, the neighbours it blends with are the first pixels of the frame next door. Leave a gap and it blends with empty space instead.

Padding 0

Cells touch. Filtering at the seam reaches into the next frame.

Bleeds at fractional scales

Padding 2

Two transparent pixels between cells absorb the sampling.

Clean edges

Two pixels is the usual answer and the default here. Go higher if you scale sprites up a long way or generate mipmaps, since both make the filter reach further. Go to zero only when you know the sheet will be drawn at exact 1:1 with nearest-neighbour filtering, which is the common setup for pixel-art games — and even then the padding costs you almost nothing.

Padding solves sampling outside your frame. The related trick you may see mentioned is extruding: repeating a frame's own edge pixels outward into the gap, so a filter that strays over the line finds more of the same sprite instead of transparency. Extruding matters most when a sprite is drawn scaled up and you would otherwise get a faint transparent halo at its border. Padding alone is enough for the great majority of 2D work.

Getting it into your project

Using the sheet and the atlas

There are two ways an engine reads a sheet, and the words for them get muddled constantly — which is the usual reason a sheet loads but nothing appears on screen.

A sprite sheet has fixed-size cells and you ask for frames by number: the engine slices the grid itself from three values, so no data file is involved at all. A texture atlas is packed to save space, holds frames of assorted sizes, and you ask for them by name — which only works because a data file lists where each one is.

This maker produces the first kind: a uniform, untrimmed grid. That is deliberate, and it is the reason the output drops into any engine without a plugin. The JSON is a convenience on top, for loaders that would rather work by name.

RouteWhat you hand overNotes
Grid slicing
works everywhere
The PNG, plus cell size, padding and margin from under the previewEvery 2D engine can do this and none of them need a plugin for it. Phaser's load.spritesheet takes a frameConfig whose frameWidth/frameHeight are the cell, whose spacing is the padding and whose margin is the margin — the maker reports all three separately for exactly this reason. Godot slices a uniform sheet with the hframes and vframes on a Sprite2D, or via Add frames from sprite sheet on a SpriteFrames resource. Unity does it in the Sprite Editor's grid slice.
Texture atlas
by name
The PNG and the .json togetherLoaders built around the TexturePacker-style atlas read this directly — Phaser's load.atlas accepts both the Hash and Array shapes and works out which it was given. Godot and Unity do not read this JSON out of the box; they want a plugin or a short script. If they are your target, take the grid route above instead — it costs you nothing here.
Your own loaderThe Plain coordinates JSONA flat list of { name, x, y, w, h } and nothing else. The least there is to parse if you are writing the loading code yourself, or driving CSS background-position for an icon sprite.

The Hash and Array variants carry identical information and differ only in shape: Hash keys the frames by filename, Array keeps them in order with the filename as a field. If your loader does not say which it wants, try Hash — it is the more common of the two.

// Plain coordinates — the whole format, nothing hidden
{
  "frames": [
    { "name": "run_01.png", "x": 0,  "y": 0, "w": 64, "h": 64 },
    { "name": "run_02.png", "x": 66, "y": 0, "w": 64, "h": 64 }
  ],
  "meta": {
    "image": "run-sheet.png",
    "size":  { "w": 256, "h": 128 },
    "cell":  { "w": 64, "h": 64 },
    "columns": 4, "rows": 2,
    "padding": 2, "margin": 0
  }
}

Note the x of the second frame: 66, not 64. That is the 64-pixel cell plus the 2 pixels of padding — the arithmetic your engine has to do too, and the reason the padding value has to travel with the sheet rather than living in your head.

What people pack

Where a sheet earns its keep

Character animation

Idle, walk, run, jump, hit. One sheet per character with a row per action is the most common layout there is: set the columns to your longest action and each action gets its own row, so the frame range for a state is one arithmetic step.

Effects and particles

Explosions, impacts, smoke puffs and spell bursts are usually exported as a numbered image sequence from an effects tool. Drop the sequence in, centre the anchor, and the sheet is done.

Tilesets and terrain

A tileset is a sprite sheet whose cells happen to be map tiles. Uniform cells matter even more here, because the map is addressed by grid coordinate. Keep padding modest for tiles and never scale them.

UI icons and web sprites

Outside games the same idea is a CSS sprite: one image of icons, positioned with background-position. The plain coordinate JSON gives you every offset you need, and one request replaces a folder of tiny files.

Why this maker

Private by design, free by default

No upload

Frames are packed in your browser with the Canvas API and the ZIP is built in memory. Unreleased art never reaches a server — no queue, no retention policy to read.

You see it move

The animation plays before you export. A frame in the wrong place is obvious in two seconds instead of after a round trip through your engine.

Coordinates included

Most free packers hand you an image and leave you to measure it. You get the atlas as well, in the shape your loader wants, with the padding accounted for.

Real transparency

PNG by default with the alpha channel intact, and a warning if you pick a format that would flatten it. No watermark, no signup, no paid tier.

FAQ

Frequently asked questions

What is a sprite sheet?
A sprite sheet is a single image holding many smaller images laid out on a regular grid. Each cell is one animation frame or one object. Your engine draws a rectangle out of that one texture instead of loading dozens of separate files, which cuts load time and lets the renderer draw many sprites in a single batch.
How do I make a sprite sheet from separate PNG frames?
Drop all the frames into this maker at once. They are sorted into natural filename order, so frame_2 comes before frame_10, and packed into a uniform grid. Set the columns and padding, check the animation preview plays in the right order, then download the sheet as a PNG together with a JSON atlas describing every cell.
Why do all the frames have to be the same size?
Because uniform cells are what makes a sheet usable without extra data. If every cell is the same box, an engine can find frame number 7 with arithmetic alone. Frames of different sizes still work here: the cell is sized to fit the largest one and the smaller frames are placed inside it at their native size, so the grid stays regular.
Why are there faint lines or edges of the next frame showing in my game?
That is texture bleeding. When a sprite is drawn at a non-integer scale or position, bilinear filtering samples just outside the frame and picks up the neighbouring cell. Adding padding between the cells gives it empty space to sample instead of your next frame. Two pixels is usually enough; use more if you scale sprites up heavily.
What is the JSON atlas file for?
It lists the pixel rectangle of every frame in the sheet, keyed by its original filename. Engines that read a texture atlas load the PNG and the JSON together and let you refer to frames by name instead of by number. If your engine slices a uniform grid by itself you can ignore the JSON and just use the cell size the maker reports.
Should I export a sprite sheet as PNG or JPG?
PNG, almost always. JPG has no transparency, so every frame gets an opaque rectangle behind it and the sprite shows as a box in your game. JPG also introduces compression noise around hard edges, which is exactly where sprite art lives. This maker defaults to PNG and warns you if you pick a format that discards the alpha channel.
Do sprite sheet dimensions need to be a power of two?
Not for ordinary sprite drawing on current hardware, but it is still a safe default. Power-of-two textures avoid restrictions that older graphics pipelines place on mipmapping and tiled wrap modes, and some engines pad non-power-of-two textures up in memory anyway. The maker has a toggle so you can turn it on when your target needs it and leave it off when you would rather not waste the pixels.
Are my images uploaded anywhere?
No. Every frame is decoded and drawn in your own browser with the HTML5 Canvas API, and the ZIP is assembled in memory on your device. Nothing is sent to a server, nothing is stored, and closing the tab discards everything.

Link to This Tool

Bloggers, mods, and forum members are welcome to share this page. Copy the snippet below to credit the source.

<a href="https://freeimagemerger.com/sprite-sheet-maker/">Sprite Sheet Maker</a> — free online tool