Texture Offset
Shift, rotate, and check seams without re-rendering
- image
- image
- edge_mask
Texture Offset does one deceptively useful thing: it shifts your texture by a fraction, rotates it, or both - and keeps the result seamless if you want it to. The classic use is seam testing. A texture can look perfectly tileable in a flat view and still show a hard seam the moment it repeats, because you never looked at the offset boundary. Shifting by half a tile exposes exactly that.
You also reach for it when a texture comes out of a generator pointing the wrong way, when a normal map needs its orientation fixed, or when you want to nudge a detail pattern into alignment with your UVs without firing up an image editor.
How it works
The node applies three things, in order: offset, then rotation. The interesting part is the wrap_mode, because it changes the math:
- repeat - the offset uses
torch.roll, which is a true circular wrap: pixels falling off one edge reappear on the other, no sampling artifacts. Rotation in this mode tiles the image 3×3, rotates, then crops the center back - which is exactly why rotation stays seamless here. - clamp - edges are stretched rather than wrapped, via grid sampling with border padding. Good for bordered images, useless for tiling.
- mirror - reflects at the edges, which creates symmetric patterns.
The second output, edge_mask, is a white-highlighted mask of the transformed edge regions - useful if you want to know where the operation affected things (for example, to feed into an inpainting pass later).
Inputs worth touching
- offset_x / offset_y (−1.0 to 1.0, as a fraction of width/height) - 0.5 shifts by exactly half, which is the seam test you'll actually run. The
repeatwrap is the one to keep on for tileable textures. - rotation (−360° to 360°) - degrees. With
repeatenabled, this is the only place you can rotate a texture in this pack and keep it seamless. - wrap_mode - repeat, clamp, or mirror, as above.
- edge_mask_width (0.0–0.5) - how wide the edge-mask highlight extends.
Outputs are image and edge_mask, both IMAGE.
Installing it
Texture Offset ships in ComfyUI-TextureAlchemy. In ComfyUI Manager, search "Texture Alchemy", install, restart. By hand:
cd ComfyUI/custom_nodes
git clone https://github.com/amtarr/ComfyUI-TextureAlchemy
# restart ComfyUI
No extra Python dependencies and no model files - pure PyTorch. It's under Add Node → Texture Alchemist → Texture. (The README's copy-the-ComfyUI_PBR_MaterialProcessor-folder instruction is stale - that's the pack's old name, so use Manager or git clone.)
Where people get burned
The most common mistake is leaving wrap_mode on repeat when you don't want circular wrapping - say, offsetting a decal or a bordered texture. You'll get pixels bleeding across edges that were supposed to stay put; switch to clamp. The other trap: offsets are fractions, not pixels. offset_x = 0.5 isn't "move right by 50 pixels", it's "move by half the image", so on a 2048 map it's a 1024-pixel shift. That's the right model for seam testing, just know what the numbers mean. Combine it with the pack's Texture Tiler - offset by half, then tile 2×2, and any remaining seam is immediately visible.
Inputs (6)
| Name | Type | Default | Description |
|---|---|---|---|
| image | IMAGE | — | |
| offset_x | FLOAT | 0.00-1–1 | Horizontal offset (-1.0 to 1.0, fraction of width) |
| offset_y | FLOAT | 0.00-1–1 | Vertical offset (-1.0 to 1.0, fraction of height) |
| rotation | FLOAT | 0-360–360 | Rotation in degrees (0-360) |
| wrap_mode | COMBO | repeat | Wrapping mode for edges: repeat (tile), clamp (extend), mirror (reflect) |
| edge_mask_width | FLOAT | 0.100–0.5 | Edge mask width (0-0.5, fraction of image) - shows affected areas |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| image | IMAGE | — |
| edge_mask | IMAGE | — |