# Compare

> Provides the compare.ts CLI invocation pattern, semantic comparison prompt template, severity thresholds, and model selection guide for UI design comparison.

- Skill: `madappgang/compare` (Agent Skill)
- Install (CLI): `npx skillmds@latest add madappgang/compare`
- Raw SKILL.md: https://api.skillmd.com/api/skills/madappgang/compare/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: AI & ML
- Author: MadAppGang (https://skillmd.com/u/madappgang)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/madappgang/compare

---


# UI Comparison Skill

This skill documents how to use the designer plugin's comparison engine from
any agent context. It covers:

1. The `compare.ts` CLI invocation pattern
2. The semantic comparison prompt template
3. Severity thresholds reference
4. Tips for reducing false positives

---

## 1. compare.ts CLI Invocation

The comparison script is located at `${CLAUDE_PLUGIN_ROOT}/scripts/compare.ts`.
It performs deterministic pixel-level comparison: normalization, optional masking,
pixelmatch, and 3-panel diff image composition.

### Basic Invocation

```bash
bun "${CLAUDE_PLUGIN_ROOT}/scripts/compare.ts" \
  --ref "${OUTPUT_DIR}/reference-raw.png" \
  --impl "${OUTPUT_DIR}/implementation-raw.png" \
  --output "${OUTPUT_DIR}" \
  --width 1440 \
  --height 900
```

### Full Invocation with All Options

```bash
bun "${CLAUDE_PLUGIN_ROOT}/scripts/compare.ts" \
  --ref "${REFERENCE_IMAGE_PATH}" \
  --impl "${IMPLEMENTATION_IMAGE_PATH}" \
  --output "${OUTPUT_DIR}" \
  --width "${VIEWPORT_WIDTH:-1440}" \
  --height "${VIEWPORT_HEIGHT:-900}" \
  --threshold "${THRESHOLD:-0.1}" \
  --masks "${MASKS_JSON:-[]}"
```

### CLI Arguments Reference

| Argument | Type | Required | Default | Description |
|----------|------|----------|---------|-------------|
| `--ref` | path | YES | — | Absolute path to reference image |
| `--impl` | path | YES | — | Absolute path to implementation image |
| `--output` | path | YES | — | Output directory (created if not exists) |
| `--width` | integer | no | 1440 | Target viewport width in pixels |
| `--height` | integer | no | 900 | Target viewport height in pixels |
| `--threshold` | float | no | 0.1 | Per-pixel tolerance (0.0–1.0) |
| `--masks` | JSON | no | `[]` | Array of MaskRegion objects to exclude |

### Mask Region Format

Masks allow excluding dynamic regions (timestamps, ads, user avatars) from the diff.

```json
[
  { "x": 0, "y": 0, "width": 200, "height": 40, "reason": "dynamic timestamp" },
  { "x": 1200, "y": 800, "width": 240, "height": 100, "reason": "user avatar" }
]
```

Pass as a JSON string with shell quoting:
```bash
MASKS='[{"x":0,"y":0,"width":200,"height":40,"reason":"timestamp"}]'
bun "${CLAUDE_PLUGIN_ROOT}/scripts/compare.ts" ... --masks "${MASKS}"
```

### Output Files

The script writes to `${OUTPUT_DIR}`:

| File | Description |
|------|-------------|
| `reference-normalized.png` | Reference resized to viewport dimensions |
| `implementation-normalized.png` | Implementation resized to viewport dimensions |
| `diff-raw.png` | Raw pixelmatch diff output (diff pixels highlighted) |
| `diff.png` | 3-panel composite: reference \| implementation \| diff overlay |
| `pixel-diff.json` | Structured result (success or error schema) |

### pixel-diff.json — Success Schema

```json
{
  "success": true,
  "runId": "ui-val-20260303-143022-a1b2c3d4",
  "diffPixelCount": 12450,
  "totalPixels": 1296000,
  "diffPercentage": 0.96,
  "severity": "WARN",
  "artifacts": {
    "referenceNormalized": "reference-normalized.png",
    "implementationNormalized": "implementation-normalized.png",
    "diffImage": "diff.png"
  },
  "masksApplied": 0,
  "duration": 2340
}
```

### pixel-diff.json — Error Schema

```json
{
  "success": false,
  "runId": "ui-val-1709500000000-a1b2c3d4",
  "error": "Reference file not found: /path/to/ref.png",
  "diffPixelCount": 0,
  "totalPixels": 1296000,
  "diffPercentage": 0,
  "severity": "FAIL",
  "threshold": 0.1,
  "viewport": { "width": 1440, "height": 900 },
  "artifacts": {
    "referenceNormalized": "reference-normalized.png",
    "implementationNormalized": "implementation-normalized.png",
    "diffRaw": "diff-raw.png",
    "diffImage": "diff.png"
  },
  "masksApplied": 0,
  "duration": 12
}
```

### Exit Codes

| Code | Phase | Condition |
|------|-------|-----------|
| 0 | — | Success |
| 1 | validation | File not found, unsupported format, non-writable output dir |
| 2 | normalization | sharp decode/resize/write failure |
| 3 | comparison | pixelmatch failure |
| 4 | output | diff image composition failure |

Always read `pixel-diff.json` after the Bash call. Use the `success` field to branch:
- `success: true` → proceed with semantic analysis
- `success: false` → surface `error` message to user

---

## 2. Semantic Comparison Prompt Template

Answer this prompt yourself, after reading both normalized images. It categorizes
differences across 7 UI categories and outputs structured JSON.

### Prompt Template

```
Compare these two UI screenshots:
- Image 1: REFERENCE (design target)
- Image 2: IMPLEMENTATION (built result)

For each difference category below, report: severity (CRITICAL/HIGH/MEDIUM/LOW),
description, and specific location.

Categories:
1. Colors - backgrounds, text, borders, buttons
2. Typography - font family, size, weight, line height
3. Spacing - padding, margin, gap between elements
4. Layout - element position, alignment, order, missing/extra elements
5. Imagery - icons, images, illustrations (wrong/missing/extra)
6. Content - text content differences (if not due to dynamic data)
7. States - hover, disabled, active states incorrectly shown

Output as JSON:
{
  "overallScore": 0-10,
  "categories": {
    "colors": { "severity": "...", "issues": [...] },
    "typography": { "severity": "...", "issues": [...] },
    "spacing": { "severity": "...", "issues": [...] },
    "layout": { "severity": "...", "issues": [...] },
    "imagery": { "severity": "...", "issues": [...] },
    "content": { "severity": "...", "issues": [...] },
    "states": { "severity": "...", "issues": [...] }
  },
  "topIssues": [...top 3 critical issues...]
}
```

### Running It

```bash
# Write prompt to file for auditability
cat > "${OUTPUT_DIR}/semantic-prompt.txt" << 'PROMPT'
Compare these two UI screenshots:
... (full prompt text above)
PROMPT
```

Then read both images, reference first, and answer:

```
Read("${OUTPUT_DIR}/reference-normalized.png")
Read("${OUTPUT_DIR}/implementation-normalized.png")
```

`Read` renders a PNG into context as an image, so after these two calls both screens
are in front of you. Write your JSON answer to `${OUTPUT_DIR}/semantic-raw.txt` and
set SEMANTIC_DIFF from it.

If either Read fails, set
`SEMANTIC_DIFF = { "skipped": true, "error": "<which image> could not be read" }`
and produce a pixel-only report. Do not derive the semantic categories from the
pixel diff — a category verdict nobody looked at is exactly what this step exists
to prevent.

**Never route this through the claudish CLI.** `claudish` has no `--image` flag; unknown
flags are forwarded to `claude`, which has none either, so the comparison runs on the
prompt text with neither screenshot attached.

---

## 3. Severity Thresholds Reference

### Pixel Diff Severity

| Diff % Range | Severity | Interpretation |
|---|---|---|
| 0.0 – 0.5% | PASS | Visual match — differences within anti-aliasing noise |
| 0.5 – 2.0% | WARN | Minor differences — review before shipping |
| 2.0 – 10.0% | FAIL | Significant differences — requires fix |
| >10.0% | CRITICAL | Major deviation — implementation diverged from design |

### Semantic Issue Severity

| Severity | Definition |
|----------|------------|
| CRITICAL | Prevents user task completion or fundamentally misrepresents the design |
| HIGH | Significant visual deviation that users will notice immediately |
| MEDIUM | Visible difference that degrades fidelity but not functionality |
| LOW | Polish-level issue: minor spacing, subtle color, tiny alignment |

---

## 4. Preconditions for Semantic Analysis

There is no model to select. The only precondition is that both normalized PNGs
exist and are readable. If either is missing, set `SEMANTIC_SKIP=true` and log:

```
WARN: normalized images not produced. Semantic analysis skipped.
```

---

## 5. Tips for Reducing False Positives

### Use Masks for Dynamic Content

Dynamic content (timestamps, user data, ads, carousels) will always differ.
Mask these regions to prevent them from inflating the diff percentage:

```bash
MASKS='[
  {"x": 0, "y": 0, "width": 300, "height": 50, "reason": "dynamic user name"},
  {"x": 900, "y": 750, "width": 540, "height": 150, "reason": "ad banner"}
]'
```

### Adjust Threshold for Anti-Aliasing

The default threshold of 0.1 filters most anti-aliasing differences.
For fonts with heavy sub-pixel rendering, increase to 0.15–0.2:

```bash
--threshold 0.15
```

Use `--includeAA false` (the default) to exclude anti-aliased edge pixels
from the diff count.

### Match Viewport to Design Spec

Always run at the viewport size the design was created for:
- Desktop designs: `--width 1440 --height 900` (default)
- Mobile designs: `--width 375 --height 812` (iPhone 14)
- Tablet designs: `--width 768 --height 1024` (iPad)

Mismatched viewports cause normalization cropping that creates false positives.

### Disable Animations Before Capture

When capturing browser screenshots, disable CSS animations and transitions
before taking the screenshot to avoid motion blur artifacts:

```javascript
const style = document.createElement('style');
style.textContent = '* { animation-duration: 0s !important; transition-duration: 0s !important; }';
document.head.appendChild(style);
```

### Wait for Network Idle

Always wait for the page to reach network idle before capturing:
- Check `document.readyState === 'complete'`
- Add a 800ms stabilization delay after load
- For SPA routes, watch for the loading spinner to disappear before capturing

### Interpret Semantic Analysis Contextually

The AI semantic analysis may flag dynamic content differences (user names,
dates, random images) as "content" or "imagery" issues. Cross-reference with
pixel diff location to determine if the issue is structural or data-driven.

