Isometric Operations

SkillMonitoring & ops

Create, refine, compose, and export isometric/dimetric assets for web and games: projection math, SVG/CSS/three.js generation, pixel-art and Blender workflows, engine tilemaps, ControlNet AI generation, and the iso-studio composer. Triggers on: isometric, dimetric, tileset, y-sort.

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the Isometric Operations skill

What this skill tells your AI

The instructions your AI receives, as published by 0xdarkmatter/claude-mods in skills/isometric-ops/SKILL.md and read by ahel’s review.

Create, refine, compose, and export isometric illustrative assets for websites and games — end to end. This skill covers the exact projection math, the vector/CSS/SVG and three.js generation routes, the pixel-art and Blender pre-render pipelines, engine tilemap integration, the AI-generation-with-structure-control workflow, asset sourcing with licence discipline, and the companion iso-studio scene composer (a standalone app that grew out of this skill). Every constant is derived and machine-checked; every workflow is runnable; every claim is sourced in its reference file.

Boundary — what this skill does NOT own. General three.js / creative-coding scaffolding lives in genart-ops; app- and game-scale three.js (GLTF pipeline, react-three-fiber, InstancedMesh) lives in the threejs-ops skill. Colour science and perceptual (OKLCH) ramp construction live in color-ops. isometric-ops owns only the isometric delta — it cross-links those siblings, it never restates them. Detail lives in the reference files below: this router points, it does not duplicate.


1. The projection decision — always step zero

Choose the projection before you draw, generate, or model anything. This is not a deferrable stylistic preference: it fixes the grid math, the tile aspect ratio, the camera rig, and the anti-aliasing strategy for the entire pipeline downstream. Changing it later means re-cutting every asset.

JobProjectionGround-axis angleTile / face ruleWhere
Web / vector illustration, diagrams, icons, hero artTrue isometric30° (all three axes 120° apart)all axes foreshorten equally; smooth vector edges ignore pixel steppingprojection-math.md §1, svg-vector-generation.md, css-isometric.md
Game tiles, tilemaps, sprite worlds, most "isometric" games2:1 dimetric (commonly called isometric in games)26.565° = arctan(1/2)integer 2px:1px steps tessellate; tileW = 2·tileHprojection-math.md §2, coordinates-depth.md, engine-integration.md
Hand-placed / library pixel-art primitives (cubes, bricks, slopes)Pixel-neat 1:222.6° (obelisk.js pixel-dot pattern)1:2 pixel dot stepping avoids staircasing at primitive-drawing levelprojection-math.md §7, pixel-art-workflow.md

The failure mode this prevents. Nearly every "isometric" game is actually dimetric (only two of the three axis separations are equal: ≈116.565°, 116.565°, 126.870°). The trap: an artist draws a "30° isometric" tile, the engine places it on a 2:1 (26.565°) diamond grid, and the tile edges do not meet their neighbours. Small at one tile, catastrophic across a 50×50 map. The fix is not a nudge tool — it is deciding, up front, that game tiles are 2:1 dimetric at 26.565° and writing that number into the tile spec so every asset is cut to the same grid.

Terminology discipline (applies everywhere). On first use per document write "2:1 dimetric (commonly called isometric in games)", then "2:1 dimetric" thereafter. Never call the 2:1 game projection "isometric" unqualified. Distinguish "isometric drawing" (100% scale) from "isometric projection" (81.65% = √(2/3) scale) whenever the distinction affects a measurement.

Canonical constants (the authority table)

Machine-emitted by iso-math.py constants; full derivations and ground-truth checks in projection-math.md.

SymbolExact formValueAppears as
Cube tilt ("magic angle")arctan(1/√2) = arcsin(1/√3)35.264°3D iso rotation
Foreshortening (projection scale)cos(35.264°) = √(2/3)81.65% (0.81650)true-projection edge scale
Isometric drawing scalefull-scale convention100%vector iso (edges read at 100%)
True ground angledefinition30°vector iso axes
SSR / top-plane squashcos(30°)86.602% (0.86603)Illustrator scale, 2D plane matrices
Figma height / circle→ellipsetan(30°)57.735% (0.57735)Figma hack, ellipse minor axis
CSS back-tiparctan(√2) = 90° − 35.264°54.7356°CSS rotateX
CSS un-foreshorten√(3/2) = 1/cos(35.264°)1.22474CSS scale3d
Dimetric ground anglearctan(1/2)26.565°game tiles
Dimetric screen slopetileH/tileW (2:1)0.5tile-space +x = (+tileW/2, +tileH/2)

