# Render Pipeline Performance Implementation Plan > **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. **Goal:** Reduce repeated preview/render CPU work by caching decoded sources, skipping unused histogram work, deduplicating identical preview requests, and coalescing rapid preview updates. **Architecture:** The implementation keeps the existing preview/render feature set but tightens the hot path in layers. First, `source-loader` becomes safely shareable across abortable requests. Next, preview rendering becomes histogram-aware per caller instead of always-on. Then the worker client and hook layer collapse duplicate requests and rapid-fire updates toward the latest relevant preview. All work stays inside the current React/worker pipeline so the rollout is incremental and low-risk. **Tech Stack:** Next.js 16, React 19, TypeScript, Vitest, Web Workers, React Flow --- ### Task 1: Make source bitmap caching work for abortable preview/render requests **Files:** - Modify: `lib/image-pipeline/source-loader.ts` - Create: `tests/image-pipeline/source-loader.test.ts` **Step 1: Write the failing tests** Add tests that prove: - two abortable calls for the same URL reuse one underlying fetch/decode pipeline - an aborted consumer does not poison a later successful consumer - a failed fetch clears the cache so the next attempt can retry **Step 2: Run test to verify it fails** Run: `pnpm test tests/image-pipeline/source-loader.test.ts` Expected: FAIL because the cache currently bypasses any request with `signal`. **Step 3: Write minimal implementation** Refactor `loadSourceBitmap` so fetch/decode caching is keyed by `sourceUrl` regardless of `signal`, while each caller still gets local abort semantics. Keep failure cleanup behavior. **Step 4: Run test to verify it passes** Run: `pnpm test tests/image-pipeline/source-loader.test.ts` Expected: PASS **Step 5: Commit** Run: - `git add tests/image-pipeline/source-loader.test.ts lib/image-pipeline/source-loader.ts` - `git commit -m "fix(image-pipeline): reuse source bitmaps for abortable requests"` ### Task 2: Make preview histogram generation opt-in **Files:** - Modify: `lib/image-pipeline/preview-renderer.ts` - Modify: `lib/image-pipeline/worker-client.ts` - Modify: `lib/image-pipeline/image-pipeline.worker.ts` - Modify: `hooks/use-pipeline-preview.ts` - Modify: `components/canvas/nodes/adjustment-preview.tsx` - Modify: `components/canvas/nodes/compare-surface.tsx` - Modify: `components/canvas/nodes/render-node.tsx` - Modify: `tests/use-pipeline-preview.test.ts` **Step 1: Write the failing tests** Extend tests so they prove: - callers can disable histogram work and still get image output - histogram defaults remain available where explicitly needed - compare/fullscreen preview call sites request no histogram **Step 2: Run test to verify it fails** Run: `pnpm test tests/use-pipeline-preview.test.ts` Expected: FAIL because the preview API does not yet accept histogram control. **Step 3: Write minimal implementation** Add an `includeHistogram` option through preview renderer, worker request/response handling, and `usePipelinePreview`. Keep histogram enabled for `AdjustmentPreview`, disable it for `CompareSurface` and the fullscreen preview in `render-node`. **Step 4: Run tests to verify they pass** Run: `pnpm test tests/use-pipeline-preview.test.ts` Expected: PASS **Step 5: Commit** Run: - `git add tests/use-pipeline-preview.test.ts lib/image-pipeline/preview-renderer.ts lib/image-pipeline/worker-client.ts lib/image-pipeline/image-pipeline.worker.ts hooks/use-pipeline-preview.ts components/canvas/nodes/adjustment-preview.tsx components/canvas/nodes/compare-surface.tsx components/canvas/nodes/render-node.tsx` - `git commit -m "perf(image-pipeline): skip unused preview histograms"` ### Task 3: Deduplicate identical in-flight preview requests **Files:** - Modify: `lib/image-pipeline/worker-client.ts` - Modify: `tests/use-pipeline-preview.test.ts` - Create: `tests/image-pipeline/worker-client.test.ts` **Step 1: Write the failing tests** Add tests that prove: - two identical preview requests share one worker/fallback execution - a different preview width or histogram flag creates a separate request - aborted subscribers are removed without cancelling surviving identical consumers **Step 2: Run tests to verify they fail** Run: `pnpm test tests/image-pipeline/worker-client.test.ts tests/use-pipeline-preview.test.ts` Expected: FAIL because every preview call currently creates a fresh worker request. **Step 3: Write minimal implementation** Add an in-flight dedupe layer keyed by source URL, pipeline steps, preview width, and histogram flag. Keep abort handling per consumer so the shared underlying request only aborts when no consumers remain. **Step 4: Run tests to verify they pass** Run: `pnpm test tests/image-pipeline/worker-client.test.ts tests/use-pipeline-preview.test.ts` Expected: PASS **Step 5: Commit** Run: - `git add tests/image-pipeline/worker-client.test.ts tests/use-pipeline-preview.test.ts lib/image-pipeline/worker-client.ts` - `git commit -m "perf(image-pipeline): dedupe inflight preview requests"` ### Task 4: Coalesce rapid preview updates toward the latest state **Files:** - Modify: `hooks/use-pipeline-preview.ts` - Modify: `tests/use-pipeline-preview.test.ts` - Modify: `components/canvas/nodes/use-node-local-data.ts` - Create or Modify: targeted test covering rapid preview invalidation behavior **Step 1: Write the failing tests** Add tests that prove: - rapid sequential preview invalidations only commit the latest visible result - stale finished renders do not overwrite newer preview state - preview rendering is deferred/coalesced enough that intermediate slider churn does not fan out one render per transient value **Step 2: Run tests to verify they fail** Run: `pnpm test tests/use-pipeline-preview.test.ts` Expected: FAIL because preview work is only lightly delayed today and each update schedules its own render path. **Step 3: Write minimal implementation** Tighten preview scheduling so it is latest-only and non-urgent during rapid edits. Keep persistence behavior intact, but ensure preview updates collapse around the newest pipeline hash rather than every intermediate one. **Step 4: Run tests to verify they pass** Run: `pnpm test tests/use-pipeline-preview.test.ts` Expected: PASS **Step 5: Commit** Run: - `git add tests/use-pipeline-preview.test.ts hooks/use-pipeline-preview.ts components/canvas/nodes/use-node-local-data.ts` - `git commit -m "perf(image-pipeline): coalesce rapid preview updates"` ### Task 5: Verify the optimization stack end-to-end **Files:** - Verify only **Step 1: Run targeted pipeline tests** Run: `pnpm test tests/image-pipeline/source-loader.test.ts tests/image-pipeline/worker-client.test.ts tests/use-pipeline-preview.test.ts` Expected: PASS **Step 2: Run broader canvas/image regression coverage** Run: `pnpm test tests/light-adjust-node.test.ts components/canvas/__tests__/compare-node.test.tsx` Expected: `tests/light-adjust-node.test.ts` should pass. `components/canvas/__tests__/compare-node.test.tsx` is currently failing in the clean baseline with missing `CanvasGraphProvider`; if still failing for the same reason, document it as a pre-existing unrelated failure instead of treating it as a regression. **Step 3: Run lint on touched files** Run: `pnpm lint lib/image-pipeline/source-loader.ts lib/image-pipeline/preview-renderer.ts lib/image-pipeline/worker-client.ts lib/image-pipeline/image-pipeline.worker.ts hooks/use-pipeline-preview.ts components/canvas/nodes/adjustment-preview.tsx components/canvas/nodes/compare-surface.tsx components/canvas/nodes/render-node.tsx components/canvas/nodes/use-node-local-data.ts tests/image-pipeline/source-loader.test.ts tests/image-pipeline/worker-client.test.ts tests/use-pipeline-preview.test.ts` Expected: PASS **Step 4: Summarize verification evidence** Capture test/lint output and note any remaining pre-existing failures separately from new work.