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
| File | What it is for |
|---|
panel.png | The source image, unchanged, named after your file. |
|---|
previews/100x40.png, 300x80.png, 800x200.png + custom size | The panel rendered at each preview size with your mode and scale (the custom size defaults to 420 × 120). |
|---|
nine-slice.json | The 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.md | Short 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
- 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. - 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. - 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). - 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).
- 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. - 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
| Symptom | Likely cause | How to check | Fix |
|---|
| Corners stretch in Unity | Mesh Type is Tight, or the Image or Sprite Renderer still draws Simple | Texture import settings; Image Type or Draw Mode | Mesh Type Full Rect + Apply, then Image Type Sliced or Draw Mode Sliced |
|---|
| Top and bottom borders look swapped in Unity | The Vector4 order (left, bottom, right, top) was typed into the L, R, T, B fields, or the other way round | Compare the Sprite Editor fields with pixels in the JSON | Type pixels into the Sprite Editor; use unity.border only for Sprite.border in scripts |
|---|
| Corners look squashed at small sizes | The control is smaller than L + R or T + B | Nerulio shows the "narrow" or "short" warning for that size | Make the control larger or the borders smaller |
|---|
| A patterned edge looks smeared | The edge is stretched, so a repeating pattern is resampled | Switch the preview to Tile and compare | Godot: axis stretch Tile or Tile Fit; Unity: Tiled; Phaser 4: tileX / tileY |
|---|
| Pixel-art corners are blurry | Linear filtering, or a scale that is not a whole number | Zoom in: soft steps on the corner | Nearest filtering and integer scales (the editor's scale is 1–4) |
|---|
| Phaser NineSlice from a TexturePacker atlas looks wrong | The frame was trimmed; the Phaser docs say NineSlice does not support trimmed TexturePacker textures | Check the atlas JSON for trimmed frames | Pack the panel untrimmed, or load it as its own image |
|---|
| "Suggest borders" finds no clear run | The middle is a gradient or noise, so no columns or rows are identical | The suggestion says there is no repeating column or row | Place the guides by hand where the corner art ends |
|---|
Other ways to do it
| Option | When it is the better choice |
|---|
| Set the borders directly in Godot's NinePatchRect inspector or Unity's Sprite Editor | One or two panels, already in the engine: you see the real engine rendering while you drag. |
|---|
| The UI Lab atlas stage | Many 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
- Add images
- Adjust settings and review the result
- 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.