Skip to content

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.

Drop a sprite, frames, a GIF or an .aseprite file to paint on — or an upscaled picture to clean up
Runs in your browser. Files are not uploaded.
Studio Pixel workspace: a CC0 ninja sprite after Canvas size +1, a yellow 1 px outline and a dark blue drop shadow
The real Studio with a CC0 asset (OpenGameArt).
Exports to
  • .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 Aseprite

Run 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

  1. Drop the sprite or its frames on this page; the Pixel workspace opens on the first frame.
  2. Press Ctrl+Alt+C and give the canvas one extra pixel on each side if the art touches the border.
  3. 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.
  4. 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.

StepNerulio PixelAseprite 1.3.18
Make roomCanvas size +1 (one command)Canvas size +1
1 px outlinePixel › Outline: 1846 px addedEdit › FX › Outline: 1846 px added, identical
Drop shadowPixel › Drop shadow: 705 px, identical to the manual resultNo command: duplicate, fill, offset 1,1, move below, flatten (about 6 steps)
UndoEach command is one History stepEach action is its own undo step

Troubleshooting

SymptomLikely causeHow to checkFix
The outline is missing on one sideThe art touches the canvas edge, so there is no pixel outside it to paintOpaque pixels in the first or last row or columnUndo, run Pixel › Canvas size… with +1 on each side, then outline again
Holes inside the sprite got outlinedEnclosed transparent pixels count as "outside"Zoom into gaps between limbs and bodyFill the gap, or select the sprite without the gap and run the outline on the selection
Only part of the character got an outlineThe command works on the current layer, and the silhouette is split across layersToggle layer visibility: the outline follows one layer's shapeMerge down (Ctrl+E) first, or add the outline on a layer that holds the whole silhouette
A glow or smoke got a hard dark borderEvery pixel with alpha above 0 counts as solidPick a glow pixel: its alpha is between 1 and 254Select only the body before outlining, or add the glow after the outline
The outline is two pixels thickThe command ran twice; each run adds another ringHistory shows two Outline entriesUndo once: each run is exactly one History step
The other frames have no outlineThe command changes only the current frameStep 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

OptionWhen 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 handSelective 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 panelYou 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.

TargetFilesCheck
.asepritelayers · blend · opacity · locked · indexed palette · frames · tagsVerified 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 · .jsonMeasured 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 bundlesGodot 4 · Unity 6 · Phaser 3/4 · PixiJS 8 … through Pack & ExportVerified 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 · .nerulioPNG-8 with the palette when an indexed frame fits it · project filePlain 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).