Aseprite to Godot 4 — .aseprite to SpriteFrames, No Plugin
No Aseprite binary on the PATH, no editor plugin: drop the .aseprite file, check its tags, and export a bundle that Godot 4.7.2 loaded and drew frame by frame in the verification run.
An .aseprite file holds frames with their own durations in milliseconds, tags (named frame ranges with a direction and repeat count), layers and slices. Godot 4 cannot open it directly: it plays a SpriteFrames resource on an AnimatedSprite2D. Nerulio reads the file in your browser, turns every tag into a SpriteFrames animation with the same timing, packs the frames into one PNG and adds a ready scene. Copy the folder under res://, drop the .tscn into your scene, and the first tag plays with Nearest filtering. Godot 4 only (checked in 4.7.2).

- Godot 4Verified· Godot 4.7.2
- .asepriteVerified· Aseprite 1.3.18
- 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
- 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
What is in an .aseprite file, and what Godot needs
Aseprite saves a whole animation document. Every frame has its own duration in milliseconds. Tags group a range of frames into a named animation (walk = frames 4–9) and say how it plays: forward, reverse or ping-pong, forever or a set number of times. Layers keep parts of the drawing apart, and slices mark rectangles such as a pivot point or a hit box.
Godot 4 plays frame animation with an AnimatedSprite2D node that reads a SpriteFrames resource. Each animation in it has a name, a speed in frames per second, a loop flag and a list of frames; each frame is a texture plus a relative duration. Godot shows a frame for relative duration ÷ (FPS × speed scale) seconds, so milliseconds have to be converted, not copied.
Nerulio writes every tag at 10 FPS, so one tick is 100 ms (Aseprite's default frame length), and gives every frame a relative duration of its milliseconds ÷ 100. The playback time of every single frame therefore matches Aseprite, even when the durations differ inside one tag.
- Tag
- A named frame range in Aseprite with a direction (forward, reverse, ping-pong) and a repeat count.
- SpriteFrames
- The Godot resource that stores animations: name, speed (FPS), loop and frames.
- AtlasTexture
- A rectangle of a larger texture. Every exported frame is one, pointing into the packed PNG.
- Relative duration
- Godot's per-frame multiplier: 1.0 lasts one tick of the animation's FPS, 2.0 two ticks.
Example: a 32 × 32 hero with three tags
The .aseprite file has 16 frames and three tags. This is what the export writes into the SpriteFrames resource:
Aseprite Godot 4 SpriteFrames
idle frames 0–3 125 ms each ∞ idle 10 FPS 1.25 × 4 loop on
walk frames 4–9 100 ms each ∞ walk 10 FPS 1.0 × 6 loop on
attack frames 10–15 100,100,100,100, attack 10 FPS 1.0, 1.0, 1.0, 1.0, loop off
150, 250 ms ×1 1.5, 2.5
duration = ms / (1000 / 10 FPS) = ms / 100 125 ms → 1.25 250 ms → 2.5
check 1.25 / 10 = 0.125 s 2.5 / 10 = 0.250 s (same as Aseprite)attack repeats once in Aseprite, so its loop flag is off: it plays once and stays on its last frame. In a script, play idle again from the node's animation_finished signal.
What survives the conversion
| In Aseprite | In Nerulio | In Godot 4 |
|---|---|---|
| Frame pixels (visible layers) | Composited frame, packed into one PNG page | An AtlasTexture region of that PNG |
| Frame duration (ms) | Kept per frame | Relative duration = ms ÷ 100; the animation speed is 10 FPS |
| Tag | One animation of the same name | A SpriteFrames animation of the same name |
| Direction reverse / ping-pong | Playback order written out (ping-pong: 0-1-2-3-2-1) | Frames listed in that order |
| Repeat ∞ | Loop | loop = true |
| Repeat 1, 2, 3… | Plays that many times in the preview | loop = false: Godot plays it once (a repeat count is not stored) |
| No tags at all | One animation default with every frame | Animation default |
| Slice with a pivot | Frame pivot (default without one: bottom centre) | Node offset = −pivot of the first frame, centered = false |
| Slices named hit / hurt … | Boxes per frame | Stored in the resource metadata nerulio, not as nodes |
| Layers, blend modes, opacity | Kept in the Studio | Flattened: Godot receives the composited frame |
Godot 4.7 also has a ping-pong loop mode of its own; the export does not depend on it because it writes the order frame by frame.
What it does
- Reads .aseprite in the browser: layers, tags (direction, repeat), per-frame durations, slices → pivots, boxes and 9-slice.
- SpriteFrames .tres with AtlasTexture regions, trim margins, relative durations and loop.
- An AnimatedSprite2D .tscn with texture_filter = Nearest and the pivot offset.
- A .png.import with lossless compression, no mipmaps and fix_alpha_border off, so faint pixels keep their colour.
How to do it
- Drop the .aseprite file.
- Check the tags and durations on the timeline (layers that compose exactly stay layers).
- Open Pack & Export and choose Godot 4.
- Unzip the bundle into your Godot project and open the scene.
Files you get
| File | What it is for |
|---|---|
hero.tres | The SpriteFrames resource: animations, FPS, loop flags, relative durations, AtlasTexture regions and trim margins; metadata nerulio with pivots, boxes and collision polygons. |
hero.tscn | A scene with one AnimatedSprite2D using the resource: Nearest filtering, autoplay of the first tag, the pivot as offset. |
hero.png | The packed frames (more pages when the frames do not fit on one). |
hero.png.import | Import settings Godot reads: lossless, no mipmaps, Fix Alpha Border off, so faint pixels keep their colour. |
README-GODOT.md | These steps for this bundle. |
Import it into Godot 4
- Unzip the bundle and copy the whole folder anywhere under
res://, for exampleres://characters/hero/. The.tresfinds the PNG by a relative path, so keep the files together. - Let the FileSystem dock finish importing. Godot uses the shipped
hero.png.import, so the atlas is imported lossless and without mipmaps. - Drag
hero.tscninto your scene, or select your ownAnimatedSprite2Dand loadhero.tresinto its Sprite Frames property. - Choose the animation in the node's Animation property or in the SpriteFrames panel; the shipped scene autoplays the first tag.
- From a script, switch with
$Hero.play("walk"). For a tag that does not loop, connectanimation_finishedand playidleagain. - For your own nodes, set Project Settings › Rendering › Textures › Canvas Textures › Default Texture Filter to Nearest once, or set the node's Texture › Filter to Nearest.
Check the result
- Run the scene:
walkwith six 100 ms frames should repeat every 0.6 s, the same as Aseprite's preview. - Open the SpriteFrames panel: every animation has the frame count of its tag (a ping-pong tag shows the written-out order).
- Zoom the 2D viewport to 400 %: pixel edges stay square. Soft edges mean the node uses Linear filtering.
print($Hero.sprite_frames.get_meta("nerulio"))lists the pivot and boxes of every frame.
Troubleshooting
| Symptom | Likely cause | How to check | Fix |
|---|---|---|---|
| The sprite looks blurry | Your node inherits the project's default Linear filter | Zoom in; check the node's Texture › Filter | Set Filter to Nearest on the node, or the project's Default Texture Filter to Nearest |
| Faint pixels (glow, smoke) change colour | The PNG was imported with Godot's default Fix Alpha Border instead of the shipped .png.import | Import dock of the PNG: Fix Alpha Border is on | Keep hero.png.import next to the PNG, or turn Fix Alpha Border off and click Reimport |
| A tag is missing | The file had no tags (everything became default), or the tag was renamed or removed before export | Look at the tag lanes on Nerulio's timeline before exporting | Add or fix the tag in Aseprite or on the timeline, then export again |
| A play-once tag loops, or a looping tag stops | Repeat ∞ becomes loop on; any repeat count becomes loop off | SpriteFrames panel: the loop toggle of that animation | Change the repeat in the tag, or toggle Loop in Godot; use animation_finished for play-once tags |
| The character jumps when the animation changes | Frames have different pivots, but a node has one offset (the first frame's pivot) | The export notes warn about differing pivots; every pivot is in metadata/nerulio | Use one pivot slice for all frames, or align pivots in Nerulio (see the pivot editor) before export |
| Timing is faster or slower than in Aseprite | The node's Speed Scale or a play() custom speed is not 1 | Inspector: Speed Scale | Set Speed Scale to 1 and call play() without a custom speed |
| Godot 3 cannot open the .tres | The resource uses the Godot 4 format with per-frame durations | — | Use Godot 4, or export a plain PNG sprite sheet and build the frames by hand in Godot 3 |
Other ways to do it
| Option | When it is the better choice |
|---|---|
| Export a PNG sheet from Aseprite (File › Export Sprite Sheet) and use Add Frames from Sprite Sheet in Godot's SpriteFrames panel | One or two animations with even timing: no extra files, but you set each animation's FPS by hand and per-frame durations are lost. See using a sprite sheet in Godot. |
| An .aseprite import plugin from the Godot Asset Library | You change the art often and want Godot to reimport on every save; such plugins call the Aseprite program, so it must be installed on every machine that imports. |
| The same file for another engine | Aseprite to Unity or Aseprite to Phaser. |
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 repeat count above one is not stored: Godot plays such an animation once.
- Layers arrive flattened; to animate parts separately in Godot, export them as separate files.
- Hit and hurt boxes are data in the resource metadata; create the
Area2DandCollisionShape2Dnodes yourself. - 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 bundle was loaded by Godot 4.7.2 in our verification runs: every animation's frame count, speed, loop and relative durations were read back and every frame was drawn at 1× and 4× and compared with the source. The .aseprite reader was checked on 231 real files that reopened in Aseprite 1.3.18 with the same tags, durations and pixels. What the steps above say about Godot follows the Godot 4.7 documentation.
Official documentation used
Questions
Do I need Aseprite installed?
No. The file is parsed in the browser. On a 231-file .aseprite corpus, 222 imported with layers and 9 flattened, each with the reason shown.
Why ship a .png.import?
Godot's default import recolours pixels under alpha 20 (fix_alpha_border). The bundle's import settings turn that off; with them, the FX sheet in the test passed in Godot.
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).