Skip to content

Tileset Extruder — Atlas Padding That Stops Texture Bleeding

Filtering and mipmaps sample just outside a tile, so neighbouring tiles bleed in as lines. Extrusion copies each tile's edge pixels outward, the classic fix used with Phaser, LÖVE, Defold and Unity tilemaps.

Thin lines or gaps between tiles appear when the GPU samples a texel just outside a tile's rectangle: linear filtering, mipmaps or a camera at a fractional position read the neighbouring tile or the empty gap. Extrusion fixes that by copying each tile's outermost pixels outward into a ring, so a sample that lands outside the tile still reads the tile's own colour. This page extrudes every tile of a grid sheet (2 px by default, 0–16) into padded-atlas.png, where tiles sit at margin = extrude and spacing = 2 × extrude. Check your engine first: Godot 4 TileSets already add 1 px of padding internally.

Drop a tileset or a repeating texture (PNG)
Open the Tile Lab
Runs in your browser. Files are not uploaded.
Tile Lab: a Kenney CC0 tilemap with its measured grid (16 px tiles, 1 px spacing)
The real Tile Lab with a CC0 asset (Kenney).
Outputs
  • TilesMeasured· Pillow
  • Padded atlasMeasured· Pillow
  • Seam reportMeasured· Pillow

Why lines appear between tiles

A tilemap draws every tile from a rectangle of the atlas. With nearest filtering and the camera on whole pixels each screen pixel reads one texel inside that rectangle and nothing bleeds. Linear filtering blends the four nearest texels, so a sample at the very edge takes up to half its colour from the texel next door; mipmaps average 2 × 2 blocks per level, so at smaller sizes the neighbour mixes in further; and a zoom or camera position that is not a whole number moves samples onto those edges.

