Skip to content

Sprite Animation Preview — Frames, Tags and Timing

Check an animation before it reaches the engine: play each tag with its real per-frame timing, step frame by frame, compare neighbours with onion skin and watch it at 1×–8× in a floating preview.

Previewing a sprite animation honestly needs the frames in order, each frame's own duration in milliseconds, and the tag's direction and repeat count; one FPS slider hides the poses an animator holds longer. Drop a sheet, frame files, a GIF or an .aseprite file, apply the import, pick a tag and press Enter: it plays with per-frame timing and loops inside the tag, steps with , and . , compares neighbours with onion skin (F3) and plays at 1×–8× in a floating window (F7). The same timing exports as GIF, APNG or engine data.

Drop a sprite sheet, numbered frames, a GIF / APNG or an .aseprite file
Runs in your browser. Files are not uploaded.
Studio Sprite workspace: one frame with its pivot, a hit box and a hurt box, and the timeline with animation tags
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

Milliseconds, direction and repeat

A frame's duration is how long it stays on screen. FPS is a shorthand for equal durations: 1000 ÷ FPS milliseconds per frame, so 12 FPS is 83.3 ms and 8 FPS is 125 ms. Animation holds key poses longer (a 250 ms hit pose between 100 ms steps), and a preview that forces one rate plays those poses wrong.

A tag plays forward, reverse or ping-pong. Ping-pong does not show the end frames twice: four frames play 1-2-3-4-3-2 and start again. The repeat count follows Aseprite: 0 loops forever, n plays n times, and for ping-pong one repeat is one pass in one direction, so 2 is there and back.

Onion skin draws the frames before and after the current one at lower opacity, previous frames tinted red and next ones blue if you want, which shows uneven spacing and wobble that full-speed playback hides. The floating preview plays the loop at whole-number zoom on a background you choose, so pixels stay square.

Duration
Milliseconds a frame stays on screen; 100 ms when a file carries no timing.
Ping-pong
Forward then back without repeating the end frames.
Repeat
0 = loop forever; n = play n times.
Onion skin
Neighbouring frames drawn faintly behind the current one.

Example: an attack that holds its hit pose

Two tags on one timeline:

attack   frames 1–5   forward    repeat 1
ms       100, 100, 100, 250, 150
one play 100 + 100 + 100 + 250 + 150 = 700 ms, then it stops on frame 5
at 10 FPS instead: 5 × 100 = 500 ms, and the hit pose flashes for 100 ms
GIF      delays 10, 10, 10, 25, 15 (hundredths of a second)

idle     frames 6–9   ping-pong  repeat 0
order    6 7 8 9 8 7 | 6 7 8 9 8 7 | …

The same milliseconds go to every export: Godot relative durations are computed from them, and GIF export rounds them to hundredths of a second.

What it does

  • Per-frame durations in milliseconds (Aseprite default 100 ms); set them one by one or for a selection.
  • Tags with forward, reverse or ping-pong direction and a repeat count (0 = loop forever).
  • Onion skin with opacity and red/blue tint; a floating preview at integer zoom on a chosen background.
  • Export the animation as GIF (adaptive palette, per-frame delays) or exact APNG, or as engine animation data.

How to do it

  1. Drop a sheet, frames, a GIF or an .aseprite file and apply the import.
  2. Select a tag on the timeline and press Enter to play; , and . step frames.
  3. Adjust durations and direction until it reads right; F3 toggles onion skin, F7 the preview.
  4. Export from Pack & Export.

Check the result

  • Watch the floating preview (F7) at the zoom and on the background colour your game uses.
  • Step with , and . across the loop point: going from the last frame to the first should look like any other step.
  • Hover a frame on the timeline: its tooltip shows its duration in milliseconds.
  • Export a GIF and open it in a browser; the export notes list any delay that had to be rounded.

Troubleshooting

SymptomLikely causeHow to checkFix
The GIF plays faster or slower than the previewGIF stores hundredths of a second: 125 ms becomes 13 (130 ms), and delays under 20 ms are raised to 20 ms, because browsers play 0 or 1 as 10The export notes count the rounded framesUse durations in steps of 10 ms, or export APNG, which keeps milliseconds
Soft edges turn jagged in the GIFGIF transparency is on or off: alpha of 128 and above becomes opaque, below becomes transparentThe export note says semi-transparent pixels changedExport APNG, which keeps every alpha level
Every frame of an imported sheet lasts 100 msA plain PNG, TexturePacker JSON and Sparrow XML carry no timing, so the default appliesThe timeline shows 100 on every frameSelect the frames and type the duration once for all of them
The loop hitches at the seamThe last frame repeats the first, so that pose plays twiceStep from the last frame to the first with onion skinDelete the duplicate frame, or switch the tag to ping-pong

Other ways to do it

OptionWhen it is the better choice
Aseprite's own previewYou are still drawing; its preview and tags are the source of the timing anyway.
The engine's player (Godot's SpriteFrames panel, Unity's Animation window)The final check: what the engine does with speed scale, state changes and physics can only be seen there.
Sprite sheet to GIFYou only need a shareable GIF of each animation.

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

  • This is a player: pixels are edited in the Pixel workspace.
  • Engines that play one FPS per animation (Defold, GameMaker) cannot follow different durations inside one tag; the export notes say so.
  • 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

GIF decoding matched Pillow on 408 of 408 frames of 29 real GIFs, APNG on 132 of 132 frames of 16 files (every dispose and blend mode). GIF delay units and disposal follow the GIF89a specification.

Official documentation used

Questions

Does a GIF keep its timing?

Yes. Every GIF frame's delay becomes that frame's duration; the decoder matched Pillow on 408 of 408 frames of 29 real GIFs.

Can I preview without cutting a sheet first?

A sheet needs its frames first: the import shows the proposed cut, and one click applies it.

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