9-Slice Editor

Drag the four borders of a panel image and see it drawn at several target sizes at once.

A nine-slice (9-patch) border is four numbers in source pixels — left, right, top, bottom — that cut an image into a 3 × 3 grid. The four corners are always drawn at their own size, the top and bottom edges stretch or repeat horizontally, the left and right edges vertically, and the centre both ways. Set the borders once here (suggested from identical columns and rows), preview any size, then enter the same numbers in Godot 4 (NinePatchRect.patch_margin_*), Unity (Sprite Editor border, Image Type Sliced) or Phaser (add.nineslice). The download is PNG + nine-slice.json + SETUP.md; rendering inside the engines was not run.

Drop image files hereProcess many files at once
No upload · processed on this device

The nine regions and what each one does

For a W × H image with borders L, R, T and B the cuts give four corners (L × T, R × T, L × B, R × B), a top and a bottom edge (W − L − R wide), a left and a right edge (H − T − B tall) and the centre. When the panel is resized, corners are copied unscaled, horizontal edges change only their width, vertical edges only their height and the centre both. That is why a rounded or bevelled corner survives any size, and why the smallest size that shows every corner whole is (L + R) × (T + B).

Stretch resamples an edge or the centre to the new length, which is right for flat colour or a gradient along the other axis. Tile repeats it at its natural size, which keeps a pattern (rivets, stitches, a brick edge) undistorted; Nerulio clips the last partial tile. Godot 4 also has Tile Fit, which stretches the tiles slightly so each one is shown whole; Nerulio's preview does not draw that mode.

Every engine stores the same four numbers under different names and in a different order. Godot 4 uses patch_margin_left/right/top/bottom on a NinePatchRect (integers, in pixels) and texture_margin_* on a StyleBoxTexture. Unity's Sprite Editor has L, R, T and B fields, while the scripting value Sprite.border is a Vector4 in the order left, bottom, right, top. Phaser's NineSlice takes leftWidth, rightWidth, topHeight, bottomHeight after the size, and becomes a 3-slice when only the left and right widths are given.

Border (patch margin)
The distance in source pixels from one edge of the image to the cut line.
Stretch
An edge or the centre is scaled to fill the new length.
Tile
An edge or the centre is repeated at its original size.
Minimum size
(L + R) × (T + B): below it the corners cannot be drawn whole and are squashed.

Example: a 48 × 48 panel drawn at 300 × 80

Borders 12 px on every side. These are the rectangles the editor's draw plan produces, the same plan the exported previews use:

source 48 × 48   borders L 12 · R 12 · T 12 · B 12   target 300 × 80   stretch
corners     4 × (12×12 → 12×12)     copied 1:1, never resampled
top/bottom  24×12 → 276×12          276 = 300 − 12 − 12   (×11.5 horizontally only)
left/right  12×24 → 12×56           56 = 80 − 12 − 12     (×2.33 vertically only)
centre      24×24 → 276×56
tile mode   top edge = 11 tiles of 24 px + 1 clipped tile of 12 px   (11 × 24 + 12 = 276)
too small   target 20 × 80: 20 < 12 + 12 → corners squashed to 10 + 10 (warning "narrow")
asymmetric  L 10 · R 6 · T 8 · B 14   →  Unity Sprite.border (x, y, z, w) = (L, B, R, T) = (10, 14, 6, 8)
Godot       patch_margin_left 12 · right 12 · top 12 · bottom 12
Phaser      this.add.nineslice(x, y, "panel", null, 300, 80, 12, 12, 12, 12)

With an integer scale of 2 the corners are drawn at exactly 24 × 24 (each source pixel becomes 2 × 2), so a pixel-art frame stays crisp at double size.

Files you get

FileWhat it is for
panel.pngThe source image, unchanged, named after your file.
previews/100x40.png, 300x80.png, 800x200.png + custom sizeThe panel rendered at each preview size with your mode and scale (the custom size defaults to 420 × 120).
nine-slice.jsonThe borders as pixels, normalized (border ÷ width or height), godot4 (patch_margin_*) and unity (border in L, B, R, T order, named), plus mode, scale and the preview sizes.
SETUP.mdShort Godot 4 and Unity steps with your numbers filled in, marked as not run inside the engines.

Enter the borders in Godot, Unity or Phaser

  1. Godot 4: add a NinePatchRect, set its Texture to the PNG and patch_margin_left, patch_margin_right, patch_margin_top, patch_margin_bottom to the four numbers from nine-slice.json.
  2. Godot 4, tile mode: set axis_stretch_horizontal and axis_stretch_vertical to Tile (or Tile Fit). For a Button or Panel theme, use a StyleBoxTexture with the same numbers in texture_margin_* and the same axis stretch settings.
  3. Unity: select the texture, set Mesh Type to Full Rect and click Apply, open the Sprite Editor and drag the green handles or type the L, R, T and B fields (the pixels values).
  4. Unity: on a Sprite Renderer set Draw Mode to Sliced (or Tiled, with Tile Mode Continuous or Adaptive); on a UI Image set Image Type to Sliced (or Tiled).
  5. Phaser 3.60 or later: this.add.nineslice(x, y, key, frame, width, height, leftWidth, rightWidth, topHeight, bottomHeight). The Phaser 4.1 API lists tileX and tileY to repeat instead of stretch.
  6. For pixel art keep Nearest filtering and resize by whole numbers of source pixels, so the corners stay sharp.

