Seeing what your agent broke.

npm i usephantom

Phantom takes a snapshot of your DOM before an AI agent makes changes, then another after. It diffs every element's position, size, color, text, and styles, and renders ghost overlays showing exactly what moved, what broke, and what changed.


Live demo

Click "Snapshot" to capture the before state. Then click "Agent edit" to simulate changes. Phantom shows the diff.

localhost:3000/dashboardInteractive

Monthly Revenue

$124,500 — up 12%

Active Users

8,241 daily active

Export ReportView Details
Take a snapshot, then simulate an agent edit to see the diff

Seven diff types

Every change gets classified with severity (low/medium/high) based on visual impact:

movedElement shifted position (≥2px)
resizedWidth or height changed (≥2px)
recoloredColor, background, or border changed
addedElement exists in after but not before
removedElement exists in before but not after
text-changedText content was modified
style-changedCSS property changed (font, padding, border, etc.)

Four overlay modes

👻 Ghost
Translucent "before" state overlaid on the current page. See where elements were before the agent moved them. Purple dashed borders with position labels.
🔥 Heatmap
Color-coded severity. Red = high impact (moved >20px, removed), amber = medium (resized, recolored), green = low (minor style changes).
📐 Outline
Wireframe view. Every changed element gets a colored border matching its diff type. Clean, non-intrusive. Good for scanning large pages.
🔬 X-ray
Side-by-side before/after for each changed element. Shows exact style values that changed. Most detailed visual mode.

Output

Four detail levels. The same diff rendered at each level:


Install

terminal
npm install usephantom -D
layout.tsx
import { Phantom } from 'usephantom'

function App() {
  return (
    <>
      <YourApp />
      {process.env.NODE_ENV === "development" && <Phantom />}
    </>
  )
}

Props

All optional. Works with zero configuration.

enabledEnable/disable (default: true)
mode"ghost" | "heatmap" | "outline" | "xray"
detail"compact" | "standard" | "detailed" | "forensic"
autoIntervalAuto-snapshot interval in ms (0 = manual)
rootScope selector (default: "body")
ignoreSelectors to skip (default: [])
moveThresholdMin px to report as moved (default: 2)
resizeThresholdMin px to report as resized (default: 2)
colorAccent color (default: "#a855f7")
toolbarShow toolbar (default: true)
positionToolbar corner (default: "bottom-right")
onDiffCalled when diff computed (receives DiffResult)
onCopyCalled when copied (receives markdown)
onSnapshotCalled when snapshot taken (receives PageSnapshot)
badgeShow diff count badge (default: true)
shortcutSnapshot shortcut (default: "ctrl+shift+p")
classNameCustom class for overlay

Types

types
type DiffType = 'moved' | 'resized' | 'recolored' | 'added'
  | 'removed' | 'text-changed' | 'style-changed'

type DiffEntry = {
  type: DiffType; selector: string; tag: string
  description: string; severity: 'low'|'medium'|'high'
  before: Partial<ElementSnapshot> | null
  after: Partial<ElementSnapshot> | null
  distance?: number; sizeDelta?: { width; height }
}

type DiffResult = {
  before: PageSnapshot; after: PageSnapshot
  entries: DiffEntry[]
  summary: { total; moved; resized; recolored; added; removed; ... }
}

type OverlayMode = 'ghost' | 'heatmap' | 'outline' | 'xray'
type OutputDetail = 'compact' | 'standard' | 'detailed' | 'forensic'

Programmatic API

script.ts
import { takeSnapshot, computeDiff, formatDiffMarkdown } from 'usephantom'

// Capture before state
const before = takeSnapshot()

// ... agent makes changes ...

// Capture after state
const after = takeSnapshot()

// Compute diff
const diff = computeDiff(before, after)
// → { entries: [...], summary: { total: 4, moved: 1, ... } }

// Generate markdown
const md = formatDiffMarkdown(diff, 'detailed')
// → "# Phantom Diff Report\n4 changes detected..."

Exports: Phantom, takeSnapshot, computeDiff, formatDiffMarkdown, formatSummaryLine, DiffOverlay

Type exports: PhantomProps, PhantomState, PageSnapshot, ElementSnapshot, DOMRectData, StyleData, DiffResult, DiffEntry, DiffType, OverlayMode, OutputDetail


Keyboard shortcuts

Ctrl+Shift+PTake snapshot / compare
EscClose overlay

~6.6kb gzipped · zero dependencies · React 18+ · MIT