Repository navigation
How Checks Decide
pw.py check FILE --target T inspects the file and compares it with the rules of one target. The rules live in skills/polywright/references/targets.json, each target with advice lines that say where its numbers come from (checked 2026-10-11).
python pw.py check --list{
"targets": {
"game-roblox": "Roblox MeshPart through the 3D Importer (FBX, OBJ or glTF)",
"game-unity": "Unity prop or character (GLB through glTFast or FBX)",
"game-unreal": "Unreal Engine 5 static mesh (FBX, glTF or USD)",
"web-threejs": "three.js, model-viewer or Babylon page (GLB)",
"ar-usdz": "Apple AR Quick Look (USDZ)",
"commerce-gltf": "Product model for e-commerce viewers (Khronos 3D Commerce guidelines)",
"print-fdm": "FDM print, 0.4 mm nozzle, 256 mm cube bed (Bambu P1S, X1, A1 class)",
"print-resin": "Resin (MSLA) print: miniatures and fine detail",
"video-blockout": "Blockout or proxy for AI video and previs (structure guides, depth and normal passes)",
"cad-step": "Engineering interchange (STEP from code CAD), with a mesh preview"
},
"checked": "2026-10-11"
}
Exit code 0.
| Rule | Fails or warns when | Targets that use it |
|---|---|---|
max_tris |
the file has more triangles than the limit (game-roblox and the advisory budgets warn; others fail) | roblox, unity, web, ar, print, video |
max_tris_per_object |
one object is over the limit | roblox (20,000) |
max_objects |
more objects than the limit (warn) | none by default |
max_materials |
more materials than the limit (warn) | roblox (1), unity (4) |
max_image_px |
the largest texture side is over the limit (warn) | roblox 1024, web and ar 2048, unity 4096, unreal 8192, commerce 2048 |
require_uvs |
an object has no UV map | roblox, unity, unreal, web, ar, commerce |
require_watertight |
there are boundary or non-manifold edges | print-fdm, print-resin, cad-step |
single_part |
more than one separate part (warn) | print-fdm |
forbid_unapplied_scale |
an object still has a scale other than 1 (warn) | roblox, unity, unreal, commerce |
max_dims |
the size does not fit the box in any rotation | print-fdm (256 mm cube), print-resin |
min_feature |
never measured: reported as unchecked, look at a render |
print-fdm 0.9 mm, print-resin 0.3 mm |
max_file_mb |
the file is bigger than the limit (warn) | web 10, ar 25, commerce 5 |
real_scale |
with --real-size-m X: the longest side is more than 10%% off X |
any |
The status is fail when any rule fails, warn when any warns, pass otherwise; the exit code is 1 only for fail. ok is false only on fail.
The park scene from Commands against Roblox: under the triangle limits and the right size, but six materials (Roblox takes one per mesh: a warning) and no UVs (a failure):
{
"ok": false,
"target": "game-roblox",
"status": "fail",
"results": [
{
"rule": "max_tris",
"status": "pass",
"detail": "6196 tris (limit 20000)"
},
{
"rule": "max_tris_per_object",
"status": "pass",
"detail": "ground_park has 4312 tris (limit 20000)"
},
{
"rule": "max_materials",
"status": "warn",
"detail": "6 materials (limit 1)"
},
{
"rule": "max_image_px",
"status": "pass",
"detail": "largest texture 0 px (limit 1024)"
},
{
"rule": "require_uvs",
"status": "fail",
"detail": "objects without UVs: ground_park, ground_default, roads, buildings, trees, tree_trunks"
},
{
"rule": "real_scale",
"status": "pass",
"detail": "longest side 111 m vs expected 110 m (x1.01)"
}
],
"advice": [
"20,000 triangles per mesh (create.roblox.com specifications, checked 2026-10-11; vendors quote 21,000, test the importer). Split bigger models into several MeshParts.",
"One material per mesh; SurfaceAppearance takes colour, normal (OpenGL, Y+), roughness, metalness. Texture size limit unverified (historically 1024 px).",
"1 stud = 0.28 m: a 1.8 m person is about 6.4 studs. Export in metres and scale in Studio, or scale by 1/0.28 before export.",
"At most 4 bone influences per vertex, root joint at the origin, no zero-thickness surfaces. CollisionFidelity Box or Hull for props."
],
"inspect": {
"tris": 6196,
"objects": 6,
"dims": [
111.01131,
111.03461,
7.3
],
"watertight": false,
"parts": 6,
"materials": [
"buildings",
"ground_default",
"ground_park",
"roads",
"tree_trunks",
"trees"
],
"max_image_px": 0,
"backend": "blender",
"blender": "5.2.1 LTS"
}
}
Exit code 1.
Fix by finishing for the target (Commands: --join --uv), or split the scene into one MeshPart per material. A generated fountain went from this failure to pass after convert --size-m 1.0 --ground --center --max-tris 15000 --uv (2026-10-11).
| Target | Key number | Source (checked 2026-10-11) |
|---|---|---|
| game-roblox | 20,000 triangles per mesh, one material | Roblox's specifications page; vendors quote 21,000 |
| game-unity | 50,000 triangles (warn) | a project choice for desktop hero props, not a Unity limit |
| game-unreal | no triangle limit | Nanite takes dense opaque meshes (UE 5.8 docs) |
| web-threejs | 150,000 triangles, 10 MB (warn) | a page-weight choice, not a spec |
| ar-usdz | about 100,000 polygons, 2048 px textures | Apple's guidance (secondary source, 2021) |
| commerce-gltf | 5 MB, 2048 px | the Khronos 3D Commerce v1.0 figures (the current text gives none) |
| print-fdm | 0.9 mm walls, 256 mm bed | two perimeters at 0.4 mm (Prusa); the Bambu P1S class bed |
This wiki describes polywright 0.1.2 and was last updated on 2026-10-11. The plugin is MIT licensed. Report problems in the issues.
Using it
- Getting started
- The skills
- Commands
- How checks decide
- The asset contract
- Errors and edge cases
- Recipes
- FAQ
The releases
Contributing
Elsewhere