make-look-scanned: turn a clean PDF into a convincing scan, from the CLI or in a browser tab
Makes PDFs look scanned (CLI or in the browser via WASM)
At a glance
- What is it?
- A Go CLI that rasterizes each page, applies skew, grain, paper tone and JPEG damage, and writes an image-only PDF. The same effect pipeline also runs client-side via WASM, but the two builds are not byte-identical.
- Who is it for?
- Use make-look-scanned when you need a reproducible, scriptable scan effect and can accept losing the text layer, or when you want the offline single-file browser build. Do not use it if downstream tooling must copy or search text in the result, if you need an ink signature or stamp, or if you cannot comply with AGPL-3.0 when redistributing the CLI.
- Can I use it commercially?
- Yes, with strict conditions. AGPL-3.0 is a network copyleft licence: if people use a modified version over a network, for example as a hosted service, you must offer them its source code under the same licence.
- Is it still maintained?
- Yes. The repository last received commits 103 days ago.
- What is it written in?
- Mainly Go, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 1, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What make-look-scanned actually produces, and who needs that
The README is blunt about the goal: take a PDF and degrade it to look like a physical scan of a printout. The effects listed are skew, grayscale, warm paper tone, scanner grain, defocus, edge shadow and JPEG compression artifacts. This is not a document-processing library with a scan mode bolted on. It is a single-purpose tool.
The people who want this are usually submitting a document to a portal that expects a scan, or producing test fixtures for an OCR or document pipeline that has to cope with real scanner output. The README frames the result as faithful to a basic scanner, and the important consequence is stated plainly: each page is rasterized to an image, run through the pipeline, and reassembled into a new image-only PDF, so the original selectable text is gone.
That is a design decision, not an oversight. A scanner produces pixels, so a convincing fake has to produce pixels too. If you need the text layer preserved, this is the wrong tool and no flag will fix it.
Rasterize, degrade, reassemble: the pipeline behind the effect
The architecture is visible in go.mod. github.com/gen2brain/go-fitz v1.24.15 wraps MuPDF and handles rasterization of the input PDF. github.com/signintech/gopdf v0.36.1 rebuilds the output document from the processed page images. github.com/BurntSushi/toml v1.6.0 reads the preset file. The effects themselves live in the repository's internal/ directory, and web/ holds the browser entry point.
The data flow is one page at a time: render the page to a bitmap at the configured --dpi (150 by default), push those pixels through the effect chain, encode the result, and append the image to the new PDF. Because go-fitz links MuPDF through cgo, the README notes the binary is self-contained and there is nothing to install at runtime.
The browser build breaks that flow in one place. MuPDF cannot compile to wasm, so the web version uses PDF.js to rasterize pages and hands the pixels to the same Go effects code compiled to wasm. The README states the output is visually equivalent to the CLI but not byte-identical, because PDF.js and MuPDF rasterize differently. Anyone who needs a hash-stable artifact across both builds should know that up front.
Installing make-look-scanned and producing a first scanned PDF
The README requires Go and a C toolchain, since go-fitz links MuPDF via cgo. There is no install command in the documentation; you build the binary from a checkout. The result is self-contained, so nothing else has to be present at runtime.
go build -o make-look-scanned .Run it with an input path and nothing else, and the default output lands next to the input with .scanned.pdf appended. The README gives in.scanned.pdf as the example result.
make-look-scanned in.pdfTo push the look harder, the README shows explicit knobs. Each numeric flag disables its effect at 0, so --skew 0 removes rotation entirely.
make-look-scanned in.pdf --noise 0.4 --skew 2.5 --jpeg-quality 30If you want a repeatable house style rather than per-run flags, define a preset in config.toml under $XDG_CONFIG_HOME/make-look-scanned/ (the README falls back to ~/.make-look-scanned/config.toml when XDG_CONFIG_HOME is unset). Keys mirror the flag names with underscores.
[presets.medium]
skew = 1.5
paper_tone = 0.6
noise = 0.2
blur = 0.6
edge_shadow = 0.3
jpeg_quality = 45Then select it by name. Precedence runs built-in defaults, then the selected preset, then explicit CLI flags, and the README says flags always win.
make-look-scanned --preset medium in.pdfFor the browser, the README offers two routes. The dev build needs network access for the PDF.js CDN and serves from web/ on port 8080. The single-file build writes dist/make-look-scanned.html, roughly 8 MB, which inlines the wasm, Go's runtime glue and PDF.js as base64 and opens directly in a browser with no server.
Determinism is the feature that separates it from one-off filters
Most tools in this space give you a slider and a download button. make-look-scanned derives its seed from the content hash of the input PDF, so the same file always produces the same scan. Same input plus the same seed yields a byte-identical PDF, according to the README.
That matters for two reasons. First, regenerating an artifact does not silently change it, which is useful when the output is checked into a test corpus. Second, when you do want variation, --seed N gives a different but still reproducible look. You are choosing between stable and deliberately varied, not between stable and random.
The limit is that determinism is a property of one build. The README's own note about PDF.js and MuPDF rasterizing differently means the byte-identical guarantee applies to the CLI, not across CLI and browser.
The text layer is gone, and that is the point
The clearest limitation is stated in the README rather than discovered later: the output is an image-only PDF and the original selectable text is gone. Search, copy-paste, text extraction and accessibility all stop working on the result. If a downstream step needs to read the text, you have to run OCR on your own output.
The effect also only does what a scanner does to a page. There is no mention of stamps, signatures, staple marks, fold lines or handwritten annotations anywhere in the documentation, so a document that needs to look signed is outside the scope of the tool. Nor is there any stated handling of very large documents; the README documents a --dpi flag but gives no guidance on memory or processing time at higher resolutions, and the material is silent on batch processing across many files.
One more boundary worth noting: the browser build is described as visually equivalent, not identical, so it is a preview and sharing path rather than a substitute for the CLI when the exact bytes matter.
How it compares with browser-based scan converters
The obvious alternative is a hosted web converter, the kind people reach for when they search for making a PDF look scanned online for free. Those tools take an upload and return a file, which is convenient and requires no toolchain. The difference in approach is not cosmetic: your document leaves your machine, the effect is usually a fixed recipe rather than named parameters, and there is no seed, so running the same file twice can produce different bytes.
make-look-scanned inverts each of those. The CLI is local and scriptable, the knobs are explicit flags with documented defaults, and the output is deterministic. The cost is that you build it yourself with Go and a C toolchain, and you accept the AGPL-3.0 obligations described below. The single-file browser build sits in between: it runs the same Go effects locally with nothing to serve, but it still pulls PDF.js, and the README notes the dev route needs the CDN.
Licence and the cost of keeping up
The project is AGPL-3.0, and the README explains why the CLI is bound to it: the binary statically links MuPDF through go-fitz, which is AGPL-3.0, so the combined binary is AGPL-3.0 and distributing it requires offering the corresponding source. The browser build does not include MuPDF, since it uses PDF.js under Apache-2.0. That distinction matters if you plan to ship the CLI inside a product. This is a description of what the README states, not legal advice; if redistribution is on your roadmap, have someone qualified read the licence.
On maintenance, the last push to the default branch was on 2026-06-21, and v1.1.0 was released the same day, following v1.0.0 on 2026-06-20. The repository is not archived. The upgrade surface is small: three direct dependencies in go.mod, and a preset file whose keys mirror flag names, so a renamed flag is the kind of change that would break a config.toml. Because output is deterministic, a dependency bump that alters rasterization would change your artifacts even though the flags did not change.
Editorial conclusion
Use make-look-scanned when you need a reproducible, scriptable scan effect and can accept losing the text layer, or when you want the offline single-file browser build. Do not use it if downstream tooling must copy or search text in the result, if you need an ink signature or stamp, or if you cannot comply with AGPL-3.0 when redistributing the CLI. Before adopting it, build it once with a C toolchain to confirm go-fitz links, run the same input twice and diff the two outputs to verify the determinism claim, and check whether the preset file is read from $XDG_CONFIG_HOME/make-look-scanned/config.toml or the ~/.make-look-scanned fallback on your machine.
Frequently asked questions
How do I make a photo look like it was scanned?
The README describes make-look-scanned as taking a PDF, not a photo, and degrading it to look like a physical scan of a printout. If your source is an image, you would first have to place it into a PDF before running the tool.
Is make-look-scanned safe to use?
The CLI runs locally and the README describes the binary as self-contained, with nothing to install at runtime. The single-file browser build also works offline because it inlines the wasm, Go's runtime glue and PDF.js, while the dev browser route needs network access for the PDF.js CDN.
How can I turn a photo into a scan?
make-look-scanned works on PDF input rather than photos; the README's usage line is make-look-scanned [flags] input.pdf. It rasterizes each page and applies skew, grayscale, paper tone, grain, blur, edge shadow and JPEG artifacts, so a photo would need to be wrapped in a PDF first.
How to make a photo look scanned on iPhone?
The README documents a Go CLI and a browser build via WASM, and mentions no iOS app. The browser build's single-file output, dist/make-look-scanned.html, opens directly in a browser, which is the only documented route that does not require building the CLI.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/overflowy-make-look-scanned)