Pixel Art Outline and Drop Shadow — 1 px, One Step
A clean 1 px outline around a sprite, and a 1 px drop shadow under it, as single commands on the canvas. On the head-to-head sprite the result was identical to Aseprite's outline, pixel for pixel, and the shadow that Aseprite 1.3.18 needs about six manual steps for is one command here.
A pixel-art outline is a 1 px ring of a dark colour around the silhouette. An outer outline paints the transparent pixels that touch the sprite, so the sprite grows by 1 px on every side and the canvas needs 1 px of room on each side first; an inner outline repaints the sprite's own edge pixels and keeps its size. In Nerulio, Pixel › Canvas size… (Ctrl+Alt+C) offers +1 on every side by default, and Pixel › Outline then draws an outer, edge-touching outline in the foreground colour on the current layer and frame; Pixel › Drop shadow adds a 1 px shadow in the background colour.

- .asepriteVerified· Aseprite 1.3.18
- Palette filesMeasured· read back
- Engine bundlesVerified· Godot 4.7.2 · Unity 6000.5
- Frame PNG · .nerulioPlain files
Outer or inner, edges or corners, and the extra pixel of canvas
An outer outline keeps every drawn pixel and adds a ring outside it. It is the usual choice for characters that must stand out from busy backgrounds, but it makes the sprite 2 px wider and taller, so a 32×32 character needs a 34×34 canvas or has to be drawn inside 30×30. An inner outline paints over the sprite's outermost pixels instead: the size stays, but the thinnest details (a 1 px sword, a strand of hair) disappear into the line.
Which neighbours count decides the corners. A 4-connected outline adds a pixel only where it touches the sprite along an edge, so diagonal corners stay open and the outline looks rounder; an 8-connected one also fills the diagonal corners and looks blockier. Nerulio's command draws the 4-connected shape, the same shape as Aseprite's default outline on the head-to-head sprite.
The outline is computed from whatever is not transparent on the layer. Enclosed holes, like the gap between an arm and the body, are transparent too and get outlined from the inside; semi-transparent glow counts as solid. Put the full silhouette on one layer, or select the area to outline: with a selection, the command only touches the selected pixels grown by one pixel.
- Outer outline
- Ring of new pixels on the transparent side of the silhouette; the sprite grows 1 px per side.
- Inner outline
- The sprite's own edge pixels recoloured; the size stays.
- 4-connected
- Neighbours that share an edge (up, down, left, right); corners stay open.
- 8-connected
- Edge and diagonal neighbours; corners are filled.
- Canvas size
- Adds or removes pixels at the borders without scaling the art.
Example: counting outline pixels
A 4×4 filled square, computed with the same routines the command uses:
square 4×4 on an 8×8 canvas 16 px of art
outer, 4-connected (Nerulio's command) +16 px (4 per side, corners open)
outer, 8-connected +20 px (the 4 corners filled too)
inner 12 px recoloured, size unchanged
square touching the left edge outer +12 px (one side has no room)
after Canvas size +1 on the left outer +16 px (complete again)
drop shadow (1 px right and down) +7 px on the square, +9 px after the outline
real sprite, 96×144 → 98×146 canvas outline 1846 px, shadow 705 px, same as AsepriteRun the outline before the shadow: the shadow is then cast by the outlined silhouette, which is how the head-to-head sprite was done in both programs.
What it does
- Pixel › Outline draws a 1 px ring outside the opaque pixels in the foreground colour, touching them by their edges (4-connected), the same shape as Aseprite's default outline. Originals stay unchanged.
- Pixel › Drop shadow places the silhouette one pixel down and to the right in the background colour, behind the art. Aseprite 1.3.18 has no shadow command; there it takes duplicating a layer, filling, offsetting, reordering and flattening.
- With a selection active, both commands act only on the selected pixels grown by one pixel, so an outline can go around one character on a crowded sheet or one part of a sprite.
- Canvas size (Ctrl+Alt+C) adds the border room first, so an outline around art that touches the edge is not cut off. Each command is one step in History, undone as a whole.
- For many frames at once, the Cleanup panel can add the outline and the shadow while it converts an upscaled picture, and the result becomes a new sprite with its frames, durations and tags.
How to do it
- Drop the sprite or its frames on this page; the Pixel workspace opens on the first frame.
- Press Ctrl+Alt+C and give the canvas one extra pixel on each side if the art touches the border.
- Set the foreground colour to the outline colour and run Pixel › Outline; set the background colour to the shadow colour and run Pixel › Drop shadow.
- Step through the frames with , and . to repeat it where needed, then save .aseprite or export the frames from Pack & Export.
Check the outline
- Before running it, look at the outermost row and column: if any pixel of the art is there, add canvas first.
- Zoom to 400 % and follow the silhouette: every edge pixel should have exactly one outline pixel outside it, and diagonal corners stay open.
- Hide the outlined layer's neighbours: if the arm-to-body gap got an outline you did not want, erase those pixels or fill the gap first.
- Step through the frames with , and .: the command acts on the current frame, so each frame that needs the outline shows it.
The same outline and shadow task, measured
Sumo Hulk sheet by Eris (OpenGameArt, CC0), 96×144. docs/STUDIO-PIXEL.md §6, T8.
| Step | Nerulio Pixel | Aseprite 1.3.18 |
|---|---|---|
| Make room | Canvas size +1 (one command) | Canvas size +1 |
| 1 px outline | Pixel › Outline: 1846 px added | Edit › FX › Outline: 1846 px added, identical |
| Drop shadow | Pixel › Drop shadow: 705 px, identical to the manual result | No command: duplicate, fill, offset 1,1, move below, flatten (about 6 steps) |
| Undo | Each command is one History step | Each action is its own undo step |
Troubleshooting
| Symptom | Likely cause | How to check | Fix |
|---|---|---|---|
| The outline is missing on one side | The art touches the canvas edge, so there is no pixel outside it to paint | Opaque pixels in the first or last row or column | Undo, run Pixel › Canvas size… with +1 on each side, then outline again |
| Holes inside the sprite got outlined | Enclosed transparent pixels count as "outside" | Zoom into gaps between limbs and body | Fill the gap, or select the sprite without the gap and run the outline on the selection |
| Only part of the character got an outline | The command works on the current layer, and the silhouette is split across layers | Toggle layer visibility: the outline follows one layer's shape | Merge down (Ctrl+E) first, or add the outline on a layer that holds the whole silhouette |
| A glow or smoke got a hard dark border | Every pixel with alpha above 0 counts as solid | Pick a glow pixel: its alpha is between 1 and 254 | Select only the body before outlining, or add the glow after the outline |
| The outline is two pixels thick | The command ran twice; each run adds another ring | History shows two Outline entries | Undo once: each run is exactly one History step |
| The other frames have no outline | The command changes only the current frame | Step to the next frame with . | Repeat on each frame, or use the Cleanup panel's outline option with the scope set to the tag or all frames |
Other ways to do it
| Option | When it is the better choice |
|---|---|
| Aseprite: Edit › FX › Outline (Shift+O) | You want an inner outline, a custom shape of neighbours, or the outline applied to all cels of the animation at once; its documentation describes Inside/Outside, shape presets and Selected/All. |
| Drawing the outline by hand | Selective outlining, where the line is lighter on the lit side or dropped where the sprite meets the ground, is an artistic choice no command makes for you. |
| Nerulio's Cleanup panel | You are converting an upscaled image anyway: the outline and shadow options apply to the whole scope and give a new sprite. See the downscaler. |
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 |
|---|---|---|
| .aseprite | layers · blend · opacity · locked · indexed palette · frames · tags | Verified Aseprite 1.3.18The real Aseprite command line reopened the file as INDEXED with 2 layers, Multiply at opacity 160 and the locked layer not editable; the frames are pixel-identical. |
| Palette files | .gpl · .pal · .hex · .ase · .act · .json | Measured read backEach format was written and read back identical; Aseprite's own .gpl, .pal, .hex and .act and a real Adobe .ase (16 colours) were read. |
| Engine bundles | Godot 4 · Unity 6 · Phaser 3/4 · PixiJS 8 … through Pack & Export | Verified Godot 4.7.2 · Unity 6000.5The sprite you drew is packed in Pack & Export; those engine files were loaded in each engine on real CC0 assets (47 of 49 runs pass; the 2 failures are a Phaser 3.90 bug the Phaser 3 preset avoids). |
| Frame PNG · .nerulio | PNG-8 with the palette when an indexed frame fits it · project file | Plain filesPlain files for any tool; no engine run is claimed for them. |
Evidence
Cleanup benchmark, 69 CC0 cases with a known 1× original: the exact size found for 81 % (±1 px: 90 %) and 74.0 % of pixels exact with the background kept; the best competitor found 30 % of sizes (perfectPixel) and the best exact-pixel score was 44.6 % (unfake.js). An Aseprite-made ×4 nearest upscale came back 100 % exact and a ×3.78 bilinear resize 99.7 %, both with the size found automatically. A 1 px outline and a drop shadow matched Aseprite pixel for pixel (1846 and 705 px). .aseprite files written here reopen in Aseprite 1.3.18 as indexed, with their layers, blend mode and lock.
Limits
- Only the outer, 4-connected outline is available as a command; inner and 8-connected outlines are not.
- The command works on the current layer and frame; whole animations need Cleanup or one run per frame.
- The shadow offset is fixed at 1 px right and 1 px down.
- Not in the Pixel workspace (Aseprite has them): layer groups, linked cels, tilemap layers, reference layers, RotSprite and free rotation or scaling of a selection (only flips and 90°), gradient, spray, contour and text tools, custom brushes (square or round, 1–16 px), tiled drawing, Lua scripting and a command line.
- The symmetry axis is typed, not dragged on the canvas. On touch there is no right button, so the background colour needs the swap button or X.
- Cleanup: JPEG input comes back close (84 % of pixels within ΔE 0.02 with the background kept) but rarely exact (12 %); simulated generated art gets the exact size 20 % of the time (±1 px: 80 %); none of 7 real generated images was snapped automatically — you type the pixel size the panel suggests.
- Indexed conversion picks the nearest colour in Oklab, not Aseprite's RGB distance: 68 % of the test sprite's pixels got a different (perceptually closer) pick. The Lospec search is the only network request and asks first.
Versions and sources
Nerulio: on the 96×144 CC0 head-to-head sprite, Canvas size +1, Outline and Drop shadow added 1846 and 705 px, pixel-identical to the result made in Aseprite 1.3.18 (docs/STUDIO-PIXEL.md §6, T8). The square counts above come from src/studio/pixel/raster.js. Aseprite's options follow its documentation.
Official documentation used
Questions
Is the outline drawn inside or outside the sprite?
Outside: it only fills transparent pixels next to opaque ones, so the art itself is not overwritten. In the head-to-head it added 1846 pixels, exactly the 4-connected outline Aseprite's Edit › FX › Outline produced on the same sprite.
Can I outline all frames of an animation at once?
The command works on the current frame. For a whole animation, run it on each frame, or use the Cleanup panel's outline and drop-shadow options, which apply to the chosen scope (this frame, the tag or all frames) and make a new sprite.
Does it work in an indexed sprite?
Yes. The outline and shadow use the foreground and background colours from the palette, and the cels are stored as indexed PNG again, so the sprite stays indexed.
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).