Collision Polygon Generator from Sprite Alpha
Generate a collision polygon from each frame's alpha, capped to a vertex count your engine likes, for a frame, a tag or every frame — then drag points to fine-tune.
A collision polygon traced from a sprite's alpha follows its silhouette more closely than a box. Nerulio traces each frame's outline along pixel edges at an alpha threshold (127 by default, so pixels at least half opaque count), simplifies it until it fits a vertex cap (12 by default, 3–64) without letting the outline cross itself, and keeps up to 8 polygons per frame in frame-canvas pixels. The polygons are exported in the Godot bundle's metadata and the generic JSON; Nerulio does not create CollisionPolygon2D nodes or Unity colliders.

- 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
Tracing, simplifying, and handing the points to an engine
The outline runs along the edges between opaque and transparent pixels, not through pixel centres, so every vertex sits on a whole pixel corner: a 16 × 16 opaque square becomes exactly (0, 0), (16, 0), (16, 16), (0, 16). Outer outlines come back clockwise on screen (y down), and specks smaller than 4 px² are dropped.
Simplification (Ramer–Douglas–Peucker) removes points that lie close to a straight line. The tolerance starts at 1 px and is raised until the polygon has no more vertices than the cap; a tolerance that would make the outline cross itself is rejected, because a self-crossing outline is not a usable physics shape. Raising the tolerance happens in steps, so a cap can be met with fewer vertices than it allows, and a low cap can round away gaps such as the space between two legs.
Engines take such polygons as point lists relative to the object. Godot's CollisionPolygon2D accepts concave polygons and, in its default solids mode, splits them into convex parts itself; it must be the child of an Area2D or a physics body. Unity's PolygonCollider2D takes points in local space, and Unity can also build its own outline from a sprite's Custom Physics Shape. In both, subtract the frame's pivot first; in Unity also flip y and divide by Pixels Per Unit.
- Alpha threshold
- The highest alpha still counted as transparent; 127 keeps pixels that are at least half opaque.
- Vertex cap
- The most points a polygon may have after simplification.
- Tolerance
- How far (in pixels) simplification may move the outline.
Example: one 32 × 48 character frame at three settings
A head, a body and two legs with a 4 px gap between them, plus a faint glow (alpha 60) around the head. Numbers from Nerulio's tracer:
traced outline (alpha > 127) 48 vertices, area 544 px²
cap 64 tolerance 1 px 16 vertices, area 540 px² (−0.7 %), max deviation 1.0 px, legs kept apart
cap 12 tolerance 3.8 px 5 vertices, area 428 px² (−21 %), max deviation 3.8 px, leg gap gone
threshold 40, cap 12 glow counted: area 628 px², 12 vertices at tolerance 2.0 px
Godot, pivot (16, 48) polygon point (22, 48) → node space (22 − 16, 48 − 48) = (6, 0)A cap of 12 did not give 12 points here: the next tolerance step already went down to 5. If the shape matters, raise the cap and compare the vertex count the Studio reports after Auto from alpha.
Where the polygons go
| Target | Polygons in the export | What you build in the engine |
|---|---|---|
| Godot 4 | collision per frame in metadata/nerulio, each polygon as a flat list x0, y0, x1, y1 … | A CollisionPolygon2D under an Area2D or a body, its polygon set from the list minus the pivot, per frame by your script |
| Generic JSON | frames[key].collision: each polygon as [[x, y], …] | Any engine that takes a point list |
| Unity 6 | None; the export notes point to the generic JSON | PolygonCollider2D points from the generic JSON, or Unity's own Custom Physics Shape |
| Aseprite, Phaser, PixiJS, LÖVE, Spine, Starling, Defold, GameMaker, CSS | None | Load the generic JSON next to the atlas |
What it does
- Auto from alpha: outline tracing on the pixel lattice with an alpha threshold.
- A maximum vertex count per polygon (for example 8 for simple physics shapes).
- Runs over this frame, the selection, the tag or every frame in one undo step.
- Polygons are stored per frame in frame-canvas pixels and export with the frame data.
How to do it
- Drop and apply your sprite import.
- Set Max vertices and the alpha threshold in the Frame panel.
- Choose the scope and press Auto from alpha.
- Select points to move them, or clear and redraw; then export.
Check the result
- After Auto from alpha the toast reports how many frames and vertices were made; step through the tag and look at the outline on each frame.
- Toggle the threshold between 127 and a low value on a frame with glow or smoke and compare the outlines.
- In Godot, add a CollisionPolygon2D under an Area2D with the converted points and turn on Debug › Visible Collision Shapes to see it over the sprite.
Troubleshooting
| Symptom | Likely cause | How to check | Fix |
|---|---|---|---|
| The polygon swallows gaps such as the space between the legs | The vertex cap forced a tolerance larger than the gap | The vertex count after Auto is far below the cap, or the outline bridges the gap | Raise the cap (for example to 24) and run Auto again |
| Glow or smoke is inside the collision shape | Its alpha is above the threshold | Lower and raise the threshold and compare | Raise the threshold so faint pixels count as transparent |
| The shape is offset in the engine | Points are frame-canvas pixels from the top-left, not relative to the pivot | Compare one point with the worked example | Subtract the pivot; in Unity also flip y and divide by Pixels Per Unit |
| Only some parts got a polygon | Up to 8 separate shapes per frame are kept, and specks under 4 px² are dropped | The Auto message mentions dropped specks or extra shapes | Join the parts in the art, or draw the missing polygon by hand |
Other ways to do it
| Option | When it is the better choice |
|---|---|
| Unity's Custom Physics Shape in the Sprite Editor | Unity only: Unity generates and edits the outline itself, and a Polygon Collider 2D uses it for new instances of the sprite. |
| A box or circle per frame | Most platformer bodies; cheaper and steadier than a detailed polygon. Draw it with the hitbox editor, or use the convex hull, rectangle and circle options of the classic Sprite Lab. |
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
- Polygons are data in the export: no CollisionPolygon2D nodes or Unity colliders are created.
- Collision polygons do not reach Unity, Aseprite or the atlas formats; use the generic JSON.
- Holes inside a shape are not part of the Studio's polygons.
- 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
The tracer and the numbers in the example come from Nerulio's own module (src/game/contour.js). The Godot bundle that carries the metadata was loaded by Godot 4.7.2; the collision polygons in it were not used as physics shapes in that run. Engine behaviour follows the Godot 4.7 and Unity 6 documentation.
Official documentation used
Classic tool
Convex hull, rectangle and circle fallbacks per frame are still in the classic Sprite Lab.
Open the classic tool →Questions
Will the polygon cover faint pixels like glows?
Only pixels above the alpha threshold count. Raise it to ignore soft glows, lower it to include them.
Is the polygon simplified?
Yes, down to the vertex cap you set, and every point stays inside the frame canvas.
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).