Nodes/LF Nodes/Iso diamond tiles
ComfyUI Node

Iso diamond tiles

Cut a seamless texture into isometric ground tiles

By lucafoscili·Created 2 years ago·Updated 22 days ago· 36
Iso diamond tiles
  • image
  • ui_widget
  • tiles
  • atlas
  • receipt
  • tile_list
◄tile_width128►
◄tile_height64►
◄variants4►
◄seed42►
◄texture_scale0.50►

You generated a nice cobblestone texture. You want a game-ready isometric terrain tile set. LF_IsoDiamondTiles is the step between those two sentences, and it's a much better idea than hand-rotating squares in an image editor.

The insight is almost embarrassingly simple: an isometric diamond is a top-down square rotated 45 degrees and squashed 2:1. So if your source texture already tiles, every diamond you cut out of it tiles with every other one - and you can generate variants just by sampling at different offsets.

How it works

The node takes one seamless top-down texture, doubles it (so sampling wraps instead of clamping at the edges), and for each variant does a GPU-side affine grid sample: the diamond's pixel grid is transformed into texture space with that 45°/2:1 projection, plus a seeded random offset in U and V. Bilinear filtering, border padding, and an antialiased diamond coverage mask for the transparent corners.

That doubling is the whole trick. Because the source wraps, a diamond that runs off one edge continues on the other side - which is the property you need for tiles that sit next to each other in a game without a visible grid.

texture_scale controls how much of the texture one diamond spans along its diagonal. At the 0.5 default you get a clean readable tile; push it up and you're zooming into the texture, down and you're zooming out to more repetition per tile. Randomise seed and raise variants for variation - with variants at 1 you're just getting the same crop four times over in a different place, which is fine if that's the point.

The inputs and outputs that matter

  • image - one seamless top-down texture. Only the first image in a batch is used, so don't expect a batch of textures to come back as a batch of tile sets.
  • tile_width / tile_height - default 128 × 64. That 2:1 is the isometric convention; deviate and you get a stretched diamond, which is occasionally what you want for a specific engine.
  • variants - how many differently-offset diamonds to cut. Default 4.
  • seed - the offsets are seeded, so the same seed gives the same tiles. Reproducible tile sets, which matters more than it sounds when you're shipping.
  • texture_scale - 0.05 to 4, default 0.5.

Outputs: tiles (all variants as one RGBA batch at the requested size), atlas (every variant side by side in a single row - the thing you actually drag into an engine or a sprite sheet), tile_list (the same tiles as a list), and receipt - a JSON payload with tile geometry, the seeded offsets and the atlas dimensions. Keep the receipt. When a tile set looks wrong three days later, the seed and offsets are how you reproduce it.

How to install it

ComfyUI Manager → search LF Nodes → install → restart. Manual:

cd ComfyUI/custom_nodes
git clone https://github.com/lucafoscili/lf-nodes
python -m pip install -r lf-nodes/requirements.txt

Pip with ComfyUI's interpreter. The pack is 154 nodes wide and its requirements are correspondingly broad (torch, opencv, rembg, av, transformers), so a fresh install is heavier than the node you're after. Also: lucafoscili/comfyui-lf is the archived old repo - the live one is lf-nodes.

There's a shipped example worth running: the pack's Workflow Runner includes an Iso Ground Tiles card that chains this after LF_SeamlessTile, so a texture that doesn't tile yet gets fixed first. It's opt-in - set WORKFLOW_RUNNER_ENABLED=true before starting ComfyUI if you want to see it.

Common issues & troubleshooting

Visible grid lines between tiles. Your texture isn't actually seamless. The projection math assumes it wraps; if the source has a hard edge, every diamond carries that edge, just rotated. Feed it through LF_SeamlessTile first, or generate tileable with circular padding in the first place.

All variants look identical. They will, at low texture_scale with a very repetitive source - the offsets are there, but there's nothing different to sample. Raise variants and texture_scale together.

Corners are black instead of transparent. They shouldn't be - the tiles come out RGBA with an antialiased coverage mask - but check what you're saving with. A JPEG or a flattened PNG export kills the alpha, and then you get black diamonds in a grid.

The atlas is enormous. It's a single row: variants × tile_width wide. At 128px and 64 variants that's an 8192px image. Cut the variants, or split the atlas yourself.

It only processed one texture. Documented behavior - first image only. Run the node per texture.

Category✨ LF Nodes/Image

Inputs (7)

NameTypeDefaultDescription
imageIMAGEOne seamless top-down texture; only the first image is used.
tile_widthINT1288–1024Diamond tile width in pixels.
tile_heightINT644–1024Diamond tile height in pixels (half the width for 2:1).
variantsINT41–64How many differently offset diamonds to cut.
seedINT420–2147483647Seed for the variant offsets.
texture_scaleFLOAT0.500.05–4Fraction of the texture one diamond spans along its diagonal.
ui_widgetoptLF_MASONRY[object Object]—

Outputs (4)

NameTypeDescription
tilesIMAGERGBA diamond tiles, one per variant, at the requested tile size.
atlasIMAGEAll variants side by side in one RGBA atlas row.
receiptJSONReceipt with tile geometry, variant offsets and atlas size.
tile_listIMAGEThe same tiles as an authoritative list.