AugmentClaude

Pixel Art Decoration Designer

Redesign and beautify pixel-art decorations for top-down game sprites.

Installation

  1. Make sure Claude is on your device and in your terminal.

    Skills load from ~/.claude/skills/ when Claude Code starts up — so you need it on your machine first. If you don't have it yet, install it once with the command below, then run claude in any terminal to verify.

    One-time setup
    npm i -g @anthropic-ai/claude-code

    Already have it? Skip ahead.

  2. Paste into Claude Code or into your terminal.

    This copies the whole skill folder into ~/.claude/skills/beautify-decoration-ivanwng97/ — the SKILL.md plus any scripts, reference docs, or templates the skill ships with. Safe default: works for every skill.

    Faster alternative (instruction-only skills)

    Skips the clone and grabs only the SKILL.md file. Don't use this if the skill ships Python scripts, reference markdowns, or asset templates — they won't be downloaded and the skill will fail when it tries to load them.

    Quick install (SKILL.md only)
    Sign up to copy
  3. Restart Claude Code.

    Quit and reopen Claude Code (or any other agent that loads from ~/.claude/skills/). New skills are picked up on startup.

  4. Just ask Claude.

    Skills auto-activate when your request matches the skill's description — no slash command needed. Trigger phrases live in the skill's own frontmatter; you can read them in the “What this skill does” section above.

Prefer to read the source first? Open on GitHub.

When Claude uses it

Iterate on the visual identity of a top-down pixel-art decoration (sprite + layout integration) in pixtuoid. Use when redesigning an existing decoration (pantry, lounge, meeting room, cubicle decor) or adding a new one. Captures the rebuild trap, the visual-verification loop, resolution constraints, sprite-format pitfalls, and the layout-integration checklist that we learned the hard way during the pantry beautify session.

What this skill does

beautify-decoration (v1)

A repo-specific iteration loop for visually redesigning a decoration in pixtuoid. Follow this when the user says "beautify X" or "make Y look better" — it short-circuits several rebuild traps and visual-design dead ends that aren't obvious from the codebase alone.

When to use

  • Redesigning an existing decoration sprite (pantry, lounge, meeting, cubicle decor)
  • Adding a new fixture (pendant lamp, water cooler, chalkboard, etc.)
  • User says "items look too small / don't read like X / blend together"
  • After making sprite edits and "I don't see any change"

The visual-iteration loop

1. Edit sprite OR layout
   ↓
2. cargo build --release --example snapshot
   ↓
3. ./target/release/examples/snapshot --cols 192 --rows 80 /tmp/snap.png
   ↓
4. .venv/bin/python3 scripts/crop-snapshot.py /tmp/snap.png --scale 3 -q <quadrant>
   (or skip the quadrant guessing: snapshot --crop-furniture pantry|couch|vending|
   printer|meeting|sofa|desk OR --crop-agent <label> renders a 40x24-cell window
   already centered on the target — no Python step)
   ↓
5. Read the cropped PNG → self-critique → back to step 1
   ↓
6. When happy, send to user with SendUserFile and short caption
   ↓
7. cargo build --release --workspace    ← rebuild the LIVE binary too
   ↓
8. Commit with iteration history (which designs were tried, why rejected)

The user is the final judge of "does it look like a fridge / coffee machine / etc." — but you should self-critique before sending. Three iterations of self-critique before bothering the user.

Step 7 is mandatory. cargo build --release --example snapshot does NOT rebuild the main binary. Users testing with ./target/release/pixtuoid run won't see sprite changes until the workspace is rebuilt. Forgetting this step is how "I changed the sprite but nothing happened in the live TUI" bugs get filed.

Step 8 is mandatory. Commit messages for sprite changes must include the iteration count and a one-line rationale for each rejected attempt. Future editors need to know which alternatives were explored — otherwise they'll re-try the same dead-end designs (the seated_sleeping sprite went through 4 iterations before reading correctly at scale).

