Tile size, margin and spacing
Tile n in column c and row r starts at x = margin + c × (tile width + spacing) and y = margin + r × (tile height + spacing). The number of columns that fit is floor((image width − margin + spacing) ÷ (tile width + spacing)); pixels left over at the right or bottom edge are ignored. Each tile is copied out of the decoded sheet as an exact rectangle, never resampled.
Measurement looks for the period of the art: per axis it builds the difference between each line and the one before, folds that profile at every period from 4 px to half the image and scores how well each fold explains it. Lines that a margin or spacing would reserve must be blank or flat, a blank first or last line inside every tile means the tile is smaller and that line is spacing ("16 px + 1 px gap" beats "17 px tiles"), and a multiple of the true period loses to its divisor. Above 4 megapixels nothing is measured and the size is typed in.
Engines take the same numbers for the whole sheet: Godot 4's TileSetAtlasSource has margins, separation and texture_region_size, Tiled's image-based tileset has margin and spacing, Phaser's addTilesetImage takes tileMargin and tileSpacing, and Unity's Sprite Editor grid slicing has Pixel Size, Offset and Padding. Only a Tiled "Collection of Images" tileset, where each tile refers to its own image file, needs the single PNGs.
- Margin
- Pixels between the image edge and the first tile.
- Spacing (separation)
- Pixels between two neighbouring tiles.
- Blank tile
- A tile whose every pixel has alpha 0; skipped by default and counted in
skippedBlank. - Alias
- A tile identical to an earlier one; written once and mapped in
aliases.
Example: a 203 × 186 sheet with 16 px tiles and 1 px spacing
The numbers the slicer uses, and the same layout in three engines:
sheet 203 × 186, tiles 16 × 16, margin 0, spacing 1
columns floor((203 − 0 + 1) / (16 + 1)) = floor(204 / 17) = 12
rows floor((186 − 0 + 1) / 17) = floor(187 / 17) = 11 → 132 tiles
tile 13 column 1, row 1 → x = 0 + 1 × 17 = 17, y = 17, 16 × 16 → tiles/tile-013.png
names 3 digits up to 1000 tiles (tile-005); 4 digits above (tile-0005 in a 1600-tile sheet)
other 100 × 64 sheet, margin 1, spacing 2 → 5 × 3 tiles at x = 1, 19, 37, 55, 73; 11 px on the right are unused
engines Godot margins (0, 0), separation (1, 1) · Tiled margin 0, spacing 1 · Phaser addTilesetImage(name, key, 16, 16, 0, 1)
File names keep the tile's grid index, so when blank tiles are skipped the numbering has gaps on purpose: tile-013 is always column 1, row 1 of this sheet.
Check the result
- Turn on the grid lines: every line should fall in a gap or on a tile edge, never through the art.
- Open
metadata.json: tileSet.columns × rows is the grid, count the files written, skippedBlank and deduplicated explain any difference. - Open one tile PNG: it is exactly the tile size, and its
rect in frames satisfies x = margin + col × (tile + spacing).
Troubleshooting
| Symptom | Likely cause | How to check | Fix |
|---|
| Every tile carries a line of its neighbour or is shifted by a pixel | Wrong margin or spacing, typically 17 px tiles instead of 16 + 1 | Grid lines cross the art instead of the gaps | Pick the other candidate in the ranked list, or type the margin and spacing |
|---|
| Fewer files than tiles in the grid | Blank tiles are skipped, or duplicates were written once as aliases | skippedBlank, deduplicated and aliases in metadata.json | Turn blank skipping or duplicate detection off to get one file per grid cell |
|---|
| The period found is 32 px but the tiles are 16 px | The art repeats a larger pattern, so a multiple of the tile scores well | Compare the scores of the 16 px and 32 px candidates | Choose the 16 px candidate or type the size |
|---|
| The tile size is not suggested at all | The image is above 4 megapixels, where the grid is not measured | The field is empty and the note says to type it | Type the tile size, margin and spacing; slicing still works |
|---|
Other ways to do it
| Option | When it is the better choice |
|---|
| Load the whole sheet in the engine with the margin and spacing | Almost every tilemap: Godot TileSetAtlasSource, a Tiled image-based tileset or Phaser addTilesetImage read one image and no tile files have to be managed. |
|---|
| Tileset slicer | You need an engine tileset with terrain rules for Godot, Tiled, Unity or LDtk rather than loose PNGs. |
|---|
| Unity Sprite Editor, Grid By Cell Size | The sheet is only used in Unity: Pixel Size, Offset and Padding create the sprite rects inside Unity's importer. |
|---|
What this page does not cover
- At most 4096 tiles per slice; near-duplicate grouping is capped at 2048 tiles.
- The ZIP carries no engine tileset;
metadata.json is the generic envelope with rects, columns and rows.
Versions and sources
Measured for docs/TILE-LAB.md: the true grid ranked first on 10 of 10 synthetic sheets (margin 1 with spacing 2, odd 15 px, 16 × 32 tiles, outlined tiles); 13 of 13 sliced tiles byte-identical to their source regions (Chromium + Pillow); 47 of 48 tiles written with 1 blank skipped and 1 alias; 1600 tiles of 8 px sliced in 2.2 s with a working cancel. The engine parameter names follow the Godot 4.7, Tiled, Phaser and Unity documentation.
Official documentation used
Tile Grid Slicer
Split a tile grid into individual PNGs at an exact tile size.
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
Split a tile grid into individual PNGs at an exact tile size.
Implementation and verified quality
advanced · exact grid region copy. Quality qualification is in progress. Pro/Flagship quality has not been established.
Before you export
Tile size must divide the image exactly. Autotile rules are not generated.