Check the result

  • Resize the engine node to 300 × 80 and compare it with previews/300x80.png: the four 12 × 12 corners should match pixel for pixel.
  • Shrink it below L + R wide: Nerulio warns "narrow" for the same size, and Phaser documents L + R as the minimum width.
  • In Unity, Debug.Log(sprite.border) should print the unity.border numbers from the JSON (left, bottom, right, top).

Troubleshooting

SymptomLikely causeHow to checkFix
Corners stretch in UnityMesh Type is Tight, or the Image or Sprite Renderer still draws SimpleTexture import settings; Image Type or Draw ModeMesh Type Full Rect + Apply, then Image Type Sliced or Draw Mode Sliced
Top and bottom borders look swapped in UnityThe Vector4 order (left, bottom, right, top) was typed into the L, R, T, B fields, or the other way roundCompare the Sprite Editor fields with pixels in the JSONType pixels into the Sprite Editor; use unity.border only for Sprite.border in scripts
Corners look squashed at small sizesThe control is smaller than L + R or T + BNerulio shows the "narrow" or "short" warning for that sizeMake the control larger or the borders smaller
A patterned edge looks smearedThe edge is stretched, so a repeating pattern is resampledSwitch the preview to Tile and compareGodot: axis stretch Tile or Tile Fit; Unity: Tiled; Phaser 4: tileX / tileY
Pixel-art corners are blurryLinear filtering, or a scale that is not a whole numberZoom in: soft steps on the cornerNearest filtering and integer scales (the editor's scale is 1–4)
Phaser NineSlice from a TexturePacker atlas looks wrongThe frame was trimmed; the Phaser docs say NineSlice does not support trimmed TexturePacker texturesCheck the atlas JSON for trimmed framesPack the panel untrimmed, or load it as its own image
"Suggest borders" finds no clear runThe middle is a gradient or noise, so no columns or rows are identicalThe suggestion says there is no repeating column or rowPlace the guides by hand where the corner art ends

Other ways to do it

OptionWhen it is the better choice
Set the borders directly in Godot's NinePatchRect inspector or Unity's Sprite EditorOne or two panels, already in the engine: you see the real engine rendering while you drag.
The UI Lab atlas stageMany panels on one sheet: each element gets its own borders and they all travel in one ui-atlas.json.

What this page does not cover

  • Rendering inside Godot, Unity or Phaser was not run: the numbers match their documentation, not an engine test.
  • No engine files are written (no .tres, .meta or Android .9.png).
  • Tile mode at an integer scale above 1 squashes the final partial tile by up to (scale − 1) ÷ scale of a source pixel.

Versions and sources

Measured in Chromium for docs/UI-LAB.md: the 6 × 6 corner blocks of every exported preview are byte-identical to the source at 100 × 40, 300 × 80, 800 × 200 and 420 × 120; a plan has 0 gaps and 0 overlaps at 300 × 80, at scale 3 and when squashed to 8 × 4; a striped 12 × 12 panel tiles with its 4 px period; the suggestion finds 6 · 6 · 6 · 6 from runs of 12 identical columns and rows. In Firefox semi-transparent corner pixels can shift by one alpha-rounding step. Godot and Unity import is UNVERIFIED. Engine settings above follow Godot 4.7, Unity 6 and the Phaser API documentation.

Official documentation used

9-Slice Editor

Drag the four borders of a panel image and see it drawn at several target sizes at once.

How it works

  1. Add images
  2. Adjust settings and review the result
  3. Download a new file

Supported formats

PNG / JPG / WebP / AVIF / HEIC → PNG + JSON (ZIP) — Input decoding depends on browser support and optional engines.

What this tool does

Drag the four borders of a panel image and see it drawn at several target sizes at once.

Implementation and verified quality

advanced · repeated-line border suggestion / nine-slice draw plan (stretch and tile). Quality qualification is in progress. Pro/Flagship quality has not been established.

Before you export

Border suggestion is a heuristic that looks for repeated columns and rows. Stretching and tiling are drawn with the browser canvas.

Frequently asked questions

What are this tool’s limitations?

Border suggestion is a heuristic that looks for repeated columns and rows. Stretching and tiling are drawn with the browser canvas.

Are my files uploaded?

This tool processes the files you open on your device without uploading them. Images you explicitly attach to community posts are uploaded separately. Optional engines and models are downloaded from third parties; those requests expose connection information such as your IP address.

Will my work be saved automatically?

No. Images and editing state stay in this tab; media output can use local temporary storage. Changing language preserves your work; reloading, closing the tab, or following a link to another page can clear it.