Skip to content

Button State Generator — Hover, Pressed, Disabled, Focus

Five states from one button, each an ordered list of operations you can see and edit — brightness, contrast, saturation, overlay, offset, outline, alpha. It is arithmetic on pixels: nothing is drawn that was not in the source.

Drop a UI panel, button, glyph sheet or UI sheet (PNG)
Open the UI Lab
Runs in your browser. Files are not uploaded.
UI Lab: a CC0 UI panel with nine-slice guides and previews at several sizes
The real UI Lab with a CC0 asset (Kenney).
Outputs
  • Nine-sliceMeasured· Pillow
  • Bitmap fontMeasured· independent .fnt parser
  • Button statesMeasured· Pillow
  • Engine importUNVERIFIED

What it does

  • Defaults: hover +10 % brightness, pressed −10 % and 1 px down, disabled desaturated at 50 % alpha, focus a 2 px ring.
  • Every operation value is visible and editable per state.
  • Output: one PNG per state, a packed strip and states.json with the rects and the exact operation values.
  • Transparent pixels are never recoloured.

How to do it

  1. Drop the button PNG.
  2. Adjust each state's operations.
  3. Check the five previews side by side.
  4. Export the ZIP.

Outputs and how each was checked

"Measured" means the downloaded file was opened again outside the page (Pillow, numpy or an independent parser) and the property was measured. Loading these files in a game engine was not tested.

TargetFilesCheck
Nine-slicenine-slice.json · previews · SETUP.mdMeasured PillowEvery resized preview keeps all four corner blocks unscaled: the same bytes in Chromium; in Firefox semi-transparent pixels can shift by one alpha-rounding step. 0 gaps, 0 overlaps.
Bitmap fontfont.png · font.fnt (BMFont text) · font.jsonMeasured independent .fnt parserThe .fnt was read back by a parser written separately in the test: header and every glyph rect match.
Button statesstates/*.png · strip · states.jsonMeasured PillowNormal equals the source (byte for byte in Chromium; in Firefox semi-transparent colour within one alpha-rounding step); every rect in states.json cuts exactly its state out of the strip.
Engine importGodot NinePatchRect · Unity Sprite border · BMFont importUNVERIFIEDNot run in Godot or Unity.

Evidence

Nine-slice corners unscaled at 100×40, 300×80, 800×200 and 420×120; border suggestion 6·6·6·6 from a run of 12 identical columns; button states: normal equal to the source, disabled grey at alpha 128, focus ring exact; .fnt glyph records equal the JSON; missing glyphs from a .po listed exactly; black on white measured 21:1. Images pass through a browser canvas: Chromium returns the same bytes; Firefox rounds the colour of semi-transparent pixels to one premultiplied step (measured on a Kenney button: up to 4 levels at alpha 63), while alpha and opaque pixels stay exact.

Limits

  • No kerning pairs, one font page, no MSDF; the SDF output is Beta.
  • A TTF's cmap is not parsed, so the missing-glyph check needs a .fnt or the Lab's own font.
  • One atlas page; touching elements count as one.
  • Rendering inside Godot or Unity was not run: the engine numbers (patch margins, L-B-R-T border) are documented, not engine-tested.

Questions

Why is the focus image larger?

The focus ring grows the canvas by the outline width; its size is recorded in states.json.

Is the normal state changed?

No — normal is the source byte for byte (checked).

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