Sprite Slicer Open the tool →

How to Import Sprite Animations into Godot 4

An original shield-grunt enemy sprite for a 2D defense game, ready for Godot 4.
Frames like this drop into Godot 4 as an AnimatedSprite2D with a few clicks.

The first time I imported a sprite sheet into Godot 4, I spent twenty minutes convinced the engine had eaten my animation. The frames were there, the FPS was set, the loop was on, and the thing still wouldn't move. Turned out I'd added the frames to an animation I never told the node to play. Easy mistake, and one of about four that catch everybody once. So this is the walkthrough I wish I'd had: two import routes, the exact menu names Godot 4 actually uses, and the handful of settings that make pixel art stay crisp instead of going to mush.

Before any of this, you need a clean sheet. A grid where every cell is the same size and the character sits in a consistent spot makes the rest trivial; a sheet with uneven cells will fight you at every step. If your frames came out of a video or an AI generator and the spacing is a little off, run them through the Sprite Slicer first to cut and align them, or pull frames straight from a clip with the Frame Extractor. There's a longer write-up on slicing a sprite sheet if your grid has gaps or odd offsets, and one on normalizing frame sizes if your cells aren't uniform yet. Get that sorted and Godot stops being the hard part.

Which route do I want?

Godot 4 gives you two sane ways to do this, and they're good at different things.

  • AnimatedSprite2D — the right call for character animations: walks, attacks, idles, anything with several named states. It holds a whole library of animations in one node and you switch between them by name.
  • Sprite2D with Hframes/Vframes — the lightweight option for a single looping animation on a plain grid sheet, like a spinning coin or a flickering torch. You animate one integer property and that's it.

If you're not sure, use AnimatedSprite2D. It scales to a real character without you rebuilding anything. I only reach for the Sprite2D route when I want one dead-simple loop and don't feel like creating a SpriteFrames resource for it.