Two Blender ortho-camera rigs, both must appear wherever rigs are discussed: 2:1 dimetric = RotX 60°, RotY 0°, RotZ 45° (cube top 2× wide as tall, sin 30° = 0.5); true isometric = RotX 54.736°, RotY 0°, RotZ 45° (all three faces equal). Most tutorials use 60/0/45 and mislabel it "isometric" — it is dimetric. See blender-prerender.md §1.


2. Task router — six routes

Each route is a numbered mini-workflow. Follow the links for the exact numbers, code, and gotchas — the steps here are the spine, the references are the flesh.

Route A — Illustrate for the web (vector / CSS / SVG)

For diagrams, icons, hero art, marketing scenes → true isometric (30°).

  1. Decide true iso (§1). Pin the light direction and palette up front (style-guide.md).
  2. Pick the medium. Live DOM elements you want selectable / accessible / SEO-visible → CSS. Static shapes and icon sets → SVG. See the decision tables in css-isometric.md and svg-vector-generation.md.
  3. CSS route. 3D: transform-style: preserve-3d on the container, per-face children, then the outer stack rotateX(54.7356deg) rotateZ(-45deg) scale3d(1.22474,…) — the scale goes on the container only (or faces double-scale). 2D affine for flat cards: the rotate(-30deg) skewX(30deg) scaleY(0.866) recipe family, derived per plane in css-isometric.md.
  4. SVG route. Reach for @elchininet/isometric (SVG-native, planes/paths) or hand-roll diamond/cube/prism paths using the plane matrix() recipes (Top matrix(0.86603, 0.5, 0.86603, -0.5, 0, 0), etc.). Grab a ready grid from assets/grids/ or emit one with iso-math.py grid-svg.
  5. Optimise & export. Simplify paths in-tool → export → SVGO/SVGOMG → raster derivatives, in that order (svg-vector-generation.md §7).
  6. Verify visually. For map-heavy web work, headless-screenshot checks belong to playwright-ops; tie the render back to the style checklist.

Route B — Build a game tileset (spec → generate → validate → pack → engine)

