Skip to main content

texturesight

UV and texture review built exclusively for AI agents. A mesh and its maps in; exact measurements, located findings, and evidence renders out.

Texture work is judged by putting a checker on the model and looking, or tiling a plane and squinting. An agent can do neither — and it turns out none of it needs eyes. Texel density is UV area against 3D area. Stretch is the singular values of the per-face UV Jacobian. A seam is an edge whose two faces land in different UV islands. A normal map is either unit-length vectors or it is broken.

model.obj + maps  ->  texturesight inspect  ->  report.json + uv_layout.png  ->  fix  ->  diff

What it measures

texel density px per world unit, area-weighted, with the spread across the mesh
distortion per-face anisotropy from the UV Jacobian's SVD; flipped winding
islands & seams UV islands vs mesh shells; seams counted from the topology, with their 3D length
packing utilization of the 0..1 square, overlap between islands, out-of-bounds faces
tiling the wrap jump judged against the texture's own pixel-to-pixel variation
repetition autocorrelation peak and offset (reported, not moralised about)
normal maps unit length, Z channel validity, green convention (OpenGL/DirectX)
data maps range used, distinct levels, constant/quantised detection, grayscale check
compression codec blocking, required on both axes because a codec's grid is square

Findings carry where and a try:. Everything is deterministic.

Quickstart

pip install texturesight

texturesight inspect --mesh model.obj --texture-px 2048
# -> out/report.json
# -> out/uv_layout.png    (islands; red = flipped, orange = stretched)
# -> out/uv_density.png   (texel density painted per face)

texturesight inspect --texture rock_normal.png       # kind guessed from the name
texturesight inspect --texture rough.png --kind roughness

Or as a Claude Code plugin:

/plugin marketplace add VortexJer/AISight
/plugin install texturesight@aisight

(the plugin carries the skill; the CLI it drives still comes from pip)

Exit codes: 0 ok, 1 bad input, 2 a FAIL-level finding.

Never unwrapped a mesh? Two renders explain the whole domain

A UV layout is unreadable if you have never made one, so every mesh inspect also writes the two pictures that explain it without words:

correspondence.png — same colour = same piece of surface. The flat shapes on the right ARE the 3D faces on the left, peeled onto the texture like a cardboard box unfolded into its die-cut template. Where a piece lands decides which pixels paint it.

checker_preview.png — the broken cube wearing a checker through its own UVs, front and back. Every defect the report measures is now visible to any eye: the giant L = the density-starved island (its cells are 3x the size), the squashed cells = the 4:1 stretch, and the mirrored L marks on the back = the flipped faces.

Proof it works: known ground truth

examples/01-cube is synthetic on purpose — every defect is injected by make_assets.py with a known magnitude, so the tests can assert the right answer rather than merely an answer.

