Skip to content

Godot 4 Sprite Sheet Import — SpriteFrames without Hand Slicing

Godot's SpriteFrames sheet dialog starts at 4×4 for every sheet of a new size and cannot measure margin or spacing. Here the grid is measured from the pixels, each row becomes a named animation, and the export is a SpriteFrames resource plus a ready AnimatedSprite2D scene.

A sprite sheet is one PNG that holds many frames in rows and columns. Godot 4 plays it with an AnimatedSprite2D whose SpriteFrames resource lists one region of the sheet per frame. You can build that resource in Godot with Add Frames from Sprite Sheet (enter the columns and rows, or the frame size, separation and offset), or let Nerulio measure the grid, turn every row into a named animation and export a finished .tres, PNG and scene. Frame size is sheet size ÷ columns and rows: 384 × 64 with 6 columns is 64 × 64. Godot 4 only; the bundle was checked in Godot 4.7.2.

Drop a sprite sheet, numbered frames, a GIF / APNG or an .aseprite file
Runs in your browser. Files are not uploaded.
Studio Sprite workspace: a 48 px samurai sheet with the detected grid, 60 frames in 10 animations, before Apply
The real Studio with a CC0 asset (OpenGameArt).
Exports to
  • 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

Sprite sheets, grids and what Godot needs

A sprite sheet puts the frames of one character on one image, read left to right and top to bottom. Most character sheets use one row per animation (idle, run, attack), so a row is an animation and a column is a moment inside it. With no border and no gaps, one frame is the sheet width ÷ columns wide and the sheet height ÷ rows tall.

Many sheets add a margin around the edge and spacing between frames. Then the sheet width is margin + columns × frame width + (columns − 1) × spacing (plus any right margin), and dividing the whole width by the column count gives a frame that is too wide. Godot's sheet dialog calls the margin Offset and the spacing Separation. A uniform sheet has one frame size everywhere; an irregular sheet (sprites of different sizes, or a packed atlas) has no grid at all and needs either atlas data or cutting by sprite outlines.

In Godot 4 an AnimatedSprite2D plays a SpriteFrames resource: named animations, each with a Speed in frames per second, a loop flag and a list of frames. A frame taken from a sheet is an AtlasTexture, a rectangle of the sheet. A plain Sprite2D with hframes and vframes can also show one cell at a time, but it divides the whole texture into equal parts, so it only fits sheets without margin and spacing. Canvas items use Linear filtering by default, which blurs pixel art until you switch to Nearest.

Nerulio measures candidate grids from the pixels (transparent separator lines, the period at which the art repeats, how alike the cells look, how often a cell edge would cut through a sprite) and shows the best one over the sheet with a high, medium or low confidence and its reasons. Nothing is cut until you press Apply. You can pick another candidate, type your own cell size, offset and gap, or cut an uneven sheet by sprite outlines instead. Empty cells are skipped, every row becomes an animation named row_1, row_2 … that you rename on the timeline, and frames start at 100 ms (10 FPS), because a PNG stores no timing.

Frame (cell) size
Width × height of one frame in pixels; with no margin or spacing, sheet width ÷ columns by sheet height ÷ rows.
Margin / Offset
Empty pixels between the sheet edge and the first frame. Godot's dialog: Offset.
Spacing / Separation
Empty pixels between neighbouring frames. Godot's dialog: Separation.
AtlasTexture
A rectangle of a larger texture; every frame of a sheet-based SpriteFrames is one.
Speed (FPS)
Frames per second of one SpriteFrames animation; each frame's relative duration multiplies one tick.

Worked example: frame size, margin and spacing

The same six-frame run cycle, laid out two ways:

Sheet A  384 × 64 px, 1 row × 6 columns, no margin, no spacing
  frame width  = 384 / 6 = 64      frame height = 64 / 1 = 64      → 64 × 64
  Godot dialog: Horizontal 6, Vertical 1           (Size becomes 64 × 64)

Sheet B  398 × 68 px, same frames with a 2 px margin and 2 px spacing
  width  = 2 + 6 × 64 + 5 × 2 + 2 = 398           height = 2 + 64 + 2 = 68
  Godot dialog: Size 64 × 64, Separation 2 × 2, Offset 2 × 2
  Horizontal 6 alone gives 398 / 6 = 66 px cells: each column drifts 2 px further

Nerulio on sheet B: cell 64 × 64, margin 2, spacing 2 (high confidence) → row_1, 6 frames
  Godot bundle: speed 10 FPS, every duration 1.0 (100 ms), loop on → one cycle 0.6 s

