The repair ladder
Each uploaded image is downscaled to at most 1600 pixels on its longest side, then run through up to 7 enhancement passes. Every pass is tried against two independent decoder engines (jsQR, then the ZXing library in try-harder mode) before moving to the next pass, so the pipeline attempts up to 20 decodes in total and stops the instant one succeeds:
- Original image. A plain decode attempt first, in case nothing is actually wrong.
- Grayscale. Converts to luminance only. Fixes color QR codes and tinted screenshots that confuse a decoder expecting plain black and white.
- Grayscale + contrast x1.8. Stretches values away from mid-gray. Fixes faded prints and low-contrast color schemes.
- Sharpen + contrast x1.5. A 3x3 unsharp-mask kernel followed by a smaller contrast boost. Fixes slightly out-of-focus or motion-blurred photos.
- Adaptive threshold. Converts each pixel to pure black or white based on the average brightness of its local neighborhood, rather than one global cutoff. Fixes QR codes under uneven lighting or blended into a busy background.
- Threshold sweep (96, 128, 160, 192). Tries four fixed black/white cutoffs in sequence. Catches images whose overall brightness sits outside what the earlier passes handle.
- Inversion + contrast x1.5. Flips black and white, then boosts contrast. Fixes QR codes designed light-on-dark, which some decoders read incorrectly by default.
Running two engines matters because jsQR and ZXing binarize and locate the QR's finder patterns differently, so an image that stumps one sometimes decodes cleanly on the other. The decoder attempts panel above the upload box lists exactly which pass and engine produced (or failed to produce) a match for your image.
Advertisement
A real before and after
The pair below is a genuine run through this pipeline, not a mockup. The left image is a QR faded and blurred enough that a raw decode attempt fails. The right image is the exact same file after step 3 (grayscale, then contrast x1.8) runs on it, at which point it decodes successfully. The two images look almost identical to the eye. That gap between what looks readable and what a decoder can actually parse is the entire reason an automated multi-step pipeline is useful: a fix that is invisible to a human eye can be the difference between a failed and a successful scan.


Frequently Asked Questions
What kinds of damaged QR codes can this fix?
Faded prints, blurry or angled photos, uneven lighting, color casts, and QR codes designed light-on-dark. It cannot recover a code that is physically missing too much data. QR codes are generated with Reed-Solomon error correction (7 to 30 percent depending on level), and once damage plus visual distortion exceeds that budget, no amount of image enhancement brings the data back.
Why do you run two different decoder engines?
jsQR and the ZXing library implement their own binarization and pattern-finding logic, so they fail on different images. Trying both roughly doubles the chance any given enhancement pass succeeds, at the cost of a bit more processing time per pass.
Does cropping the image help?
Yes. Tighter cropping around the QR code, before uploading, reduces the area the decoder has to search and removes background clutter that can confuse pattern detection, especially for photographed or artistically blended codes.
What if none of the 20 attempts work?
The tool shows how many passes it tried and suggests cropping closer, retaking the photo straight on with even lighting, or uploading a higher-resolution version. If the code is simply too small or too damaged in the original photo, a better photo is the only real fix.
Is this the same tool as the QR scanner?
No. The scanner at /scanner is built for quick camera and upload scans with a lighter 3-pass enhancement and a URL safety check. This tool runs the full 7-step, 2-engine repair ladder for images the scanner could not read.
Is my image uploaded anywhere?
No. Every enhancement pass and every decode attempt runs in your browser using canvas and JavaScript. The image never leaves your device.