name: tinyworld-lowpoly-stylized-3d description: Use when adding, importing, designing, reviewing, or animating low-poly / stylized 3D assets in Tiny World Builder, including Three.js procedural meshes, GLB/GLTF assets, Poly Pizza models, material palettes, scale/orientation, silhouettes, clouds/planes/crop dusters, and toolbar thumbnails.
Tiny World Low-Poly Stylized 3D
Use this together with:
- Project skill
.codex/skills/tinyworld-single-file/SKILL.mdfor single-file constraints. - Project skill
.codex/skills/tinyworld-render-performance/SKILL.mdfor GPU/shadow/renderer limits. - Installed skill
.agents/skills/3d-modeling/SKILL.mdfor topology, UV, export, LOD, and GLB hygiene. - Installed skill
.agents/skills/poly-pizza-api/SKILL.mdwhen sourcing low-poly models from Poly Pizza. - Installed skill
.agents/skills/lightweight-3d-effects/SKILL.mdwhen adding decorative lightweight 3D effects.
Tiny World art direction
- Low-poly, toy-like, readable at thumbnail size.
- Chunky primitives with bevels/rounded slabs, not realistic detail.
- Strong silhouettes beat micro-detail.
- Bright but not washed out: use saturated local color plus darker trim/shadow-side material.
- Keep texture use rare and intentional. Procedural
THREE.MeshLambertMaterialcolors should remain the default for built-in objects. - Custom/generated voxel stamps should not render full bounding-cage trim by default; reserve bounds frames for explicit decorative-outline stamps.
- If generated voxel stamps include a broad ground/platform part, sink that base into the terrain rather than showing a raised tile under the object.
- Use flat/Lambert lighting semantics compatible with Three.js r128.
- Avoid glossy/PBR realism unless an imported asset already depends on it.
Scale rules
- One grid tile is
1 x 1world unit. - Small props should fit comfortably inside a tile: ~0.2–0.8 units wide.
- Houses can occupy one or multiple tiles, but doors/windows must remain readable from the default camera.
- Flying ambient objects should be scaled to feel like toys above the board, not real-world aircraft; crop duster wingspan target is around 1–1.5 tiles.
- Always normalize imported model scale with
Box3bounds, then apply a target span. - Apply orientation fixes once at model root or a named wrapper; do not keep stacking ad-hoc rotations in the animation loop.
Material and palette rules
- Prefer 2–4 materials per object: body, dark trim, highlight, accent.
- Bespoke generated voxel/custom-part models should use semantic material families rather than collapsing to the seed material. A greenhouse needs glass + frame + planting/base materials; an airship needs hull + brass/copper machinery + fabric/canvas balloon panels + cable/rope rigging + glass bridge/window accents.
- Generated custom-part renderers must keep those semantic families editable
after creation.
voxelAppearanceMaterial()should apply globalmaterialTextureeven when a part has no inferred body/top role, and body/top colours or textures should override only parts whose material/color maps to the matching role. - Use the custom-part
cableprimitive for actual connections: balloon ropes, crane lines, tethers, rigging, moorings, bridge suspension lines, and other angled cords. Do not fake these as vertical stone/wood columns when endpoints are known. - Use custom-part
sphere/ellipsoidprimitives for rounded balloon envelopes, domes, tanks, and soft canopies. Panel bands or ribs can be boxes only when they read as raised seams; colored balloon fabric panels should use curved ellipsoid slices (phiStart/phiLength) rather than square side plates. - Default custom-part stamps should be board-scale on first creation. Compact bridges, decks, docks, and props should be about one tile wide and can use a small negative Y offset to sit into the terrain/water; only deliberate hero objects should claim 1.5+ tiles.
- Native TinyWorld objects can be used as parts of a scene, but they should not
stand in for a requested model when
customPartscan express the model directly. - Never mutate shared
M.*material colors for one instance; clone or create a new material. The one allowed global exception isapplySeasonFoliage(), which centrally retints shared foliage/grass materials for season changes. - Three.js r128
MeshLambertMaterialdoes not acceptflatShading; keep faceted model-stamp fallbacks through non-indexed/flat-normal geometry instead of unsupported material flags. - Built-in material references should live in
engine/world/04-textures.jsas deterministic coarse canvas textures before adding per-object overrides. Current coarse material maps includepath-pavers,castle-block,brick-building,roof-shingles,window-lit,window-unlit,grass-voxel,grass-side,soil-side,fence-timber,crop-stalk,corn-cob, sunflower maps, andisland-side-blocks. Keep them intentionally large and calm from the default/top-down camera; if the whole island reads noisy, lower world-UV repeat before adding detail. Ground path is brick/paver blockwork, not rock chips or gravel: keep path terrain onpath-paversand avoid loose pebble overlays that make it read as stone rubble. Ground grass and ground stone default back to the older calmcottage-grass/cottage-stonemaps; use the newer coarse maps on object materials, side panels, or explicit user-selected terrain texture overrides instead. Stone riser/side faces use a separate large-block material (M.stoneSide) so cliffs can scale up without changing the stone ground cap. Home island edge grass/soil/rock strata is not a texture-map option: it is the dedicatedM.boardSideEdgeshader material, aligned fromTOP_H(the visible grass-cap top) down a shallow side backing behind the foreground greebles. It samplestextures/island-side-strata-gpt.png, a fixed 1024x192 horizontal strata slice; keep that slice image-driven and the same dimensions rather than rebuilding tall normalized procedural bands. Grass richness should stay FPS-safe through shared texture maps (grass-voxelorgrass-sideonly when intentionally selected), not added blade meshes. Stone blocks should stay light cool gray like the stair/column references rather than tan or charcoal; fence timber should stay warm and use horizontal grain bands so rails read correctly. - Cottage-style defaults remain part of the built-in material language:
texCottageGrass,texCottageWood, and the texture-folder atlases are still useful for softer surfaces, but stone/path/roof/window/fence/crop references should use the newer coarse maps when the requested style is square voxel material. - For imported texture variants, create explicit material variants and swap them at the model mesh level.
- For toolbar thumbnails, increase contrast/saturation carefully so icons read against the white toolbar, but keep the in-world material natural.
- If a model comes with a texture atlas, set
texture.encoding = THREE.sRGBEncodingand checkflipYfor GLTF compatibility. - Repo-backed model stamps must run a material hydration pass: preserve real embedded materials, apply known sidecar atlases (GLTF atlases use
flipY = false), parse OBJ.mtlsidecars when present, warn when they are missing, and apply a deterministic TinyWorld palette fallback to blankpalette/white materials so imports do not look like unpainted 3D prints. - GLB/GLTF model stamps must adapt PBR
MeshStandardMaterial/MeshPhysicalMaterialinto TinyWorld-lit Lambert materials while preserving base-color maps, vertex colors, transparency, emissive maps, skinning, and morph flags. The app has no environment map, so metallic PBR GLBs otherwise render nearly black beside native Lambert objects. Do not blindly preserve occlusion or normal maps on that conversion path: black AO/ORM red channels remove indirect lighting in the glTF spec, and broken/uniform normal maps can dominate lighting. The model-stamp loader samples AO maps and drops black ones, keeps non-color maps linear, and does not copy normal maps into the TinyWorld-lit Lambert material. The scene also includes model-stamp import safety fill lights so converted GLBs are not dependent on the shadow-casting sun alone. - Model stamp factories should apply
opts.appearancethemselves so world rendering, ghost previews, and selection previews share texture/color overrides; avoid applying the same model-stamp appearance again at the board render wrapper. - Wear-and-tear should stay stylized: global grime/desaturation plus small batched chips/scuffs/moss beats realistic noise-heavy shader work.
- Floating-board depth can reuse existing roof language by inverting a stepped roof form under the board: dark gray shingle-textured slabs, board-footprint width/depth, vertically compressed, and attached below the dirt body. Utility underside dressing should stay toy-like and readable: chunky pipe cylinders, cable trays, clamps, junction boxes, and short dangling cable drops in the existing steel/dark underside palette.
- Voxel lift/propeller engines use an explicit part palette inside
makeVoxelLiftEngine: mottled stone body,pipe-metalsteel hubs/shaft, wood crates/blades, and pale plank labels. Keep future engine material tweaks local to that factory so terrain/object materials do not drift globally. - Visual richness should come from selective density contrast: keep cliffs, walls, terrain bodies, and island masses chunky, then spend extra detail on roofs, windows, crops, trees, path storytelling, and hero landmarks. Prefer instanced/rule-based surface detail such as wheel ruts, edge roots, tiny signs/crates, and beacon/banners over globally raising voxel resolution.
- Garden fences are a style of the existing fence kind (
appearance.fenceStyle: "garden"), not a separate object kind. They should stay dark timber with a simple vine rail and small warm fruit accents, and still flow through the contiguous fence span renderer for long runs. - Lamp and spotlight stamps should read as physical voxel objects first: chunky metal bases/heads with warm emissive glass, plus a blurred haze and a fake ground spill decal. Spotlights point from the fixture outward/down; the light cone should be narrow at the head and wider/softer where it falls on the ground.
- Blast/shield art direction is locked to the supplied Voxel Blast Shield Core
classes, adapted into TinyWorld rather than recreated. Keep the dark damaged
metal panels, four rising corner keystones, panel chains extending from the
corners, outward blue rune faces, and
window.VoxelShieldcontrols intact. - The Tower house variant has paired factories:
makeStoneToweris the normal faceted/conical design andmakeVoxelStoneToweris the voxel counterpart. Keep their silhouettes aligned when changing tower roof, balcony, window, door, or flag details. Castle/turret rendering should stay block-built:makeTurretdelegates to the square voxel keep inmakeVoxelTurret.
Model import hygiene
- Keep assets under
models/and ensurepublish.shcopies them todist/models/. - Use the vendored Three.js r128 GLTF stack for GLB/GLTF (
GLTFLoader,DRACOLoader,MeshoptDecoder, and the module-backedKTX2Loaderbootstrap) and configure the loader before loading imported model stamps. Surface remaining unsupported-extension errors instead of silently showing the generic placeholder. - After loading:
- compute
Box3, center model at origin, - scale to target tile/world size,
- set cast/receive shadow intentionally,
- tag moving subparts in
userData, - dispose cloned materials/geometries if removed.
- compute
- Search named nodes before doing geometry surgery. Common names:
prop,propeller,blade,rotor,fan,wheel,flap.
Animation rules
- Animate only transforms and opacity.
- Respect the existing
userData.landingpattern for placed cell objects. - For propellers: wrap or find the named prop mesh, spin around its local blade axis every frame, and add a translucent disc for high-RPM readability. For TinyWorld-built lift engines, keep the fan plane centred on the lower shaft mount, prefer a shared dark shader blur/strobe disc, and hide physical blade groups once the spin ramp reaches speed.
- For rocket/jet flames: prefer chunky pixel/block shader sheets or capped particle pools over many animated micro-meshes. Preserve the toy-like silhouette with a plume that narrows toward the bottom, hard flicker bands, and warm core/outer colours rather than realistic volumetric fire. When replacing an object style, keep the older object factory as an inactive legacy helper instead of deleting it.
- For aircraft: use shallow easing, pitch with climb/descent slope, and bank during turns. Do not teleport or dive straight down into the board.
- Small world motion is preferred over heavy renderer tricks: tree asymmetry,
crop/wheat/corn/sunflower sway, window glow, smoke, waterfalls, clouds, and
engine movement should sell life while keeping object roots in the existing
animatedCellObjects/runtime sets. - Particle effects should be capped and use cheap cloned
MeshBasicMaterial; dispose particle materials when particles die.
Validation checklist
- Inline script passes:
perl -0ne 'print $1 if m#<script>\s*(.*?)\s*</script>#s' tiny-world-builder.html | node --check. ./publish.shcopies any new assets intodist/.- Default camera shows the asset at the intended scale.
- Shadows are visible but not noisy; no huge new shadow casters.
- Toolbar thumbnail remains readable.
- No material mutation leaks into other objects.