Change the frames to 125 ms on Nerulio's timeline and the bundle keeps 10 FPS but writes a duration of 1.25 on every frame: 1.25 ÷ 10 = 0.125 s. Godot's own dialog gives every frame 1.0, so there you change the Speed instead (8 FPS for 125 ms).

What it does

  • Grid, margin and spacing measured from the art, shown with a confidence before anything is cut.
  • Animations per row (or by hand on the timeline) with per-frame durations — Godot gets relative durations and loop.
  • Trim keeps each frame's offset through AtlasTexture margins, so frames do not jump.
  • The scene uses Nearest filtering and the bundle ships lossless, mipmap-free import settings.

How to do it

  1. Drop the sheet and press Apply.
  2. Rename the animations and adjust their timing.
  3. Open Pack & Export and pick Godot 4.
  4. Copy the folder into your project and open the .tscn.

Files you get

Pack & Export › Godot 4 writes one folder (named after your file; hero here):

FileWhat it is for
hero.tresThe SpriteFrames resource: one animation per row or tag with its speed, loop flag and relative durations; one AtlasTexture per frame with filter_clip on and a margin that restores trimmed frames to their full cell size.
hero.pngThe frames repacked into a new PNG (trimmed, 2 px apart, never rotated). Your original sheet is not referenced.
hero.tscnAn AnimatedSprite2D using the resource: Nearest filtering, autoplay of the first animation, centered off and the pivot (bottom centre unless you set one) as offset.
hero.png.importGodot import settings: lossless, no mipmaps, Fix Alpha Border off.
README-GODOT.mdThe import steps for this bundle.

Put it into Godot 4

  1. Unzip the bundle and copy the whole folder under res://, for example res://characters/hero/. Keep the .tres, the PNG and its .png.import together; the resource finds the PNG by a relative path.
  2. Wait until the FileSystem dock has imported the PNG. Godot reads the shipped .png.import, so the texture comes in lossless and without mipmaps.
  3. Drag hero.tscn into your scene, or select your own AnimatedSprite2D and load hero.tres into its Sprite Frames property.
  4. Open the SpriteFrames panel: check each animation's name, its Speed (10 FPS unless you changed it) and its loop toggle. Rename the row_N animations here if you did not do it in Nerulio.
  5. Switch animations from a script with $Hero.play("run"). An animation with loop off stops on its last frame and emits animation_finished; play the next one from that signal.
  6. For pixel art, set Project Settings › Rendering › Textures › Canvas Textures › Default Texture Filter to Nearest so your other nodes are sharp too. For a low-resolution game, the Godot docs recommend Display › Window › Stretch › Mode viewport with Scale Mode integer.

Without Nerulio, Godot 4.7 cuts a uniform sheet itself: select the AnimatedSprite2D, choose New SpriteFrames, click Add Frames from Sprite Sheet in the SpriteFrames panel and open the PNG. Set Horizontal and Vertical, or Size, Separation and Offset for a sheet with gaps; Auto Slice guesses the counts but sets separation and offset to 0. Click the frames of one row (Frame Order sets the order), press Add N Frame(s), rename the animation and set its Speed (FPS). Repeat per row. Every frame gets a relative duration of 1.0, and the dialog starts again from 4 × 4 for a sheet of another size.

Check the result

  • Each animation in the SpriteFrames panel has as many frames as its row has filled cells; a transparent frame at the end means an empty cell was added.
  • Run the scene: six 100 ms frames repeat every 0.6 s.
  • Zoom the 2D viewport to 400 %: pixel edges stay square, and no line of a neighbouring frame shows at the border of a frame.
  • Switch between animations: the feet stay on the same spot. If the character hops, the frames are not aligned on a common pivot.

Troubleshooting

