Sprite Sheet Frame Size — Measure Cell, Margin and Spacing
Phaser, Godot, Unity and Defold all ask the same four questions before they cut a sheet: how wide one frame is, how tall, how far the first frame sits from the edge, and how many pixels lie between frames. The Sprite workspace measures those numbers from the pixels and shows them with a confidence, so you stop trying 32, then 48, then 64.
A sprite sheet's frame size is the width and height of one cell. Without a border or gaps it is sheet width ÷ columns by sheet height ÷ rows: 384 × 64 with 6 columns gives 64 × 64. With a margin m on both sides and spacing s between frames, frame width = (sheet width − 2m − (columns − 1) × s) ÷ columns. Phaser, Godot and Unity all ask for those four numbers (frame width, height, margin, spacing). Nerulio measures them from the pixels and shows a confidence, or skips the typing by writing every frame's exact rectangle into atlas data.

- Godot 4Verified· Godot 4.7.2
- Unity 6Verified· Unity 6000.5.3f1
- Phaser 3 / 4Verified· Phaser 3.90 + 4.2
- PixiJS 8Verified· PixiJS 8.21
- Aseprite JSONVerified· Phaser load.aseprite · PixiJS 8.21
- .asepriteVerified· Aseprite 1.3.18
- Spine / libGDXVerified· spine-canvas 4.2
- Starling / Sparrow XMLVerified· Phaser 4.2
- LÖVEVerified· LÖVE 11.5
- DefoldBuilt· Defold bob.jar 1.13.1
- GameMakerUNVERIFIED
- CSS spritesVerified· Chromium
- GIF / APNGDecoded· Pillow
The four numbers that describe a grid
A grid sheet is fully described by the frame (cell) width and height, the margin between the sheet edge and the first frame, and the spacing between neighbouring frames. From those, frame n of a row starts at x = margin + n × (frame width + spacing); the frame width plus the spacing is the pitch at which the art repeats.
Dividing the sheet size by a guessed column count only works when margin and spacing are 0. A result that is not a whole number (398 ÷ 6 = 66.33) is the tell-tale sign of a margin or spacing, or of a wrong column count. A whole number is not proof either: a 256 px sheet divides by 8, 16, 32, 64 and 128, so count the columns from the art, not from the arithmetic.
Nerulio does not trust "divides evenly". It looks for fully transparent separator lines where a gap should be, the period at which the columns and rows repeat, cells whose content looks alike, and cell edges that would cut through a sprite. Candidate sizes include 8–128 px, sizes suggested by the measured pitch and sizes that divide the sheet evenly; margins up to 64 px and spacing up to 16 px are searched. Each candidate shows high, medium or low confidence, its reasons and how many cells hold pixels.
A sheet whose sprites have different sizes (or a packed atlas) has no single frame size. Cutting it by sprite outlines and exporting atlas data, which stores one rectangle per frame, replaces the grid numbers altogether.
- Frame (cell) size
- Width and height of one frame in pixels.
- Margin
- Empty border before the first frame; Godot calls it Offset, Unity Offset, Phaser margin.
- Spacing
- Empty pixels between two frames; Godot calls it Separation, Unity Padding, Phaser spacing.
- Pitch
- Frame size + spacing: the distance from one frame's left edge to the next.
- Columns × rows
- How many frames fit across and down: floor((sheet − margin + spacing) ÷ pitch).
Worked example: a sheet with margin and spacing
Sheet 398 × 134 px, the art shows 6 columns × 2 rows
398 / 6 = 66.33 not whole → there is a margin and/or spacing
first opaque column at x = 2, transparent gap between frames = 2 px
frame width = (398 − 2 × 2 − 5 × 2) / 6 = 384 / 6 = 64
frame height = (134 − 2 × 2 − 1 × 2) / 2 = 128 / 2 = 64
pitch = 64 + 2 = 66 → frames start at x = 2, 68, 134, 200, 266, 332
Phaser frameWidth 64, frameHeight 64, margin 2, spacing 2
Unity Grid By Cell Size: Pixel Size 64 × 64, Offset 2 × 2, Padding 2 × 2
Godot Add Frames from Sprite Sheet: Size 64 × 64, Separation 2 × 2, Offset 2 × 2Check: the last frame ends at 332 + 64 = 396, and the 2 px right margin brings the row to 398. If your numbers do not land exactly on the sheet width, one of them is wrong.
What it does
- Cell size, margin and spacing are measured from the pixels — transparent separator lines, the repeat pitch of the art, content crossing a cell edge — and each candidate grid carries a high, medium or low confidence with its reasons.
- The best grid is drawn over the sheet as a preview; nothing is cut until Apply, and another candidate is one click away.
- For a sheet no measurement can read, the custom grid takes cell width and height, offset X/Y (the margin) and gap X/Y (the spacing) — the same four numbers engines ask for.
- Uneven sheets can be cut by islands instead of a grid; such a sheet has no single frame size, and an atlas export is the way into the engine.
- Or skip the typing: Pack & Export writes atlas data with every frame's exact rectangle, which Godot 4.7.2, Unity 6 and Phaser 3.90 / 4.2 loaded in the verification runs.
How to do it
- Drop the sheet on the Sprite workspace.
- Read the grid decision in the Import panel: cell size, offset and gap, with the confidence.
- If it is off, choose an alternative or type the custom grid, then press Apply.
- Copy the numbers into your engine using the table below, or open Pack & Export and export for that engine.
Measure it, then enter it
- Drop the sheet on Nerulio's Sprite workspace and read the grid decision in the Import panel: cell size, offset and gap, the confidence, and "N of M cells hold pixels".
- If the overlay is off, click another candidate or open the custom grid and type cell width and height, offset X/Y and gap X/Y; the preview shows the whole cells and the pixels left over before anything is cut.
- Phaser:
this.load.spritesheet('hero', 'hero.png', { frameWidth: 64, frameHeight: 64, margin: 2, spacing: 2 });endFramelimits the count if the last cells are empty. - Unity: set Sprite Mode to Multiple, open the Sprite Editor, choose Slice › Grid By Cell Size, enter Pixel Size, Offset and Padding, then Slice and Apply.
- Godot 4: SpriteFrames panel › Add Frames from Sprite Sheet, then Size, Separation and Offset; see sprite sheets in Godot.
- Or skip the numbers: Pack & Export writes each frame's own rectangle for Godot, Unity, Phaser and others.
Where the measured numbers go
| Engine | Field names | Measured value to enter |
|---|---|---|
| Phaser 3 / 4 · this.load.spritesheet | frameWidth, frameHeight, margin, spacing | Cell width, cell height, offset (one value), gap (one value) |
| Godot 4 · Sprite2D | hframes, vframes | Columns and rows — only when offset and gap are both 0 |
| Godot 4 · SpriteFrames | An AtlasTexture region per frame | Written for you by the Godot 4 export (verified in 4.7.2) |
| Unity · Sprite Editor › Slice › Grid By Cell Size | Pixel Size, Offset, Padding | Cell size, offset X/Y, gap X/Y |
| Defold · tile source | tile_width, tile_height, tile_margin, tile_spacing | Cell size; the Studio's .tilesource re-lays the frames with margin and spacing 0 (built with bob.jar 1.13.1) |
Field names are the engines' own. We did not run these hand-typed settings in the engines; the checked route is exporting an atlas from Pack & Export.
Troubleshooting
| Symptom | Likely cause | How to check | Fix |
|---|---|---|---|
| Frames drift a little further with every column | Spacing left at 0, or a frame width that includes the gap | Frame n should start at margin + n × pitch; compare with the art | Enter the measured spacing and the pure frame width |
| The last column or row is missing | Frame size or margin slightly too large, so the final cell does not fit | floor((sheet − margin + spacing) ÷ pitch) must equal the columns you see | Recheck margin and spacing; a right-hand margin is not part of any cell |
| Each frame shows half a sprite, or two sprites | Cell size is half or double the real one (48 × 96 read for 48 × 48) | Look for sprites cut by a cell edge or two feet in one cell | Pick the candidate with the right size; Nerulio's importer prefers the one that splits nothing |
| Left and top margins differ, and Phaser misplaces frames | Phaser's load.spritesheet has one margin and one spacing for both axes | Nerulio reports X and Y separately | Export a Phaser atlas instead; Unity's Offset and Padding take X and Y |
| Blank frames at the end of the animation | The sheet has unused cells | Count filled cells; Nerulio lists "N of M cells hold pixels" | Phaser endFrame, Unity with Keep Empty Rects off, or Nerulio (it skips empty cells) |
Engine exports and how each was checked
"Verified" means the exported files were loaded by the real engine or tool on real CC0 assets and the result was compared with the source. Anything that was not loaded is labelled as such.
| Target | Files | Check |
|---|---|---|
| Godot 4 | .tres SpriteFrames · .tscn AnimatedSprite2D · .png.import | Verified Godot 4.7.2Loaded by Godot; every animation frame drawn and compared with the source file. The scene uses Nearest filtering; rotation is never used. |
| Unity 6 | .unity.json · Editor/NerulioSpriteImporter.cs · AnimationClip | Verified Unity 6000.5.3f1Batch mode: sprite rects, pivots, pixels, clip keys and durations checked. |
| Phaser 3 / 4 | atlas JSON (multiatlas) · .anims.json | Verified Phaser 3.90 + 4.2Frames and animations drawn by both versions. |
| PixiJS 8 | spritesheet JSON · animations · anchor · meta.scale | Verified PixiJS 8.21Rotated and multi-page atlases included. |
| Aseprite JSON | hash / array · frameTags · slices | Verified Phaser load.aseprite · PixiJS 8.21Tags keep their direction and repeat count. |
| .aseprite | layers · tags · durations · slices | Verified Aseprite 1.3.18Opened and rendered by the Aseprite command line. |
| Spine / libGDX | .atlas | Verified spine-canvas 4.2Every region drawn by the Spine runtime, rotated and trimmed; libGDX itself was not run. |
| Starling / Sparrow XML | XML per page | Verified Phaser 4.2Phaser 3.90 draws trimmed XML frames wrongly (its own bug); the "Sparrow XML (Phaser 3)" preset turns trim off and passes in 3.90 and 4.2. |
| LÖVE | Lua quads · nerulio_atlas.lua · main.lua | Verified LÖVE 11.5Every frame drawn and every animation stepped through the shipped helper. |
| Defold | .atlas · .tilesource | Built Defold bob.jar 1.13.1Built by Defold's command-line builder; the built texture's UVs were read back. |
| GameMaker | spr_<name>_stripN.png · gamemaker.json | UNVERIFIEDNot loaded in GameMaker (no headless GameMaker). The strips are cut the way its strip importer cuts them. |
| CSS sprites | .css · preview .html | Verified ChromiumEvery class drawn from the shipped stylesheet. |
| GIF / APNG | one file per animation | Decoded PillowEvery frame and delay decoded again after export; APNG is exact. |
Evidence
GIF decoder: 408 of 408 frames identical to Pillow on 29 real GIFs; APNG 132 of 132 on 16 files. .aseprite: 231 corpus files imported, re-written and reopened in Aseprite 1.3.18 with the same tags, durations and pixels (231/231). Exports: 49 runs in the real engines on real CC0 assets, 47 pass — the 2 failures are Phaser 3.90's trimmed-XML bug, which the Phaser 3 preset avoids.
Limits
- A grid or island guess is a preview with a confidence; nothing is cut until you press Apply, and every choice has one-click alternatives.
- .aseprite files whose layers do not compose exactly (e.g. group opacity) are imported flattened, and the Import panel says why.
- Rotated frames in an imported atlas stay rotated; their export is UNVERIFIED.
- Images up to 32,768 px per side (268 million pixels), subject to your browser's memory.
Versions and sources
The grid measurement is Nerulio's own (src/game/grid-detect.js); on 16 real assets imported through the UI its default choices cut the right frames 16 times out of 16. The field names come from the Phaser, Unity 6 and Godot 4.7 documentation and editor source; these hand-typed settings were not run in the engines. The route that was run in Godot 4.7.2, Unity 6000.5.3f1 and Phaser 3.90 / 4.2 is the atlas export.
Official documentation used
Questions
My frames drift further off with every column. What is wrong?
The sheet has spacing between frames and the engine was told 0, or it got a frame width that includes the gap. Give the measured gap to Phaser as spacing or to Unity as Padding, and the measured cell size as the frame size.
Godot's hframes and vframes cut my sheet wrong.
hframes and vframes split the whole texture into equal parts, with no margin and no spacing, so they fit only a sheet that is exactly columns × frame width wide. For any other sheet, export a SpriteFrames resource from Pack & Export: its AtlasTexture regions carry each frame's exact pixel rectangle.
The margin is different on the left and on the top.
The Studio measures X and Y separately. Unity's Offset takes both. Phaser's load.spritesheet has a single margin and a single spacing value for both axes, so for such a sheet use the Phaser atlas export instead.
Can the measured size be wrong?
Yes, which is why it comes with a confidence and alternatives rather than a silent cut. On 16 real assets imported through the UI, the default choices gave the right frames 16 times out of 16.
Is it free, and do I need an account?
Everything on these pages is free and works without an account. There is no watermark and no export that is held back.
Are my sprites uploaded?
No. The Studio decodes, packs and exports in this browser tab. Projects are autosaved in the browser (IndexedDB) and can be saved as a .nerulio file.
Does it need Aseprite or an engine plugin installed?
No. .aseprite files are read and written in the browser, and each engine bundle carries what that engine needs (for Godot, the import settings and scene; for Unity and the Godot TileSet, a small editor script).