One window,
every control.
Pick a source, pick a target resolution, press Start. The output opens in its own window with a live HUD showing exactly what the GPU is doing.
Nothing ever
leaves the GPU.
ScreenCaptureKit delivers frames that are already GPU-resident, so captured pixels never make a round trip through system memory. Everything after that is Metal compute.
ScreenCaptureKit ──► IOSurface ──► MTLTexture (zero copy)
│
┌──────────────┴──────────────┐
│ optical flow │ once per captured pair
└──────┬───────────────┬──────┘
│ │
reprojection │ │ interpolation
▼ ▼
EASU ─► accumulate ─► RCAS generated frame
│
▼
aspect-correct present ──► CAMetalLayer
CSR Ultra
The default. Variance clipping in YCoCg instead of a min/max box, so one outlier pixel cannot let stale history through; a hue-preserving clip toward the current colour; and anti-flicker weighting. A better safeguard means it can accumulate harder.
CSR Temporal
Motion-driven history reprojection with neighbourhood rectification and disocclusion rejection. Kills the shimmer and crawl a purely spatial upscaler shows on text and thin UI lines — the dominant artefact on desktop content.
CSR Spatial
Edge-adaptive spatial upsampling, both stages: a 12-tap neighbourhood with four overlapping 5-tap crosses and the kernel rotated onto the detected edge, then contrast-adaptive sharpening with a per-channel limiter that provably keeps the result in range.
Frame generation
Optional 2× or 3×, driven by the same optical-flow field the temporal path uses. Sharing one flow result is why 3× costs barely more than 2×. Off by default — it interpolates, so it adds latency.
Fallbacks that cannot fail
If a backend will not initialise, CSR walks down to Apple's MetalFX spatial scaler, then to an MPS resample that works on any Metal device. You always get a picture.
Zero-copy capture
Captured frames arrive as IOSurfaces that are already GPU-resident and are wrapped as Metal textures directly. Pixels never make a round trip through system memory on their way into the upscaler.
4K, in eight
milliseconds.
Mean GPU time per frame on an Apple M1 (8-core GPU), 60
iterations, measured by the app's own --selftest.
| Stage | 1080p → 4K | 1440p → 4K | 720p → 1080p |
|---|---|---|---|
| CSR Ultra | 8.0 ms · 125 fps | 7.6 ms · 133 fps | 1.9 ms · 526 fps |
| CSR Temporal | 7.7 ms · 130 fps | 7.2 ms · 139 fps | 1.8 ms · 545 fps |
| CSR Spatial | 5.3 ms · 190 fps | 4.7 ms · 211 fps | 1.2 ms · 818 fps |
| MetalFX | 3.6 ms · 281 fps | 3.6 ms · 280 fps | 0.9 ms · 1066 fps |
| Metal GPU | 2.1 ms · 487 fps | 2.6 ms · 392 fps | 0.6 ms · 1614 fps |
| Optical flow (per pair) | 3.7 ms | 6.8 ms | 2.1 ms |
| Frame generation | 0.5 ms | 0.8 ms | 0.2 ms |
Cost scales with the output resolution, not the ratio — 1080p→4K and 1440p→4K cost the same, because both passes run at 4K. Choosing a lower target is the effective lever, not a smaller source.
What you need.
CSR is small, but it is a real-time GPU workload. The minimum column is what runs; the recommended column is what runs 4K comfortably.
One line.
Verifies the machine, checks the download against its published SHA-256, installs to Applications, and runs the built-in self-test to confirm the GPU pipeline actually works. No sudo, no Xcode.
curl -fsSL https://chopstickshq.com/csr/install.sh | bash
version.json on launch, verifies the zip SHA-256, replaces
the app, and restarts. Being offline stays silent.
Installed bundle, shaders included.
Dependencies. Apple frameworks only.
Telemetry. Only the update check leaves the Mac.