How the 4K image upscaler works
Everything happens on your own device: your browser reads the image, your graphics chip enlarges and sharpens it, and the result is saved to a file you download. No part of the image is sent to a server.
In plain language
A normal resize spreads each pixel over a larger area, which blurs edges. 4K Images looks at the direction of edges in the image and enlarges along them, so lines and outlines stay crisp. It then sharpens where there is real contrast and leaves smooth areas such as skies and skin alone, so noise is not amplified.
Optional cleanup smooths sensor noise and the blocky artifacts of heavily compressed JPEGs while keeping edges intact. It helps most with phone photos and images saved at low quality.
What it does not do: it does not invent detail, or bring back detail that is not in the original. A blurry or very small photo becomes larger and cleaner, not more detailed. It uses classic image-processing algorithms only; there is no machine-learning model involved.
The technical pipeline
Each image is processed in a Web Worker, off the page's main thread, so the page stays responsive. (Safari versions older than 16.4 cannot decode images inside a worker, so there the decoding step runs on the page.)
-
Decode
The browser decodes the file with createImageBitmap, applying the EXIF orientation so photos come out the right way up. The engine notes whether the image has transparency.
-
Optional cleanup
The engine first measures how noisy the image is, then scales the filter to it, so clean images keep their fine texture. Low runs an edge-preserving bilateral filter (5×5); High uses a 7×7 bilateral filter plus a deblocking pass on the 8×8 JPEG block grid. Both are WebGL2 fragment shaders. Off skips this step.
-
Edge-adaptive upscaling
EASU (Edge-Adaptive Spatial Upsampling) from AMD FidelityFX Super Resolution 1 enlarges the image in passes of at most 2×, and the last pass lands exactly on the target size. Large images are processed in overlapping tiles so graphics memory limits are never exceeded. Without WebGL2, the engine falls back to a Lanczos3 resize (pica).
-
Sharpening
RCAS (Robust Contrast-Adaptive Sharpening) runs after every enlargement pass, while edges are still narrow, and adds definition in proportion to local contrast. Its limiter prevents halos, and flat areas are left alone so they do not turn grainy. The Sharpness slider sets its strength; 0 turns it off. Without WebGL2, an unsharp mask with a threshold is used instead.
-
Optional colour & light
White balance (grey-world and white-patch estimates from a reduced copy of the image), levels, exposure in EV steps, shadows, highlights, vibrance (which protects skin tones), saturation and clarity (local contrast from a wide blur). All of it is done in linear light on the graphics chip, after sharpening. Everything is off by default; the preview in the Colour panel runs the same maths on a smaller copy.
-
Pixel-art mode
For sprites and pixel art, the steps above are replaced by whole-pixel enlargement: Scale2x/Scale3x (EPX) for up to three passes, then nearest neighbour, so edges stay hard and no new colours are added. The result is saved as PNG. The engine suggests this mode when an image has few colours and hard edges.
-
Encoding
The result is saved with codecs from the Squoosh project compiled to WebAssembly (via jSquash): MozJPEG, libwebp, libavif and OxiPNG. Only the codec for the format you pick is downloaded. If a codec cannot run, the browser’s own encoder is used instead.
How the engine numbers are measured
Every number in the Inside the engine section is produced by a script in the project’s repository that runs the real engine in a browser and writes the results to a data file. The site is built from that file, and the build fails if the engine’s code has changed since the numbers were measured, so they always describe the code you are running.
The current numbers were measured on 2026-09-25 with Chromium 153.0.8010.12 on a Intel(R) Core(TM) i7-9700K CPU @ 3.60GHz with ANGLE (AMD, AMD Radeon RX 5500 XT (0x00007340) Direct3D11 vs_5_0 ps_5_0, D3D11).
- Speed: a 1920 × 1080 image enlarged to 3840 × 2160 with default settings and saved as JPEG; the median of 5 runs after a warm-up.
- Quality (SSIM, PSNR): Each sample is reduced to half size with Lanczos3, enlarged back ×2, and compared with the original (PSNR on RGB, SSIM on luma, 8×8 windows). The samples are the three CC0 demo photos.
- Cleanup: the standard deviation of pixel values on a flat patch of a synthetic noisy test image, enlarged to 4K with cleanup Off, Low and High.
- White balance: a synthetic scene with a warm cast (red ×1.25, blue ×0.7); the CIEDE2000 difference between its grey patch and true grey, before and after automatic white balance.
- Exposure: the ratio of linear brightness on a mid-grey patch at +1 EV and at 0 EV.
- Pixel art: the number of colours in the enlarged output of a 32 × 32 test sprite that were not in the sprite.
Timings depend on the device: a phone or an integrated graphics chip is slower than the desktop graphics card above.
Open-source credits
- AMD FidelityFX Super Resolution 1 (EASU, RCAS) MIT
- jSquash (MozJPEG, libwebp, libavif, OxiPNG) Apache-2.0; codecs under BSD, IJG and MIT licences
- pica MIT
- fflate MIT
- Demo photos: Wilfredor, Frank-2.0 and Jebulon on Wikimedia Commons CC0 1.0
- Inter, Inter Display, Space Grotesk and JetBrains Mono typefaces SIL Open Font License 1.1
- Lucide icons ISC
Full licence texts: third-party notices.