Route A — AnimatedSprite2D (the one you'll use most)

  1. Add an AnimatedSprite2D node to your scene (the Add Child Node dialog, then search for it).
  2. With the node selected, look at the Inspector on the right. Click its Sprite Frames property and choose New SpriteFrames. That creates the resource that will hold your frames.
  3. Click that new SpriteFrames resource once more to open the SpriteFrames editor in the bottom panel. This is the workbench where everything else happens.
  4. In that editor, find the Add frames from a Sprite Sheet button — it's the little grid/sheet icon, sitting next to the plain "add image" one. Click it and pick your PNG.
  5. A split dialog opens. Set the Horizontal and Vertical frame counts to match your grid. If your sheet is 8 columns by 1 row, that's Horizontal 8, Vertical 1. Get this wrong and you'll slice frames into quarters — it's the single most common cause of a "why does my sprite look chopped" question.
  6. The cells highlight as you set the counts. You can select only the cells you want (click, or click-drag a range) if the sheet packs several actions together. Then confirm to add them to the animation.
  7. Back in the editor, rename the animation from default to something meaningful like walk or idle. Set its FPS — around 12 is a good starting point for pixel art; anything from 8 to 15 reads fine depending on the action. Toggle Loop on for anything that cycles (a walk loops; a one-shot attack usually doesn't).
  8. To make it actually play, either flip on Autoplay on Load (the little "A" button in the editor toolbar) for a quick test, or drive it from code with $AnimatedSprite2D.play("walk"). In a script that's usually animated_sprite.play("walk") after you've grabbed the node reference.

That last step is the one that got me on day one. Adding frames to an animation does nothing on its own — something has to call play() or you have to tick Autoplay. The frames sit there politely, waiting to be told.

Adding more animations to the same node

One AnimatedSprite2D can hold all your states. Use the New Animation button (the page-with-a-plus icon) in the SpriteFrames editor to create attack, hurt, death, and so on, then add frames to each the same way. In your script you swap states with play("attack") and back to play("idle") when it finishes. Keeping every action in one resource beats juggling a separate node per animation — I learned that the slow way.

Route B — Sprite2D with Hframes/Vframes

For a simple grid sheet where you just want one loop, skip SpriteFrames entirely.

  1. Add a Sprite2D node and drag your PNG into its Texture slot in the Inspector.
  2. Set Hframes and Vframes to your grid dimensions. A 6-frame horizontal strip is Hframes 6, Vframes 1. Godot now treats the texture as a grid and exposes a single integer frame property.
  3. Animate that frame property. Either add an AnimationPlayer node and keyframe frame from 0 up through your last index (set the track to a discrete/nearest interpolation so it snaps between whole frames rather than tweening), or do it from code in _process by stepping the integer on a timer.

It's barely any setup, which is the whole appeal. The tradeoff is there's no named-animation system — one sheet, one sequence. The moment you need multiple states, move to Route A rather than stacking Sprite2D hacks.

Making it pixel-perfect

By default Godot applies a linear filter to textures, which is great for photos and terrible for pixel art — it smears your hard edges into a blur. You fix this in one of two places.

  • Per texture: select the PNG in the FileSystem dock, open the Import dock, set the Filter to Nearest, turn off Mipmaps, and click Reimport. Do this and that one sprite stays crisp.
  • Project-wide default: go to Project Settings → Rendering → Textures → Canvas Textures and set Default Texture Filter to Nearest. Now every 2D texture imports sharp by default, which is what you want for a pixel-art game. I set this on a new project before I import a single asset.

Either works. The project default saves you from forgetting on the fortieth sprite, which you will.

The gotchas that actually bite

  • Blurry sprite? The filter isn't set to Nearest. Check the Import dock for that texture, or the project default. Nine times out of ten that's the whole problem.
  • Frames look chopped or you've got too many/too few? Your Hframes/Vframes (or the Horizontal/Vertical counts in the sheet dialog) don't match the real grid. Recount the columns and rows on the sheet and re-enter them.
  • Character drifts or floats as it animates? Set the offset/pivot so the feet stay planted. For a grounded unit I anchor the pivot at the bottom-center of the cell — inconsistent framing between frames is what causes the foot-slide. If your frames themselves are misaligned, that's a slicing problem, not a Godot one; fix it upstream.
  • Animation won't play? Route A: nobody called play() and Autoplay is off. Route B: the AnimationPlayer track is interpolating instead of snapping, or it's not set to loop.

Coming from another engine?

The grid-and-frames idea carries across, only the menus differ. If you're porting a project or your team is on something else, I've got parallel walkthroughs for importing into Unity and importing into GameMaker. The clean-sheet prep is identical for all three — get the grid uniform once and it imports everywhere.

FAQ

Q. AnimatedSprite2D or AnimationPlayer for character animation?

AnimatedSprite2D for the frame-by-frame sprite playback itself — it holds the SpriteFrames and plays named animations. You'd add an AnimationPlayer on top only if you also need to keyframe other properties (position, modulate, spawning hitboxes) in sync. For plain sprite cycles, AnimatedSprite2D alone is enough.

Q. My imported sprite is blurry. What did I do wrong?

Nothing structural — the texture filter is set to linear. Select the PNG, open the Import dock, set Filter to Nearest, disable Mipmaps, and Reimport. Or set the project default at Project Settings → Rendering → Textures → Canvas Textures → Default Texture Filter = Nearest so it never happens again.

Q. The frames came in sliced wrong. How do I fix the count?

Your Horizontal/Vertical frame counts (Route A) or Hframes/Vframes (Route B) don't match the sheet's real grid. Count the actual columns and rows in your PNG and re-enter those numbers. If the sheet's cells aren't uniform to begin with, re-cut it first with the Sprite Slicer.

Q. What FPS should I set for the animation?

Around 12 is a solid default for pixel art. Slower idles read fine at 8; snappy attacks can go to 15 or so. There's no universal number — set it, play it, and trust your eyes. The FPS lives per-animation in the SpriteFrames editor.

Q. How do I play a specific animation from code?

Grab the node and call play() with the animation name, for example animated_sprite.play("walk") or $AnimatedSprite2D.play("attack"). Adding frames in the editor does not start playback on its own — either call play() or enable Autoplay on Load.

Q. My character slides or floats while animating. Why?

The pivot or per-frame framing is inconsistent. Set the AnimatedSprite2D's offset so the pivot sits at the feet (bottom-center) and make sure every frame is positioned the same way on the sheet. If the frames themselves drift, fix the alignment upstream when you slice — Godot is just showing you what's in the cells.

Open Sprite Slicer — cut & align your frames →