Upscale comparePro
An original and its upscale on one shared view: drag or key the divider, zoom and pan both sides together, and put the same spot from both images side by side in a split lens with the pixel grid.
On this page
Installation
Usage
import { UpscaleCompare } from "@/components/ui/chatcn/pro/upscale-compare"
<UpscaleCompare before={original} after={job.result} progress={job.progress} onRetry={job.retry} />Pass both images with their intrinsic width and height; the labels show the sizes and the factor. Both sides share one zoom and pan, so the divider always compares the same spot. Once a pixel of the original covers 3 screen pixels it draws without smoothing, the way a viewer should, so what you see is what the model started from. Leave after out while the job runs: the after side shows the original softened behind the progress.
Examples
Split lens
The lens shows the original on its start half and the upscale on its end half, from the same region, so the seam lines up across both sources. From 8× it draws the source pixel grid on each half at that half's own resolution. It draws the images but never reads them, so it needs no CORS.
States
Props
| Prop | Type | Notes |
|---|---|---|
before, after | { src, alt, width, height } | after is optional while upscaling |
factor | number | For the label. Derived from the widths |
status | "ready" | "upscaling" | "error" | Default from whether after is set |
progress | number | 0 to 1 while upscaling. Shown as a percent and a thin fill |
error, onRetry | string, () => void | What failed and how to fix it, and the Retry action |
position, defaultPosition, onPositionChange | number | 0 to 100 from the start edge. Default 50 |
loupe, defaultLoupe, onLoupeChange | boolean | The split lens |
loupeZoom, loupeZoomLevels, loupeSize | number, number[], number | Default 4, [2, 4, 8] and 184 px |
beforeLabel, afterLabel | string | Default "Original" and "Upscaled" |
toolbar | boolean | Zoom buttons, percent and the lens toggle. Default on |
disabled | boolean | Hides the handle and stops every gesture |
Keyboard
| Key | Action |
|---|---|
← → | Move the divider 1 %. Mirrored in right to left layouts |
Shift + arrows, PageUp PageDown | Move it 10 % |
Home, End | All original, all upscale |
+, -, 0 | Zoom both sides in, out, or fit. With the lens open, + and - change its zoom |
L (hold) | Show the lens on the divider while held |
Shift + L | Keep the lens open; Esc closes it |
Accessibility
- The handle is a
role="slider"with a value text that names both sides: "Original 40%, upscaled 60%". - The images are decorative layers of one comparison; the labels carry the sizes and the factor. Give each image an
altthat describes the picture. - Progress is a
progressbar; a failed job is an alert with the fix and a Retry button. - Touch: drag along the image to move the divider, pinch to zoom, drag to pan once zoomed. Vertical swipes still scroll the page until you zoom.
- Under forced colors the lens hides and a real outline keeps the frame.
Motion
- Click to jump glides the divider in 180 ms; dragging and keys move it at once.
- Zoom buttons and double click animate the shared view for 200 ms. Wheel, pinch, drag and keys track the input directly.
- The lens opens from 0.96 with a 150 ms fade under the pointer, and instantly from the keyboard.
- Under reduced motion only the fades remain.