Padding and extrusion are different fixes. Padding (TexturePacker's shape padding) leaves transparent pixels between the rectangles: the neighbour is no longer read, but the empty gap is, which shows as a dark or see-through line. Extrusion repeats the edge pixels outward (TexturePacker: "repeats the sprite's pixels at the border; sprite's size is not changed"), so whatever leaks in has the tile's own colour. For tiles that must meet edge to edge, extrusion is the one that hides the line.

Some engines already do it. Godot 4's TileSetAtlasSource.use_texture_padding is on by default and generates an internal texture with one extra pixel around each tile, so a Godot 4 TileSet usually needs nothing here. Tiled supports margin and spacing precisely so that extruded tilesets can be used, and Phaser's addTilesetImage takes tileMargin and tileSpacing. After extruding, those two numbers change, and forgetting that is the most common new problem.

Example: extruding a 203 × 186 sheet by 2 px

The sheet has 12 × 11 tiles of 16 × 16 with 1 px spacing. Pixel positions in the output:

source    203 × 186: 12 × 11 tiles of 16 × 16, margin 0, spacing 1
extrude 2 → each cell 16 + 2 × 2 = 20 px → padded-atlas.png 12 × 20 = 240 by 11 × 20 = 220
tile 13   column 1, row 1: source 17,17 → atlas x = 1 × 20 + 2 = 22, y = 22 (pixels 22–37)
ring      x 20–21 repeat the tile's column 0, x 38–39 repeat column 15; each 2 × 2 corner repeats the corner pixel
engine    margin 2, spacing 4 → Tiled margin 2 / spacing 4 · Phaser addTilesetImage(name, key, 16, 16, 2, 4) · Godot margins (2, 2), separation (4, 4)
padding   the same 4 px left transparent: a linear sample halfway between texel 37 and 38 is 50 % tile, 50 % nothing

The source sheet's own 1 px spacing disappears: the atlas is laid out again from the grid. metadata.json in the same ZIP keeps the source rects (tile 13 at 17,17), so use margin and spacing for the padded atlas.

What it does

  • Nine blits per tile: the tile, its four edges stretched outward and its four corner pixels.
  • Works on sheets with margin and spacing (the tiles are found by the measured grid).
  • Output: padded-atlas.png next to the sliced tiles and metadata.json (source rects); in the atlas each tile sits at margin = extrude, spacing = 2 × extrude.
  • Quality checks recorded in Chromium and Firefox: byte-identical to the reference extrusion, 1×1 cells included.

How to do it

  1. Drop the tileset.
  2. Confirm the grid.
  3. Set the extrude width (1–2 px is typical).
  4. Download the padded atlas and metadata.

Check the result

  • Open padded-atlas.png: it is columns × (tile + 2 × extrude) by rows × (tile + 2 × extrude), and tile 0 starts at (extrude, extrude).
  • Zoom into the ring around one tile: every ring pixel equals the nearest edge pixel of that tile.
  • In the engine, set margin = extrude and spacing = 2 × extrude, then pan and zoom the camera by fractional amounts across a tile border: no line should appear.

Troubleshooting

SymptomLikely causeHow to checkFix
Lines flicker only while the camera moves or zoomsFractional positions plus linear filtering sample across tile bordersStop the camera at a whole-pixel position: the lines disappearExtrude the atlas, or use nearest filtering with whole-pixel camera positions
Lines appear only when the map is zoomed outMipmaps average blocks that include the neighbouring tileTurn mipmaps off for the tileset texture: the lines vanishExtrude more (2–4 px), or disable mipmaps for 2D tilemaps
After extruding, every tile shows part of its neighbourThe engine still uses the old margin and spacingTile 0 should start at (extrude, extrude) in the padded atlasSet margin = extrude and spacing = 2 × extrude (extrude 2 → 2 and 4)
Dark fringes around transparent parts of tilesThe colour under fully transparent pixels is black and filtering blends it in; extrusion only adds pixels around tilesLook at the RGB of transparent pixels near the edgeFill the colour under transparent pixels with edge bleed
Gaps remain with nearest filtering and no mipmapsThe tiles themselves are placed apart: map positions or scale are not whole pixelsCheck tile positions and the camera scaleFix the map or camera; this is outside what an atlas can fix, and Nerulio cannot change it

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
Tilestiles/tile-NNN.png · metadata.jsonMeasured PillowEvery sliced tile equals its source region pixel for pixel (margin, spacing, blanks, duplicates).
Padded atlaspadded-atlas.pngMeasured PillowEdge pixels duplicated outward; the recorded atlas-padding quality checks pass in Chromium and Firefox.
Seam reportPNG (healed) · measurements on screenMeasured PillowA wrapping tile is reported seamless, a ramp is reported as a seam, and the healed tile's mean wrap difference is below 8 levels.

Evidence

Grid detection ranked the true grid first on 10 of 10 synthetic sheets (margin 1 / spacing 2 included); 13/13 sliced tiles byte-identical to their source regions; a 1600-tile slice finishes in 2.2 s and can be cancelled; seam verdicts measured against the tile's own interior.

Limits

  • Regular grids only; above 4 megapixels the tile size is typed in instead of measured.
  • Near-duplicate detection is a pixel threshold, not perceptual similarity.
  • "Make seamless" changes the art inside its blend bands and synthesises nothing; before and after are shown.
  • A tile smaller than 2 × 2 cannot be measured for seams.

Versions and sources

Measured for docs/TILE-LAB.md and tests/recipes-browser.py: a 2 × 1 sheet of 1 px tiles with 1 px extrusion gives a 6 × 3 atlas with the edge pixels isolated; a sheet with 8 px tiles, margin 1 and separation 2 gives a 36 × 24 atlas whose 6 × 64 tile pixels and 6 × 8 left-edge pixels equal the source; the output is byte-identical to the reference extrusion for a 32 × 32 sheet at padding 3 and for 1 × 1, 1 × 16 and 16 × 1 cells at padding 2 (Chromium and Firefox). The example positions were recomputed with the same layout function. Engine behaviour follows the Godot 4.7, Tiled, Phaser and TexturePacker documentation.

Official documentation used

Questions

Do I need to change my engine's tile settings?

Yes: the tiles move apart. In the padded atlas the margin is the extrude width and the spacing twice it (extrude 2 → margin 2, spacing 4); metadata.json keeps the source sheet's rects.

Is this the same as edge bleed?

No: extrusion adds pixels around each tile in an atlas; edge bleed fills transparent pixels inside an image.

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