For tilemaps and sprite worlds → 2:1 dimetric (26.565°). This is the discipline route; skipping the spec is how sets drift.

  1. Write the spec first. Copy the fill-in template from tile-spec.md and pin: projection + exact angle, tile W×H (W = 2H), unit elevation (px per z-step), anchor at the feet, footprint grammar (1×1, 2×1, 2×2…), transparent margin/bleed, palette tokens, one light direction, output format, name_direction_variant.png naming, and scale grammar (one human = N tiles).
  2. Generate the tiles — draw them (pixel-art-workflow.md), pre-render from 3D (Route D), or AI-generate (Route C). Every asset obeys the spec's numbers.
  3. Validate each tile against the spec: uv run scripts/tile-validate.py --tile-w 64 --tile-h 32 tiles/*.png — flags dimension drift, alpha halos, edge-bleed, off-centre anchor, palette overflow (exit 10 on any violation). Every spec line maps to a check (tile-spec.md "How the spec feeds tile-validate.py").
  4. Pack into an atlas: uv run scripts/sheet-pack.py tiles/ --trim --padding 2 --pot → one sheet PNG + a JSON atlas. An atlas turns N texture binds into 1 — the single biggest win for tile-heavy scenes (coordinates-depth.md §11).
  5. Integrate into the engine — Godot 4 TileMapLayer (Shape=Isometric, Layout=Diamond Down, Y-Sort on, origin at feet), Unity orthographic checklist, or Phaser/PixiJS manual cart↔iso. Atlas anchor/pivot mapping is spelled out in engine-integration.md.
  6. The runtime math — tile↔screen, picking, and the (x+y, z, layer, zBias) depth sort with anchor-at-feet — lives in coordinates-depth.md and is mirrored by iso-math.py to-screen / to-tile (round-trip verified).

Route C — AI pipeline (generate → control → refine → vectorize)

Fast, but perspective drifts without structure control. Pick the model by what the output must be, then hold the geometry with ControlNet.

  1. Climb the decision ladder (ai-generation.md §1): editable vectors → Recraft (vector-native); hero raster → Midjourney --sref/--sw (+ Firefly for brand-safe vector with Content Credentials); local control / tilesets → Flux/SDXL + iso LoRA + ControlNet; consistent large sets → a custom-trained model (Scenario/Layer) on 10–20 on-style refs.
  2. Prompt from the ready scaffolds in assets/prompt-library.md — subject + projection + material language + simplification rule + lighting rule + output intent, plus the universal negative-prompt block (vanishing points, perspective distortion, dramatic shadows, text, watermarks). Doctrine in ai-generation.md §6.
  3. Control the structure. For anything that must tessellate or hold true perspective, condition with ControlNet: depth (massing), MLSD (architecture lines), lineart/canny (exact outlines). The gold-standard workflow is Blender blockout → depth + normal pass → dual-ControlNet generation (ai-generation.md §4; blockout export via Route D or iso-studio, Route E).
  4. Refine. Upscale with the creative camp at resemblance-high / creativity-low to sharpen edges without inventing perspective-breaking geometry; need >4× → regenerate at a higher base instead. Clean AI edge-halos (semi-transparent fringe) mechanically — tile-validate.py detects them (ai-refinement.md).
  5. Vectorize if you need scalable output: Recraft (cleanest) → Vectorizer.AI → SVGcode/potrace → Illustrator Image Trace + Expand; re-impose the three-tone plane system after tracing (ai-refinement.md §4, style-guide.md).
  6. Check licences before delivery — LoRA and model licences bite (see Route F and the gotcha index).

Route D — Pre-render from 3D (Blender / three.js)

Model once, bake sprites for eight directions. The web-native alternative to Blender is a three.js scene.

  1. Rig the ortho camera at the correct rotation for your projection — both rigs are in blender-prerender.md §1 (60/0/45 dimetric vs 54.736/0/45 true iso) with the cube-top verification test.
  2. Blender route. Drive it headless: blender -b -P assets/blender-iso-rig.py -- --projection dimetric21 --directions 8 --out ./sheet. A parented empty spins the model for N-direction batching; transparent film; one render per direction. Add --passes for the depth + camera-space normal maps that feed ControlNet (Route C).
  3. three.js route. Owns only the iso delta (threejs-orthographic.md): exact-rotation idiom (camera.rotation.order='YXZ'; y=-π/4; x=atan(-1/√2)), frustum sizing with the resize-recompute gotcha, pixel-perfect world→CSS-px mapping, render-to-target sprite export at 1×/2×/4×, constrained OrbitControls, and 8-direction sprite baking in the browser. General scene scaffolding → genart-ops.
  4. Feed the tileset pipeline. Baked sprites re-enter Route B at step 3 (validate) → 4 (pack) → 5 (engine).

Route E — Compose a scene (iso-studio)

The companion iso-studio scene composer (standalone app, local checkout X:\Forge\iso-studio) stages assets on a snap-to-grid isometric canvas with automatic depth sorting and a blockout-to-ControlNet export path. See §5 below for the launch command and status.

  1. Launch the app (§5), pick a projection, set tile width and grid extent.
  2. Import PNG/SVG/WebP by drag-drop, paste, or file picker; assets land in the tray.
  3. Place & snap with full / half / quarter / free snap modes; set each asset's anchor and footprint so snapping and sorting stay correct.
  4. Depth sorts automatically by (tileX + tileY), then elevation, then zBias, across ground / props / overlay layers.
  5. Export PNG at 1×/2×/4× (transparent, cropped) or save the scene as JSON conforming to the app repo's scene-schema.json (version "1.0").
  6. Blockout → ControlNet (v2 feature): place flat-shaded grey primitives and export a depth-map / lineart render that conditions the AI pipeline (Route C, step 3).

Route F — Source existing assets (licences)

Do not draw what you can legally reuse — but check the licence before delivery.

  1. CC0 first — Kenney iso packs, itch.io CC0 sets (Screaming Brain's 1,008 floors, etc.), OpenGameArt (asset-sourcing.md).
  2. Marketplaces — IconScout, Flaticon (attribution on free), Icons8, Streamline, Iconify, DrawKit, Blush, Storyset, Icograms.
  3. The procurement rule — before client delivery verify current plan + current licence + AI-training clause. "Commercial use permitted" ≠ "dataset use permitted" (DrawKit explicitly forbids AI training). Track attribution; prefer SVG source over PNG.

3. Scripts

All scripts follow the Skill Resource Protocol: stdout = data only, semantic exit codes, --help with EXAMPLES, --json envelopes. Pure-stdlib scripts run with python; Pillow scripts use PEP 723 inline metadata via uv run (on this Windows machine avoid the Store python3 stub — it exits 49).

ScriptWhat it doesLaunch
iso-math.pyCanonical constants, tile↔screen transforms, SVG grids, CSS/SVG/Illustrator/Figma transform recipespython scripts/iso-math.py constants --projection true --json · … to-screen 3 2 --tile-w 64 --tile-h 32 · … grid-svg --projection dimetric21 --tile-w 64 --extent 8 > grid.svg · … transforms --target css-3d
tile-validate.pyQA gate for (especially AI) tiles: dimension, alpha-halo, edge-bleed, anchor, palette checks; exit 10 on violationuv run scripts/tile-validate.py --tile-w 64 --tile-h 32 tiles/*.png
sheet-pack.pyPack a tiles directory into a spritesheet PNG + JSON atlas; --trim --padding N --pot, deterministic orderuv run scripts/sheet-pack.py tiles/ --trim --padding 2 --pot
check-iso-facts.py§7 staleness verifier: --offline asserts constants + reference citations; --live npm-checks named packages (exit 7 advisory / 10 drift)python scripts/check-iso-facts.py --offline

4. Assets

AssetWhat it isUse
prompt-library.mdReady-to-paste prompt scaffolds by target tool (city block, room cutaway, floating island, warehouse, control room, dashboard, sprite tileset, icon) + universal negative block + Midjourney/Firefly/Recraft/Flux cheatsheetsRoute C, step 2
palettes/three-tone-presets.json8 three-tone presets (kenney-prototype-grey, pastel-dollhouse, industrial-muted, cyberpunk-teal-violet, blueprint, earthy-game, mono-ink, brand-neutral); top-lightest verified by WCAG luminanceRoute A/B, style-guide.md
grids/Pre-generated true-iso-{32,64,128}.svg and dimetric-2to1-{32,64,128}.svg (line slope 0.5 dimetric / tan30° true iso)Route A, backdrops
blender-iso-rig.pyHeadless Blender ortho-rig + N-direction sprite baker + optional depth/normal passesRoute D, step 2
iso-studio (external)The zero-dependency scene composer — standalone repo at X:\Forge\iso-studio (github.com/0xDarkMatter/iso-studio), owns scene-schema.json + the asset library; pointer: iso-studio.mdRoute E, §5

5. iso-studio — the scene composer (standalone app)

iso-studio is a zero-dependency, no-build isometric scene composer that grew out of this skill and now lives in its own repository — local checkout X:\Forge\iso-studio, remote github.com/0xDarkMatter/iso-studio (index.html + server.mjs, no npm deps). Launch it, then work the docked palettes:

node X:\Forge\iso-studio\server.mjs      # then open http://localhost:4323
PORT=8080 node X:\Forge\iso-studio\server.mjs
  • Canvas + Grid — projection selector (2:1 dimetric / true isometric / custom angle), tile W×H (H is derived-and-locked for the two named projections), grid extent, and a full / half / quarter / free snap segmented control.
  • Asset tray — drag-drop, clipboard-paste, or file-picker import (PNG/SVG/WebP, stored as data URIs so scenes are self-contained); click-to-place, stays armed for rapid placement.
  • Depth sorting — automatic (x+y) → elevation → layer → zBias sort across ground / props / overlay, matching the doctrine in coordinates-depth.md exactly.
  • Inspector, Scene, Export palettes — anchor/footprint/elevation/scale/flip/zBias editing; background/checkerboard/canvas size; PNG export at 1×/2×/4× (crop-to-content, transparent), SVG export (gated — every placed asset must be SVG-sourced), and scene JSON save/load conforming to the app repo's scene-schema.json (version "1.0").
  • Blockout mode (signature feature) — place flat-shaded three-tone grey primitives (box / slab / ramp / cylinder) and export a depth map and a lineart render sized to the canvas; both condition the ControlNet step of the AI pipeline (ai-generation.md §4) without touching Blender.
  • Undo/redo (Ctrl+Z / Ctrl+Y, ≥50 steps, drag-moves and rapid nudges coalesced into single entries) and the full hotkey legend via ? in-app.

The full manual — workspace tour, projection/snap configuration, anchor-at-feet discipline, the complete hotkey table, the scene-JSON schema walkthrough, the blockout → depth/lineart → ControlNet round trip step by step, and a "known limits" section (depth export is per-instance flat grey, elevation-aware but not per-face; flipX mirrors a ramp's slope, no-op on symmetric primitives) — lives in the app repo at docs/MANUAL.md; this skill's references/iso-studio.md is the quickstart pointer.


6. Gotcha index — the top 10 footguns

#FootgunFixReference
1Mislabelled dimetric — calling 2:1 game tiles "isometric" and cutting them to a 30° grid; tiles don't tessellateGame tiles are 2:1 dimetric at 26.565°; write the exact angle into the tile specprojection-math.md §2/§4, tile-spec.md
2Skew without scale — shearing a flat asset onto the iso axes but skipping the cos(30°)=0.86603 vertical scale; right axes, wrong height, won't stackRun the full Scale → Shear → Rotate; build on the iso plane, never skew-and-call-it-doneprojection-math.md §3.2/§3.3
3Centre anchors break y-sort — a tall sprite's centre is high on screen, so a centre-based sort draws it behind nearer objectsAnchor every sprite at its visual feet (ground contact); the sort key uses the tile it stands oncoordinates-depth.md §8
4Elevation folded into the depth key — using x+y+z makes a tall near object sort behind a short far onez is a tie-breaker after (x+y), never added into it: (x+y, z, layer, zBias)coordinates-depth.md §5/§7
5Unity near-clip clipping — default near plane hides geometry behind the ortho camera on the XZ mapPush near clip to a large negative (−1000+); Stable Fit → Close Fit shadows; disable cascadesengine-integration.md §2
6Ortho frustum not recomputed on resize — the iso view stretches when the viewport changesRecompute aspect-scaled left/right/top/bottom in the resize handler; update the projection matrixthreejs-orthographic.md §3
7AI halo edges — semi-transparent fringe around AI-generated tiles that shows as a seamAlpha-threshold / defringe; tile-validate.py flags halo % mechanicallyai-refinement.md §3
8Licence AI-training clause — "commercial use OK" is not "dataset use OK"; DrawKit forbids AI trainingVerify plan + licence + AI-training clause before client deliveryasset-sourcing.md
9Flux-dev LoRA licences — Flux.1-dev derivatives are non-commercial; shipping them in a client project is a violationCheck each LoRA's licence flag; prefer permissively-licensed adapters for commercial workai-generation.md §2/§7
10Staircasing & the >4× upscale trap — 30° lines staircase in pixel art; pushing an upscaler past 4× invents perspective-breaking detailCommit to 2:1 pixel-neat stepping; above 4× regenerate at a higher base, don't upscalepixel-art-workflow.md §1, ai-refinement.md §1

Related skills

genart-ops (general three.js / creative coding) · threejs-ops (app/game-scale three.js: GLTF, r3f, InstancedMesh) · color-ops (colour science, OKLCH ramps) · frontend-design (production UI craft) · playwright-ops (headless render verification).

Signals

GitHub stars
36
Forks
5
Last commit
Aug 2026
Advanced
Catalog kind
skill
Gateway key
isometric-ops
Source
github.com/0xdarkmatter/claude-mods