Godot is the engine we use most ourselves, so everything is built around how Godot 4 wants 2D animation: a SpriteFrames resource played by an AnimatedSprite2D. There are three ways in:
- The Godot addon: generate from a dock inside the editor and get a ready
SpriteFramesfile. It also converts any PNG + JSON sheet (ours, Aseprite or TexturePacker). - The
.tresexport from the website, if you’d rather generate in the browser. - A plain PNG sheet, imported by hand or built from code.
This page also covers the project settings that keep pixel art sharp. For a longer walkthrough with 8-direction movement code, read How to import a sprite sheet in Godot 4.
Tested with Godot 4.3 and 4.4. Everything here also applies to 4.2.
Download the addon
aispritemaker-godot.zip · Godot 4.2 or newer · free, MIT licensed · generating needs an API key (every plan includes one); the sheet converter works without a key.
The AI Sprite Maker addon
Install
- Download aispritemaker-godot.zip and unzip it into your project so you get
res://addons/aispritemaker/plugin.cfg. Or, in the editor, open AssetLib → Import…, pick the zip and install. - Open Project → Project Settings → Plugins and enable AI Sprite Maker.
- The AI Sprite Maker dock appears on the right.
Generate from the dock
- Paste your API key (
asm_live_…, from Dashboard → API keys) and press Save. The key is stored in Editor Settings (aispritemaker/api_key), which is per user and outside the project, so it never ends up in git. - Pick a Type: Sprite, Animation (choose a motion from the list, loaded live from the motion library), Turnaround (4 or 8 directions) or Tileset.
- Write a prompt and choose style, size, palette (0 = the style’s default) and view. Optionally pick a Reference image from the project, or press Selected to use the image selected in the FileSystem dock.
- Press Generate. The dock shows progress; Cancel cancels the job and releases its credits.
Results are saved to Save to (default res://sprites/aispritemaker/):
| File | What it is |
|---|---|
fox-knight_walk.png | The sheet: one row per direction, one column per frame |
fox-knight_walk.json | Frame rectangles, durations and one tag per animation (Aseprite-compatible) |
fox-knight_walk.tres | A SpriteFrames with AtlasTexture frames, fps and loop set per animation |
Animations are named <motion>_<direction>, for example walk_down, walk_left, walk_right and walk_up. Two options at the bottom of the dock save setup time:
- Add an AnimatedSprite2D to the open scene drops a configured
AnimatedSprite2D(Nearest filter, autoplay) under the selected node. It’s a normal editor action, so Ctrl+Z undoes it. - Use Nearest filter project-wide sets
rendering/textures/canvas_textures/default_texture_filterto Nearest, which is step 1 below in one click.
Convert sheets you already have
The addon turns any PNG + JSON sheet into SpriteFrames: our exports, Aseprite JSON (array or hash; tags with forward, reverse and pingpong; per-frame durations) and TexturePacker JSON (hash or array with an animations map). The PNG must be the file named in the JSON’s meta.image, next to the JSON.
- In the dock: Sheet JSON → SpriteFrames…, pick the
.json; the.tresis written next to it. - In the FileSystem dock (Godot 4.4+): right-click a
.json→ Create SpriteFrames (AI Sprite Maker). - Headless, for CI or batch jobs:
godot --headless --path . -s res://addons/aispritemaker/tools/convert_sheet.gd -- res://sprites/hero.json res://sprites/slime.json
Per-frame durations become SpriteFrames frame durations relative to the animation speed, so uneven timing from Aseprite survives the conversion.
Use the result
$AnimatedSprite2D.sprite_frames = load("res://sprites/aispritemaker/fox-knight_walk.tres")
$AnimatedSprite2D.play("walk_down")
The rest of this page applies whether you use the addon or not.
1. Set up the project for pixel art (once)
Do this before importing anything, or Godot will smooth your sprites into mush.
- Project → Project Settings → Rendering → Textures → Canvas Textures → Default Texture Filter →
Nearest. - Display → Window → Stretch: set Mode to
viewportfor a fixed low-res look (orcanvas_itemsif you mix pixel art with smooth UI), Aspect tokeep, and Scale Mode tointeger(Godot 4.2+) so the game only scales by whole numbers. - Set the base Viewport Width/Height to your game’s native resolution, e.g. 480 × 270 or 320 × 180, and use Window Width/Height Override for the size of the desktop window.
- Optional, for movement without shimmering: Rendering → 2D → Snap → Snap 2D Transforms to Pixel on.
If you only want nearest filtering on some nodes, leave the project default alone and set CanvasItem → Texture → Filter to Nearest on the node instead. Children inherit it.
2. Or export a .tres from the website
Open the job in the generator or your dashboard and choose Export → Godot 4 (SpriteFrames .tres). You get a ZIP with two files:
fox_knight_walk_godot.zip
├── fox_knight_walk.png # the sheet, one row per direction
└── fox_knight_walk.tres # SpriteFrames: one animation per row
Inside the .tres there is one animation per direction, named after the job and the direction, for example fox_knight_walk_down, fox_knight_walk_left, fox_knight_walk_right and fox_knight_walk_up. Check the exact names in the SpriteFrames panel or with sprite_frames.get_animation_names(). Each animation uses the job’s frame rate as its speed and loops if the motion loops (attacks and deaths don’t). Every frame is an AtlasTexture that points at a region of the PNG, so there is only one texture in memory.
Keep the PNG next to the .tres, or update the path
The .tres refers to the sheet as res://<name>.png. The simplest setup is to unzip both files into your project root, open the .tres once so Godot imports it, then move both together inside the Godot FileSystem dock (drag and drop). Godot rewrites the path for you. If you move them in your OS file manager instead, Godot shows a “Fix Dependencies” dialog the next time you open the resource; point it at the PNG.
3. Use it with AnimatedSprite2D
- Add an AnimatedSprite2D node to your character scene.
- In the Inspector, drag the
.tresfile onto Sprite Frames. - Pick an animation in the Animation dropdown and tick Autoplay on Load if you want it to start immediately.
- Set Offset so the feet sit on the node origin. Our frames put the character’s feet on the bottom edge, so with a 64 px frame use
Offset = (0, -32)and Centered on. That waypositionis where the character stands, which makes Y-sorting and collisions line up.
Play animations from code:
extends CharacterBody2D
const SPEED := 90.0
@onready var anim: AnimatedSprite2D = $AnimatedSprite2D
var facing := "down"
func _physics_process(_delta: float) -> void:
var input := Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down")
velocity = input * SPEED
move_and_slide()
if input != Vector2.ZERO:
# pick the dominant axis for 4-direction art
if absf(input.x) > absf(input.y):
facing = "right" if input.x > 0.0 else "left"
else:
facing = "down" if input.y > 0.0 else "up"
anim.play("fox_knight_walk_" + facing)
else:
anim.play("fox_knight_idle_" + facing)
play() does nothing if that animation is already playing, so calling it every frame is fine. For one-shot animations like an attack, wait for animation_finished:
func attack() -> void:
anim.play("fox_knight_attack-slash_" + facing)
await anim.animation_finished
anim.play("fox_knight_idle_" + facing)
Combining several motions in one SpriteFrames
Each export is one motion. To keep idle, walk and attack in one resource, open the first .tres, then in the SpriteFrames panel use Add Frames from Sprite Sheet (the grid icon) to add the other sheets as new animations. Or merge them in code at load time with SpriteFrames.add_animation() and add_frame() as shown below.
4. Import any PNG sheet manually
If you exported a plain PNG (or the sheet came from somewhere else), build the SpriteFrames in the editor:
- Add an AnimatedSprite2D, then in Sprite Frames choose New SpriteFrames and click it to open the SpriteFrames panel at the bottom.
- Rename
defaultto your animation name, e.g.walk_down. - Click Add Frames from Sprite Sheet (the grid icon), pick the PNG, and set Horizontal to the number of columns and Vertical to the number of rows. Both values are in the job’s JSON metadata (
columns,rows) and in the export dialog. - Click the frames of the first row in order, then Add Frame(s). Repeat for each row as its own animation.
- Set the FPS box to the job’s
fpsand toggle Loop for looping motions.
5. Build SpriteFrames from code
This is handy for tools, mods or loading sheets at runtime. It reads the frame layout from the JSON metadata we export with every sheet:
func frames_from_sheet(sheet: Texture2D, frame_w: int, frame_h: int,
columns: int, directions: PackedStringArray, prefix: String,
fps: float, loop := true) -> SpriteFrames:
var sf := SpriteFrames.new()
sf.remove_animation("default")
for row in directions.size():
var anim_name := "%s_%s" % [prefix, directions[row]]
sf.add_animation(anim_name)
sf.set_animation_speed(anim_name, fps)
sf.set_animation_loop(anim_name, loop)
for col in columns:
var tex := AtlasTexture.new()
tex.atlas = sheet
tex.region = Rect2(col * frame_w, row * frame_h, frame_w, frame_h)
sf.add_frame(anim_name, tex)
return sf
Then use it:
var sheet: Texture2D = load("res://sprites/fox_knight_walk.png")
$AnimatedSprite2D.sprite_frames = frames_from_sheet(
sheet, 64, 64, 8, PackedStringArray(["down", "left", "right", "up"]),
"walk", 10.0)
$AnimatedSprite2D.play("walk_down")
If you want to keep the result, save it with ResourceSaver.save(sf, "res://sprites/fox_knight.tres").
6. Sprite2D + AnimationPlayer instead
If you already drive everything with an AnimationPlayer (for hitboxes, sounds and particles on exact frames), skip SpriteFrames. Use a Sprite2D with Animation → Hframes = columns and Vframes = rows, then key the Frame property in the AnimationPlayer. Frame numbers count left to right, top to bottom, so row r, column c is r * columns + c. Set the track’s update mode to Discrete.
Troubleshooting
| Problem | Cause and fix |
|---|---|
| Sprites look blurry | Texture filter is Linear. Set the project default to Nearest (step 1) or the node’s Texture → Filter to Nearest |
| Sprites shimmer or wobble when the camera moves | Non-integer scaling or sub-pixel positions. Use Scale Mode integer and turn on Snap 2D Transforms to Pixel |
| Thin lines from the neighbouring frame at the edge | Filtering or a non-integer scale samples outside the region. Fix the filter and scale first. If you pack sheets yourself, add 1–2 px padding |
| “Fix Dependencies” dialog when opening the .tres | The PNG isn’t at the path the .tres expects. Point the dialog at the PNG, or move files inside the FileSystem dock |
| Animation plays too fast or slow | The SpriteFrames speed is in frames per second. Match the job’s fps, or change Speed Scale on the node |
| Character slides when changing direction | Offsets differ between animations. Use the same frame size and the same Offset for all of a character’s sheets |
Other ways to generate for Godot
- The CLI writes the same PNG, JSON and
.tresfiles from your terminal (--format png,json,godot), with correctres://paths when run inside a Godot project. - With the MCP server, a coding agent such as Claude Code can generate a sheet and wire it into your scenes.