UL

uilianks/loom

Developer tools
24 stars Quality 68 Trend 38MIT

Seamless game textures and PBR materials as code for AI agents: noise, bricks, planks, weathering; albedo/normal/height/roughness/AO/ORM maps, lit previews and a live viewer.

Overview

Write a small .tx.js file of fields (noise, cells, bricks, planks, scales, ...) combined with operators and weathering masks; loom writes every map an engine needs: albedo, normal, 16-bit height, roughness, metallic, AO, a packed ORM and emissive. Every generator and operator tiles, so , and each build reports a seam check to prove it. Built for AI agents (it ships as a Claude Code skill). Every build writes a : the material tiled 3×3 under a raking light, on a lit sphere and an isometric cube, and every map, so the agent judges its textures the way it reads its code. A live viewer rebuilds on save. Part of a family with (vector art, skeletal animation), (pixel art), (game audio) and (low-poly and voxel 3D). Inspired by , with the same architecture: sandboxed source files, lazy cached builds, errors with exact locations, a live viewer and an annotate-and-send feedback loop.

README

loom

Seamless game textures and PBR materials as code. Write a small .tx.js file of fields (noise, cells, bricks, planks, scales, …) combined with operators and weathering masks; loom writes every map an engine needs: albedo, normal, 16-bit height, roughness, metallic, AO, a packed ORM and emissive. Every generator and operator tiles, so materials are seamless by construction, and each build reports a seam check to prove it.

Built for AI agents (it ships as a Claude Code skill). Every build writes a preview sheet: the material tiled 3×3 under a raking light, on a lit sphere and an isometric cube, and every map, so the agent judges its textures the way it reads its code. A live viewer rebuilds on save.

Part of a family with vex (vector art, skeletal animation), pxl (pixel art), chirp (game audio) and kiln (low-poly and voxel 3D). Inspired by claude-pxl, with the same architecture: sandboxed source files, lazy cached builds, errors with exact locations, a live viewer and an annotate-and-send feedback loop.

In a 3D scene

The Dragon’s Hoard, built in kiln and rendered in Blender: mossy brick, wet cobblestone, stained concrete, worn wood, rusted iron, glowing lava rock, dragon scales and hammered gold, all from showcase/materials.tx.js. A kiln palette entry points at a loom output folder ({ tex: "…/brick_wall", uv: 1.5 }) and the GLB embeds the albedo, normal, ORM and emissive maps.

Showcase

Nine materials from showcase/materials.tx.js, about 15 lines each.

Each one tiled 3×3: no seams.

Material Built from
brick_wall bricks with wobble, chipped by thresholded ridged, grime in cavity(), moss on facing("up")
rusty_panels tiles + rivets (dots along .dist), paint flaking from the edges, rust in the seams with drips()
wet_cobblestone cells().edge stones, per-stone hue from .id, mud and puddles in the blurred gaps
wood_planks planks with .grain, per-board tone, scuffs
scifi_panel tiles, grooves, emissive light strips on some plates, scratches from stretched ridged
lava_rock cells crust over glowing emit magma in the cracks
dragon_scales overlapping scales, hue sheen per scale, metallic gold rims
stained_concrete poured noise, pits, saw-cut seams, wet stains with low roughness
hammered_gold cells dents hammered into the metal, tarnish in the hollows

Quick start

git clone https://github.com/uilianks/loom && cd loom
npm install
node bin/loom.js build showcase/materials.tx.js     # -> showcase/out//*.png + previews
node bin/loom.js serve showcase/materials.tx.js     # live viewer on http://127.0.0.1:4350

Requires Node ≥ 22.18 (it runs the TypeScript sources directly).

pal({ brick: "#8e4a35", brickL: "#b46a4c", brickD: "#5e2c22", mortar: "#9c968a", grime: "#2b2622", moss: "#4c6a2c" })

const b = bricks({ rows: 10, cols: 4, gap: 0.014, bevel: 0.02, wobble: 0.5, seed: 3 })
const grain = noise({ scale: 24, octaves: 4 })
const height = b.mul(0.85).add(grain.mul(0.1))
const grime = height.cavity(0.006, 16).max(b.mask.invert().mul(0.5))
const moss = height.facing("up", 4).mul(noise({ scale: 6, seed: 9 }).threshold(0.58, 0.2)).mul(b.mask)

material("brick_wall", { size: 512, depth: 0.03 }, {
  height,
  color: ramp(b.id.mix(grain, 0.35), ["brickD", "brick", "brickL"])
    .mix("mortar", b.mask.invert()).mix("grime", grime.mul(0.6)).mix("moss", moss.mul(0.85)),
  rough: grain.levels(0, 1, 0.75, 0.95).mix(0.6, moss),
})

Features

  • Generators: fractal and ridged noise (stretchable for grain and streaks), Worley cells with edges and ids, dots, running-bond bricks, tiles, hexagons, planks with grain, overlapping scales, stripes, waves, basket weave, checker and shape decals (circle, rect, ngon, star, ring, line: markings, rims, runes, logos). Patterns expose .mask, .id (a random value per piece) and .dist.
  • Images in: image("photo.png", { seamless: 0.25 }) turns a photo or painted texture into a tiling color (edge-blended), and .luma() into a height map, to mix with generated detail.
  • Operators: arithmetic, mix, levels, smoothstep, threshold, normalize, blur, domain warp, offset, repeat, quarter-turn rotate and flip, all wrapping at the tile edge.
  • Weathering from height: cavity() for grime, convex() for worn edges, facing("up") for moss and dust, drips() for rust and rain streaks.
  • Maps: albedo, tangent-space normal (GL or DX), 16-bit height, roughness, metallic, AO derived from height, ORM pack and emissive, plus a JSON manifest per material.
  • Seam check on every build, and periods, repeats and rotations that would break tiling are refused.
  • Live viewer: lit plane, sphere and cube with a draggable light, every map, every named field.
  • Annotate mode: draw on the texture, pin notes and send to Claude; the feedback records what the material and every named field are at each marked pixel.
  • Errors point at file:line:col with “did you mean” suggestions.

Use as a Claude Code skill

git clone https://github.com/uilianks/loom ~/.claude/skills/loom-textures && (cd ~/.claude/skills/loom-textures && npm install)

Then ask Claude for a material (“a mossy castle wall”, “rusted ship hull”, “a stylized grass tile”). SKILL.md teaches the workflow and the habits that make materials read as real; the full API is in reference.md.

Development

npm run check          # type check + tests (seamlessness of every generator and op, normals, errors, outputs, feedback)
node scripts/gallery.ts  # regenerate docs/media/gallery.png and tiled.png

License

MIT. Inspired by and partly adapted from claude-pxl by crsolver; see NOTICE.

View the full README on GitHub

Recommended Tools

Try a different keyword or remove a filter.

Install

npx skillfish add uilianks/loom
New to MCP? Start with the mcpskill.org guide →