Skip to content

Seamless Texture Checker — Find and Fix Tiling Seams

"Mean difference 23" means nothing on its own. The checker compares the wrap edges with the difference between two ordinary neighbouring lines inside the same tile, so the verdict says how much worse the seam is than the texture itself.

A texture tiles without a visible seam when its right column continues into its left column and its bottom row into its top row. The checker measures exactly those wrap pairs and reports each axis next to the same difference taken between ordinary neighbouring lines inside the tile, because "mean 23" only means something compared with the texture's own variation. It shows 2 × 2 and 3 × 3 repeats, a heatmap along each edge and a verdict per axis, and can make the tile seamless by offsetting it half a tile and cross-fading, which changes the art inside the blend bands.

Drop a tileset or a repeating texture (PNG)
Open the Tile Lab
Runs in your browser. Files are not uploaded.
Tile Lab seam check: a CC0 texture repeated 2×2 with its wrap edges measured
The real Tile Lab with a CC0 asset (ambientCG).
Outputs
  • TilesMeasured· Pillow
  • Padded atlasMeasured· Pillow
  • Seam reportMeasured· Pillow

How a seam is measured

For every row the checker compares the last pixel with the first (the horizontal wrap), and for every column the bottom pixel with the top one (the vertical wrap). The difference of two pixels is the largest of their four channel differences, alpha included, on the 0–255 scale. Each axis gets a mean and a worst value, plus a per-line profile for the heatmap.

The reference is the mean difference between every pair of adjacent columns (or rows) inside the tile. An axis passes when its wrap mean is at most max(2, 1.25 × that neighbour mean), and the tile is called seamless when both axes pass. That is why a noisy stone texture whose wrap differs by 21 levels passes (its neighbours differ by 21 too) while a smooth gradient with a jump of 252 fails (its neighbours differ by 4). The heatmap is scaled against the same neighbour mean, so a seamless tile stays dark instead of being normalised to bright red.

Make seamless offsets the tile by half its width and height, so the old wrap edges meet in the middle, then cross-fades a band on each side of that middle line with the half-shifted copy, using smooth weights. The band defaults to 12 % of the tile (8 px on a 64 px tile) and can be set from 0 to 64 px. The new edges wrap by construction; the price is a blended cross through the middle, where large features can appear doubled. Nothing is synthesised.

Wrap pair
The two lines that touch when the tile repeats: last column and first column, bottom row and top row.
Neighbour mean
The average difference between adjacent lines inside the tile, the texture's own variation.
Ratio
Wrap mean ÷ neighbour mean; around 1 is invisible, 63 is a hard seam.
Blend band
The strip on each side of the moved seam that is cross-faded by Make seamless.

Example: a gradient that fails and a noise tile that passes

Both tiles are 64 × 64. The numbers are what the checker reports:

ramp tile       grey 0 → 252 from left to right (+4 per column), all rows identical
horizontal      column 63 (252) against column 0 (0): mean 252, worst 252
neighbour mean  4.00 → ratio 252 / 4 = 63; limit max(2, 4 × 1.25) = 5 → seam
vertical        rows identical: wrap 0, neighbours 0 → passes
make seamless   offset 32, 32; band round(64 × 0.12) = 8 px → wrap mean 4, neighbours 5.78, ratio 0.69 → seamless
noise tile      values 96–160: wrap mean 20.8 / 18.8, neighbour mean 21.5 → ratio 0.97 / 0.87 → seamless

The noise tile has a larger wrap difference than the healed ramp and still passes, because its interior varies just as much. A single threshold on the raw number would get both of these wrong.

What it does

  • 2×2 and 3×3 repeat previews of the whole image (or any tile of a sheet).
  • Left↔right and top↔bottom mean and worst difference, next to the same measurement inside the tile.
  • Edge heatmap scaled against that interior difference, and an optional edge match against a second tile.
  • Make seamless: half-offset and cross-fade across the seam, with before/after shown.

How to do it

  1. Drop the texture or tile.
  2. Read the verdict and the numbers per axis.
  3. Look at the heatmap and the repeat preview.
  4. Optionally make it seamless and download.

Check the result

  • Look at the centre of the 3 × 3 repeat at 100 % and 200 %: no line, no step in brightness and no change of texture density along its borders.
  • Read both axes: each ratio near 1 or below; the heatmap has no bright band along a whole edge.
  • After Make seamless, compare before and after inside the blend bands for doubled features, and check the verdict again.
  • Put the downloaded PNG on a large repeating surface in your engine and move the camera across a seam.

Troubleshooting

SymptomLikely causeHow to checkFix
The checker says seamless but a line shows in the gameThe engine clamps instead of repeating, or mipmaps and compression change the edge texelsCompare the 3 × 3 preview with the game at the same zoom; test with mipmaps offSet the texture to repeat/wrap in the engine's sampler settings; check the compressed texture
A seam is flagged on a sprite with a transparent borderAlpha counts in the difference, and a transparent edge against an opaque one is a real jumpIs the tile meant to repeat at all?Ignore the result for sprites; the check is for repeating textures and tiles
Make seamless leaves a blurry or doubled cross in the middleThe cross-fade mixes two different parts of the texture inside the bandCompare before and after around the middle linesTry a narrower band under Advanced, or repaint the middle in an image editor
The tile passes but the surface looks obviously repeatedDistinct features recur at the tile period: that is repetition, not a seamLook at the 3 × 3 preview from a distanceUse a larger texture, several tile variants, or break the pattern with decals

Other ways to do it

OptionWhen it is the better choice
Offset the image by half in an image editor and repaint the cross by handThe seam needs real painting (a crack or a plank edge) rather than a blend.
Export the texture with tiling from the tool that generated itA procedural or material tool made the texture: its own tiling option avoids healing afterwards. For normal maps see seams in tiling normal maps.

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

  • The verdict measures pixel differences at the wrap; it does not detect visible repetition or lighting that changes across the tile.
  • Only the healed PNG is written; the measurements stay on screen.
  • 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 in tests/game-seams.test.mjs: a cosine tile that wraps gives a ratio below 1.3 and is reported seamless; a ramp gives a mean above 240 and a ratio above 20 and is reported as a seam; after Make seamless the mean falls below 8. The worked example above was recomputed with the same module (a 64 px ramp and a seeded noise tile).

Questions

Does "make seamless" change my art?

Yes, inside the blend bands: it offsets by half the tile and cross-fades. It synthesises nothing, and shows before and after.

Why is a noisy texture judged seamless with a big edge difference?

Because its interior differs just as much. The verdict compares the seam with the texture's own variation.

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