Sharp edges (the things that wasted time during the pantry session)

1. The rebuild trap

  • cargo build --release --workspace does not rebuild examples. Use cargo build --release --example snapshot when iterating on examples/snapshot.
  • include_str! in crates/pixtuoid/src/tui/embedded_pack.rs bakes sprite files at compile time. A build.rs exists at crates/pixtuoid/build.rs that emits rerun-if-changed for every .sprite and pack.toml — so a sprite edit DOES trigger a rebuild now. If you added a new asset and edits still aren't being picked up, check that build.rs is matching its extension.
  • If unsure, verify with: strings target/release/examples/snapshot | grep "<some unique string from your sprite>".

2. Snapshot defaults hide the large sprite variants

examples/snapshot defaults to 192×80 cells → buffer 192×160. Several layouts (pantry, corridor appliances) have conditional variants based on room dimensions. Corridor items (vending machine, printer) only appear when walkway_h ≥ 9–10. Use the default --cols 192 --rows 80 to see everything.

Pantry-specific threshold: pantry_room.width >= 36 triggers the 32×10 sprite; below that, the 20×8 pantry_small.sprite is used. Threshold lives in crates/pixtuoid-core/src/layout.rs:compute().

3. Visual-inspection helper

The full PNG is too big to grok at a glance and too small at thumbnail. Crop the relevant quadrant with PIL:

from PIL import Image
img = Image.open('/tmp/snap.png')
w, h = img.size
# Pantry is bottom-left quadrant; adjust ratios for other zones:
#   meeting:  (0, 0, 0.30*w, 0.45*h)
#   pantry:   (0, 0.49*h, 0.30*w, h)
#   cubicle:  (0.30*w, 0, w, 0.55*h)
#   lounge:   pre-2026 retired; merged into cubicle band
crop = img.crop((0, int(h*0.49), int(w*0.30), h))
crop = crop.resize((crop.width*2, crop.height*2), Image.NEAREST)
crop.save('/tmp/crop.png')

Then Read the cropped PNG — you (Claude) can see PNG content via the Read tool.

PIL is available system-wide (installed via pip3 install --user --break-system-packages Pillow). If a fresh environment misses it, install once.

4. Resolution budget

  • Each sprite pixel ≈ half a terminal cell (half-block compression).
  • Subzones smaller than ~5 display cells wide blur into pixel noise — users can't read them.
  • Sub-pixel detail (a 1-cell handle, a 1-cell stripe) is invisible. Iterate on silhouette + color identity, not pixel polish.
  • A 32×10 sprite has only ~16 display cells of width. Three zones of ~5 cells each is the practical max for legibility. Drop items; don't shrink them.

5. Identity mistakes that look identical to each other

Symptoms of weak identity:

  • Transparent body (.): the wall color shows through, weakening the silhouette. Use a solid fill color for appliances.
  • All-dark appliances: a row of M-bodied items reads as "row of dark boxes." Give each appliance a distinct base color (e.g., w white fridge against M dark coffee machine + M dark microwave with q glass).
  • Symmetric H-frame on a white box → reads as washing machine, not fridge. Use asymmetric handles (single-side handle, or center-French-door pair).
  • Cyan + blue dispenser dots next to each other → reads as cyan-cyan because b is dark and gets dim. Space them out or use c + r.

6. Sprite-format pitfalls

  • Every row in a .sprite file must have exactly the same number of space-separated cells. Off-by-one is the most common bug.
  • Verify with: awk '/^@/{next}/^#/{next}NF{print NR": "NF}' crates/pixtuoid/sprites/default/foo.sprite — all NF values must match.
  • Or visualize packed rows: awk '/^@/{next}/^#/{next}NF{for(i=1;i<=NF;i++)printf "%s",$i;print " ["NF"]"}' foo.sprite.
  • Palette keys must be unique RGB (the per-agent recolor pass substitutes by RGB equality — see embedded_pack.rs header comment).
  • Reuse existing palette keys when possible; new keys go in crates/pixtuoid/sprites/default/pack.toml [palette] section.

