Performance

Incremental scanning, hash-based caching, and route-level optimization for fast theme generation.

Overview

Nightfall is designed to be fast even on large applications. The first scan captures everything; subsequent scans only process what changed. On a typical 20-route app, incremental rescans take under 2 seconds.

Incremental Scanning

After the first full scan, Nightfall only re-scans routes where the visual output changed. It detects changes by comparing a hash of the visible DOM state:

# First scan: full (all routes)
npx nightfall-css scan --url http://localhost:3000 --routes / /about /pricing
# [scan] Scanning 3 routes (first run)...
# [scan] /         → 14 colors found (320ms)
# [scan] /about    → 8 colors found (180ms)
# [scan] /pricing  → 11 colors found (220ms)
# [scan] Total: 720ms

# Second scan: incremental (only changed routes)
npx nightfall-css scan --url http://localhost:3000 --routes / /about /pricing
# [scan] /         → unchanged (hash match, skipped)
# [scan] /about    → unchanged (hash match, skipped)
# [scan] /pricing  → 2 colors changed (190ms)
# [scan] Total: 190ms

Hash-Based Caching

Nightfall caches scan results in .nightfall/cache/. Each route gets a hash based on the visible DOM state. The hash includes:

  • Computed styles — All color-related CSS properties on every visible element
  • CSS variables — Values of all custom properties on :root
  • DOM structure — Element nesting that affects color relationships
.nightfall/cache/route-hashes.json
{
  "/": {
    "hash": "a3f8e2c1",
    "scannedAt": "2026-04-08T12:00:00Z",
    "colorCount": 14
  },
  "/about": {
    "hash": "b7d4f190",
    "scannedAt": "2026-04-08T12:00:00Z",
    "colorCount": 8
  },
  "/pricing": {
    "hash": "c2e5a3b8",
    "scannedAt": "2026-04-08T12:00:05Z",
    "colorCount": 11
  }
}

Route-Level Caching

Each route's scan data is cached independently. This means:

  • Parallel scanning — Multiple routes can be scanned concurrently using Playwright browser contexts
  • Partial invalidation — Changing one route only rescans that route, not the whole app
  • Cache sharing — Colors found on multiple routes are deduplicated in the final output

Cache Management

# View cache stats
npx nightfall-css cache --stats
# Routes cached: 12
# Total colors: 34
# Cache size: 48KB
# Last full scan: 2026-04-08T12:00:00Z

# Clear the cache (forces full rescan)
npx nightfall-css cache --clear

# Clear cache for a specific route
npx nightfall-css cache --clear --route /pricing

Parallel Route Scanning

By default, Nightfall scans up to 3 routes concurrently. Increase this for faster scans on large sites (at the cost of more memory):

# Scan 5 routes in parallel
npx nightfall-css scan --url http://localhost:3000 \
  --routes / /about /pricing /docs /blog \
  --concurrency 5

Generation Performance

Theme generation (the transform step) is fast because it operates on the color graph in memory, not on the DOM. Typical generation times:

  • Small app (10-20 colors) — Under 10ms
  • Medium app (20-50 colors) — Under 25ms
  • Large app (50-100 colors) — Under 50ms
  • Very large (100+ colors) — Under 100ms

The bottleneck is always the scanning step (browser launch + page rendering), not the transformation.

Watch Mode Optimization

In watch mode, Nightfall keeps the Playwright browser open between rescans, eliminating the browser launch overhead (typically 500-1000ms):

# Watch mode keeps browser warm
npx nightfall-css watch --url http://localhost:3000

# First scan: ~1200ms (includes browser launch)
# Subsequent rescans: ~200ms (browser already running)
# Incremental rescans: ~100ms (only changed routes)