injected measured
one face's UVs squashed to 1/4 in u anisotropy max 4.0:1 (exact)
one island scaled to 1/3 density spread 3.0x, min 109.23 of 327.68 px/unit (exact)
one face's UV winding reversed [FAIL] 2 face(s) have inverted UV winding
one island moved outside 0..1 2 face(s) fall outside, uv bbox [0.01, 0.01, 1.99, 0.66]
a normal map gamma-encoded (sRGB'd) [FAIL] 88.2% of texels are not unit-length (mean 1.1612)
a texture's edges darkened does not tile: the wrap jump is 34.74x the texture's own variation
a roughness map quantised to 6 levels only 4 distinct levels: it was quantised somewhere

And the clean cube reports anisotropy exactly 1.0, density spread exactly 1.0x, zero findings, exit 0.

the broken cube: red = flipped winding, orange = the 4:1 stretch, the small island = the density hole (the sixth is off-canvas, which is the out-of-bounds evidence)

Three bugs the clean reference caught

The clean cube exists to fail loudly when the maths is wrong, and it did — all three of these were found before shipping, by the reference, not by a user:

  1. A transposed matrix in the UV Jacobian reported a conformal map (square UVs on square faces) as 2.618:1 stretched. Every asset would have been accused of stretch. (test_uniform_scale_is_conformal)
  2. Codec blocking was tested on one axis, so a posterised roughness map — contour bands that happen to correlate with x but not y — was reported as JPEG artifacts. A codec's grid is square; both axes must agree. (test_banding_is_not_reported_as_codec_blocking)
  3. Repetition warned on every tiling texture. A texture that tiles is periodic by definition. It is now reported as a measurement, and only warned about for a tight repeat inside a single tile.

False positives are how a tool loses the right to be believed, so they are treated as bugs of the same severity as false negatives.

Before / after: one turn of the loop

examples/02-crate is the classic layout edit gone wrong: the lid's island packed at 1/3 scale. Invisible on the model until the texture is painted — then the lid is blurry forever. The finding names the island, its place, and the factor:

islands: #4 is the sparsest (2.05 px/unit, 2 face(s)) vs #0 (6.14)
[WARN] texel density varies 3.0x across the mesh
       where: island #4 at uv (0.35, 0.35)-(0.45, 0.45): 2.05 px/unit vs 5.46 mesh mean
       try:   scale island #4 up ~2.7x in the UV editor

Apply the try: line, re-inspect into a new out dir, and prove it:

texturesight diff out_starved out_fixed
  density: mean 5.46 -> 6.14 px/unit; spread 3.0x -> 1.0x
  GONE [texel-density-uneven] texel density varies 3.0x across the mesh

before: island #4 dark (starved) · after: uniform density

Blind vs measured: the hero crate

examples/03-crate-hero is the full study: a 576-triangle sci-fi supply crate, unwrapped and textured twice — once by a cold-context agent with no tools at all (numpy + PIL, one shot), once through this tool's loop. The blind build's painted maps survive the audit untouched; its little UV mapper hides three real bugs: 148 flipped faces (FAIL), 7.35:1 stretch, 54x texel density spread. The after build imports the blind generator, rewrites only the mapper, and lands at 0 flips, 1.005 p95 anisotropy, 3.35x spread — with the two remaining warnings explained as intent (trim-sheet overlap, downres'd hidden faces).

left: the blind unwrap — 148 flipped faces in red, islands stacked 336 deep · right: the rewritten mapper — 0 flips, welded shells, 92% packing

texel density painted per face — blind: a 54x spread between the best- and worst-fed faces · after: 3.35x, with the hidden faces' downres declared as intent

And the same pair through the human-eye renders — read them knowing what they CAN'T show:

the crate and its unfolded template, blind vs after — in the blind one the island labels pile into smudges: 336 pieces stacked on the same texels. Same box outside; the template underneath is a heap vs a layout.

the checker test is the honest one: blind and after look almost the same (the flips live on the trims — 9.9% of the surface — and the stacking shares texels invisibly). A checker catches what it can see; the 148 flips, 54x spread and 336-deep stacking were measured, not spotted. That is the whole point of this tool.

Full numbers in the example's README.

Reading the results honestly

Several findings are only defects in context, and the tool says so rather than pretending:

  • Out-of-bounds UVs are correct for tiling and UDIMs.
  • Overlap is correct for mirrored geometry sharing texture.
  • Repetition is correct for a brick or a weave.
  • The green convention is a guess from the map's own statistics — no format records it. Confirm against what the engine expects.

Scope

Reads OBJ (with UVs) and standard image formats.

Does not do: unwrapping, packing, baking, painting, or fixing anything — it audits; you fix it in the tool that made it. It does not judge whether a texture is beautiful, only whether it is correct.

Siblings, same philosophy: solidsight (3D geometry), animationsight (motion clips).

Leaving: texturesight uninstall removes this tool's skill and package. pip install aisight && aisight uninstall removes the whole family — every skill, every package and the plugin marketplace. See aisight.

License

MIT

Metadata

Release files for texturesight 0.5.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for texturesight 0.5.2
File Size Uploaded
texturesight-0.5.2.tar.gz 42.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for texturesight 0.5.2
File Interpreter ABI Platform
texturesight-0.5.2-py3-none-any.whl Python 3 none any Details

Total release size: 81.3 kB

Release files / texturesight-0.5.2.tar.gz

Download URL texturesight-0.5.2.tar.gz
Size 42.3 kB
Tags Source
SHA-256 checksum
How to use checksums
8d2ce41f54bd000d796f5eae901d0b8c4f499586e3b49f9751e349cc9b30dde9
BLAKE2b-256 checksum
How to use checksums
b9170bd6f86c14a95da43956058c683f59db57272ee9ee1cc1770db3ba015a69
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 23, 2026.

Transparency log

Release files / texturesight-0.5.2-py3-none-any.whl

Download URL texturesight-0.5.2-py3-none-any.whl
Size 39.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
284e2978da8d60158b6ff9f741b053dc03e18ad83209e14ab9925e798cd8815d
BLAKE2b-256 checksum
How to use checksums
e7acb7db8890011ec977d58447c47f01266275bbfa3bcebdbd4410bd810b604a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 23, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.5.2 This release

2 release files

0.5.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page