SymptomLikely causeHow to checkFix
Frames slide a little further with every columnThe frame size includes the spacing, or the margin and spacing were left at 0Check that sheet width = offset + columns × size + (columns − 1) × separationEnter the real Size, Separation and Offset in Godot, or let Nerulio measure them; see measuring frame size
A thin line of the next frame shows at the edgeTexture bleeding: filtering or a sub-pixel position samples pixels just outside the frame, and the frames touch with no spacingZoom in while the sprite moves; look for Linear filtering and fractional positionsUse Nearest filtering; keep filter_clip on (the bundle sets it; on a Sprite2D region it is Region › Filter Clip); give frames padding, see edge bleeding
The sprite is blurryThe node inherits the project default filter, which is LinearInspector › CanvasItem › Texture › FilterSet Nearest on the node or as the project default; see blurry pixel art in Godot
The character wobbles from frame to frameThe art is not placed identically inside each cell, or trimmed frames of different sizes are centredOnion-skin the frames in Nerulio's timeline; compare the feet positionAlign the frames before export (the bundle restores trimmed frames to full cell size); see jitter after trimming
The animation plays too fast or too slowGodot's dialog gives every frame 1.0, so the Speed alone sets the timing; or Speed Scale is not 1SpriteFrames panel: Speed (FPS); Inspector: Speed ScaleSet Speed to 1000 ÷ your frame time in ms (100 ms → 10 FPS), or set per-frame times in Nerulio; see frame duration in Godot
Nerulio proposes the wrong grid (for example 48 × 96 instead of 48 × 48)The art of one frame spans two cells, or the sheet mixes sprite sizesThe Import panel shows the confidence and reasons of each candidatePick another candidate, type a custom grid, or cut by sprite outlines. Nerulio cannot separate frames whose pixels overlap each other; draw those boxes by hand

Other ways to do it

OptionWhen it is the better choice
Godot's Add Frames from Sprite Sheet (manual route above)A uniform sheet with one or two animations at an even frame rate: no extra files, no export step.
Sprite2D with hframes / vframes and an AnimationPlayer keying frameYou already animate other properties with an AnimationPlayer; works only when the sheet has no margin and no spacing.
The .aseprite file instead of its PNGTags and per-frame timing come along: Aseprite to Godot.
A packed atlas with its JSONFrames of different sizes already packed by another tool: TexturePacker to Godot.

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.

TargetFilesCheck
Godot 4.tres SpriteFrames · .tscn AnimatedSprite2D · .png.importVerified 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 · AnimationClipVerified Unity 6000.5.3f1Batch mode: sprite rects, pivots, pixels, clip keys and durations checked.
Phaser 3 / 4atlas JSON (multiatlas) · .anims.jsonVerified Phaser 3.90 + 4.2Frames and animations drawn by both versions.
PixiJS 8spritesheet JSON · animations · anchor · meta.scaleVerified PixiJS 8.21Rotated and multi-page atlases included.
Aseprite JSONhash / array · frameTags · slicesVerified Phaser load.aseprite · PixiJS 8.21Tags keep their direction and repeat count.
.asepritelayers · tags · durations · slicesVerified Aseprite 1.3.18Opened and rendered by the Aseprite command line.
Spine / libGDX.atlasVerified spine-canvas 4.2Every region drawn by the Spine runtime, rotated and trimmed; libGDX itself was not run.
Starling / Sparrow XMLXML per pageVerified 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ÖVELua quads · nerulio_atlas.lua · main.luaVerified LÖVE 11.5Every frame drawn and every animation stepped through the shipped helper.
Defold.atlas · .tilesourceBuilt Defold bob.jar 1.13.1Built by Defold's command-line builder; the built texture's UVs were read back.
GameMakerspr_<name>_stripN.png · gamemaker.jsonUNVERIFIEDNot loaded in GameMaker (no headless GameMaker). The strips are cut the way its strip importer cuts them.
CSS sprites.css · preview .htmlVerified ChromiumEvery class drawn from the shipped stylesheet.
GIF / APNGone file per animationDecoded 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

  • The bundle targets Godot 4 only; Godot 3 cannot read the resource.
  • Nerulio repacks the frames into its own PNG, so later edits to your original sheet need a new export.
  • Hitboxes stay in the resource metadata; the export creates no collision nodes.
  • 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

Nerulio's Godot bundle was loaded by Godot 4.7.2 in the verification runs, including a 288 × 480 samurai sheet cut into 48 × 48 cells (60 frames in 10 row animations) and a Kenney character sheet; every frame was drawn at 1× and compared with the source, and the shipped scene was drawn sharp at 4×. With the default choices the importer cut 16 of 16 real test assets correctly. The Godot sheet-dialog labels come from the Godot 4.7 editor source; everything else about Godot follows the Godot 4.7 documentation.

Official documentation used

Questions

Does it work for Godot 3?

The export targets Godot 4 (verified in 4.7.2). Godot 3 was not tested.

Can I keep my original PNG and just get the regions?

No: the export always repacks the frames into its own PNG, 2 px apart. With trim off, every region is a whole cell of your grid.

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).