7. Layout integration checklist

When a sprite changes size:

  1. Update the walkable-mask footprint in core::layout::build_walkable_mask — there's a per-WaypointKind match arm with hardcoded (w, h) tuples.
  2. If the obstacle is a non-waypoint (plant, wall decor, pod decor), update the corresponding mark_blocked call too.
  3. Run cargo test -p pixtuoid-core — the walkable_mask_is_fully_connected_across_buffer_sizes test catches mask/sprite mismatches by trying multiple buffer sizes and asserting BFS reach from the door.
  4. If the connectivity test fails on the smallest buffer (96×70), the sprite is too big for that pantry. Add a _small variant + conditional pick (see pantry_counter_size in SceneLayout for the pattern).
  5. Update animation list in crates/pixtuoid/sprites/default/pack.toml and embedded_pack.rs to include both foo.sprite and foo_small.sprite if you added a variant.

8. Live binary uses different binary than snapshot

./target/release/pixtuoid run uses the main binary. examples/snapshot uses its own binary. Both need cargo build --release --example snapshot (or cargo build --release --workspace --example snapshot) when iterating on snapshot — and cargo build --release is fine for the live TUI binary.

Self-critique checklist — MANDATORY before every SendUserFile

You must run this checklist explicitly before each SendUserFile in a beautify loop. State the result of each row in the message (✅/⚠️/❌). Fix any ❌ before sending; if you ship a ⚠️, call it out so the user knows the trade-off.

CheckWhat it means
Stranger-IDIf a stranger saw this with no context, would they identify each new element as the intended thing? Name each element explicitly.
Visually differsDiff is noticeable, not a sub-pixel tweak. If hash-identical to last attempt, you didn't actually rebuild.
Subzone widthEach new sub-element ≥ 5 display cells wide (horizontal cells = buffer px; vertical cells = buffer px / 2 due to half-block).
Color distinctnessNew elements use colors distinct from immediate neighbours.
cargo testConnectivity test passes (cargo test --workspace --features pixtuoid-core/test-renderer).
--debug-walkableRendered the overlay and visually checked no narrow / isolated walkable pockets near the new element.

Skipping this checklist defeats the point of the skill — the whole reason it exists is that past sessions shipped invisible / unverified changes.

Workflow when adding a NEW decoration

  1. Sketch the design as a list of cells per row (count exactly).
  2. Pick a palette: reuse pack.toml keys; only add new ones if necessary.
  3. Write the .sprite file; verify row widths with the awk command above.
  4. Add the include_str! line to embedded_pack.rs.
  5. Add the [animations.foo] block to pack.toml.
  6. Decide where it lives in the layout — add a Point placement in SceneLayout::compute.
  7. Add the obstacle footprint to build_walkable_mask (or a waypoint kind if it's interactive).
  8. Add a DrawableKind::Foo variant + paint_drawable arm if z-sorting matters.
  9. Run cargo test -p pixtuoid-core.
  10. Snapshot + iterate.

Recap of the pantry session (case study)

What we did: replaced the 20×8 pantry counter with a 32×10 design through 8 iterations:

  • v1–v3: Too crowded, 6 zones × 3 cells each = unreadable.
  • v4: Simplified to 3 zones (fridge / coffee / microwave-snacks) at 8/10/10 cells.
  • v5–v6: Tried adding detail (handle pairs, dividers). User said "no difference between v5/v6" — too subtle to read at scale.
  • v7: Discovered cargo build --workspace was not rebuilding the snapshot example, so v6 was never actually rendered. Fixed by adding build.rs.
  • v8: Color-coded for identity — solid WHITE fridge vs. dark coffee + dark microwave. Strong silhouette differentiation. (Honest self-critique: still looks washing-machine-y due to H-frame.)

Lessons: silhouette + color over detail, always rebuild the example explicitly, bump cols to 192 for the large variant.

Related skills