You lay a magazine page on the scanner glass, set 600 dpi, and the picture that comes back has a wavy plaid across the jacket that is not on the paper. Put the paper under a loupe and there is no plaid. There are rows of ink dots, evenly spaced, tilted at a fixed angle, growing and shrinking to make light and dark. The waves happened between the paper and the file.
They happened because the scanner brings a grid of its own. Its sensor samples the page at fixed intervals, and one regular grid sampling another of similar spacing produces a beat. Wikipedia files the effect under aliasing: a moire pattern is “a special case of aliasing, due to undersampling a fine regular pattern”. For two parallel gratings of pitch p and p + δp the light and dark bands land 2d = (p²/δp) + p apart, so the closer the two spacings, the wider and more obvious the pattern. Two rulers with slightly different marks do the same thing.
The standard advice is to blur the scan a little. It works in the narrow sense that the plaid fades and fails in every other sense: at 600 dpi the halftone dots are the same size as eyelashes, serifs and the weave of a coat, and a blur cannot tell them apart because it never looks at what it removes. You trade a pattern for a soft picture, sharpen it back, and the pattern returns with the detail.
There is a better handle. The screen repeats, and anything that repeats collapses in a frequency picture into a few bright spikes at one spacing and one angle. Measure those spikes, cut exactly those frequencies, leave the rest alone. The workbench below does that in this browser tab: your scan is read by the page itself, with no upload, no fetch and no request of any kind, which matters when the file is a client’s artwork or somebody’s family photographs.
Scan descreening workbench
A printed picture is not made of tones, it is made of dots on a grid, and a scan of it carries that grid as a moire pattern. Blurring hides it and takes the detail with it. This finds the grid in the frequency picture of the scan and cuts out exactly those frequencies. The file is read in this browser tab: nothing is uploaded, no server sees your scan.
or press Enter to pick one. It is read on your own machine and never sent anywhere.
Sample: a photograph screened here at four angles, the way a magazine page is printed.
The descreened picture, at working size.
Every bright dot is one frequency the print screen put into the scan. The warm patches are what is being cut out. The centre is the average brightness and is never touched.
Nothing has been measured yet.
| Screen | Ruling | Angle | Cell | Peaks | Fit |
|---|
Cutting a notch out of the spectrum takes some edge sharpness with it, that is what a notch does, so the sharpening is part of the method and not a garnish. The scan resolution is only used to turn the measurement into lines per inch, it changes nothing in the filter.
Two grids and one beat
Print has no grey ink. It has one ink and white paper, so it fakes grey by covering a fraction of the paper with dots. Classic amplitude modulated screening “produces a regular grid of dots that vary in size”, and the spacing of that grid is the screen ruling, in lines per inch. Newsprint runs around 85 lpi, a coated sheet on an offset press from 85 to 185 lpi.
A colour page carries four such grids, one per ink, deliberately rotated against each other: black at 45 degrees, magenta at 75, cyan at 15, yellow at 0. The three inks the eye notices sit 30 degrees apart because “interscreen moiré becomes more visible when the angles of any two screens are less than 30 degrees apart”, and black takes the 45 because “the screen pattern is least visible when rotated 45°”. Yellow goes in the 15 degree gap beside cyan, usually at “typically 108%” of the other rulings, since yellow moire is too pale to see. What interference remains is the rosette, a moire made deliberately fine enough to ignore.
Your scanner adds a fifth grid, and the sampling theorem is blunt about the consequence: a signal survives sampling only if the rate is more than twice the highest frequency in it, and “any frequency component above fs/2 is indistinguishable from a lower-frequency component, called an alias”. A 150 lpi screen scanned at 300 dpi sits exactly where that inequality must be strict and is not. The screen folds down to a low frequency, and a low frequency is a large, slow, obvious pattern. That is the plaid on the jacket.
A frequency picture, without the maths
Any square of pixels can be written as a stack of stripe patterns added together, each with a direction, a spacing and a strength, and the frequency picture (the two dimensional Fourier transform) maps that strength. The centre is the flat average of the tile, distance from the centre means finer stripes, direction from the centre is the direction the stripes run. A photograph makes a soft blob around the middle that fades outwards. A halftone screen does the opposite: repeating exactly, it appears as a handful of bright dots on a regular lattice, all at one distance from the centre (the ruling) and one angle (the screen angle), each with a mirrored twin opposite. Everything worth keeping is the blob, everything you want gone is the spikes.
The workbench builds that picture from luminance in 256 by 256 tiles, inside a worker thread that “can’t directly manipulate the DOM” and has no reason to. Each tile is multiplied by a periodic Hann window first, because a tile cut out of a picture has hard edges, and hard edges put a bright cross through the frequency picture that the print never made. The transform is a hand written iterative radix-2 FFT: bit reversal, then log2(N) butterfly stages against a precomputed twiddle table, rows and then columns. The magnitude of up to twelve tiles is then averaged: the picture’s detail differs per tile and averages down, the screen sits at the same coordinates in every tile and does not.
Then the envelope has to go, because a photograph’s spectrum falls off steeply from the middle and a real screen spike far out can be weaker than ordinary content near the centre. The tool divides the averaged magnitude by a heavily blurred copy of itself, the DC bin first replaced by the ring around it so the centre value does not smear through the blur. What remains is a flat floor with the spikes clear of it, and the threshold is mean plus four standard deviations of that floor, measured per image rather than hard coded.
Finding peaks is not finding a screen. Local maxima outside a protected radius near the centre (low frequencies are picture, not print) are reduced with Gauss’s algorithm to a candidate basis of two vectors, and a peak only counts if it is an integer combination of them. Those integer coefficients feed a 2×2 least squares refit, so ruling and angle come from twenty peaks at once instead of the brightest one. A second stage uses the fact above: separations share one ruling and differ only in angle, so a perpendicular pair at the same radius calls a screen from two peaks.
What the notch costs
Filtering multiplies the spectrum by 1 minus a clamped sum of Gaussian bells: one on every lattice point of every detected screen, one on every mirror of those points, plus the stray peaks belonging to no single lattice, the beats between separations. The spectrum goes back through the inverse transform, tiles are added at half tile hop, then divided by the accumulated window weights so the overlap does not band.
Removing frequencies is never free. The screen ruling is not a private lane: an edge is built from every frequency at once, so a notch at the screen frequency takes a slice out of every edge in the picture. Edges come out softer. That is what a notch is rather than a defect in the implementation, and it is why the sharpening afterwards slider is part of the method rather than a garnish. It runs 0 to 150 per cent, starts at 45, and the unsharp mask is sized to the halftone cell, so contrast returns at the scale the notch took it from. It is the slider to reach for when a result looks mushy, alongside the usual reasoning about why an image looks blurry and which cause it actually is.
The second cost is that you do not get all of it. Every screen frequency carries a scaled copy of the picture’s own spectrum as sidebands, so widening the notch until those vanish deletes real detail with them. On the built in sample the energy on the lattice falls by about 95 per cent, not 100, and the remainder is a faint weave visible at 100 per cent zoom. The notch radius slider (1 to 12 bins in half steps, seeded from the measurement) and the notch strength slider (0 to 100 per cent) let you pick your own point on that trade. The scan resolution field, 72 to 4800 dpi, only turns the measured spacing into a ruling in lines per inch.
Colour needs less of this than brightness
Your eye resolves brightness detail far better than colour detail: “the human vision system processes color information (hue and colorfulness) at about a third of the resolution of luminance”, and a whole compression habit rests on it. In 4:2:0 chroma subsampling the colour channels are halved horizontally, and being “only sampled on each alternate line in this scheme, the vertical resolution is halved” too.
Two things follow. The screen shows up far more strongly in brightness than in colour, which is why analysis and filtering run on luminance alone. And colour can be rebuilt more crudely, which is what the checkbox for bringing the colour back from a median pass does: a 3×3 median softened down to roughly one halftone cell removes the coloured fringes left by individual ink dots without anyone seeing the cost. Leave it off for artwork with fine coloured type, where a median at that scale eats the strokes.
What the workbench will not do
Pictures are worked on at 800 px on the long side, with the reported ruling scaled by the same factor, and the line under the source says which rulings are already gone from that copy. That is the first limit to understand: a screen finer than the working copy can hold is not in the data being filtered, and no notch reaches it. A picture smaller than one 256 px tile is padded by mirroring, and a mirrored screen is a screen at the mirror of its angle, an angle the printer never used, so only tiles at least half real picture are trusted, with a fallback to the whole grid when even that is impossible. Small crops measure worse than full pages.
Detection stops at three screens: a four ink page gets its three strongest separations, and the one dropped is normally yellow, which contributes least to luminance. That is usually the right three, but it is a cap, and where yellow carries weight you will see a little more residual weave.
It is a filter, not a reconstruction: it removes what the screen added and invents nothing, so detail smaller than a halftone cell was never in the scan and is not coming back. It expects amplitude modulated screening, dots of varying size on a regular grid. Frequency modulated or stochastic screening is the other method, and dots that do not sit on a regular grid put no spikes in the frequency picture, which leaves the lattice fit nothing to lock onto. A file that is not an image, or an image the browser cannot decode, produces a clear line of text and leaves the scan you already had in place; an empty file list does nothing. Silent failure on an unsupported format is not a feature, it is a bug with good manners.
Local processing, incidentally, is not the rare part. Most comparable web tools do post your file to a server: moireremoval.com charges credits per image and says images are “processed securely and automatically deleted after processing”, which is a promise about a server rather than the absence of one. But texloom.studio says its “free Quick mode runs entirely on your device”, using a “median-filter pipeline” there and a cloud model for its paid mode. The difference worth arguing about is measurement: a median removes everything at the scale it is set to, screen or no screen, while a fitted lattice touches only the frequencies the screen occupies. That is why the frequency picture is one of the three views rather than a hidden detail: switch to it and the notches are drawn where the spikes were.
Rescanning is still the cheapest fix
If you still have the paper, the best repair happens before the file exists. Sample at more than twice the finest thing you want to record: a 150 lpi page at 300 dpi aliases into waves, at 600 or 1200 the screen resolves as dots, and reducing the image afterwards with a decent filter averages those dots back into tone. It is the same arithmetic as deciding how big an image should be before you upload it, one step earlier. Scanner drivers offer the shortcut too: “some image scanner computer programs provide an optional filter, called a ‘descreen’ filter”. It acts on full resolution data before the scanner throws anything away, which is the advantage, and it is usually a blur applied before you see the result, which is not.
Rotating the original a few degrees on the glass changes the geometry between print grid and sensor grid, which moves the beat, sometimes to a spacing too fine to see. And if the page is one you designed, do not scan it at all: the file that went to the printer has no screen in it, since the screen is added at print time, one of several things that change when a design leaves the screen for paper.
Getting the result into WordPress
The download is a PNG, which the specification calls “lossless, portable, well-compressed storage”. That matters here: baseline JPEG “is split into blocks of 8×8 pixels” which are transformed and quantised, and “the quantization stage always results in a loss of information”, so re-encoding a descreened picture at mid quality lays a fresh periodic grid over the one you just removed. If the final file must be a JPEG, pick the quality from evidence on your own image, which is what the image quality curve is for.
Then the upload. Since version 5.3 WordPress scales large uploads down: big_image_size_threshold defaults to 2560, and “if the original image width or height is above the threshold, it will be scaled down”. A 600 dpi scan of A4 is several times over that, so decide the final pixel size yourself, and check where the upload limit really lives before a large file bounces. If the cropping and the last sharpening happen inside WordPress, WunderPaint works on the file already in the media library.
Moire in a scan is neither damage nor noise. It is arithmetic: two regular grids of similar spacing, one sampling the other, producing a third pattern that exists in neither. Every fix that works follows from that sentence. Change a grid, by rescanning higher or turning the page on the glass, and the beat moves or disappears. Remove the exact frequencies the screen occupies and the beat has nothing left to beat against. Blur everything at that scale and you have solved a different problem correctly.
Blur became the standard advice when a frequency picture was a specialist operation. It is not one now: a dozen 256 point transforms, a lattice fit and a filtered inverse take about a quarter of a second in a page you already have open. Measure first, remove only what you measured, and keep the measurement visible enough to argue with.
Expect an honest result rather than a miracle. The plaid goes, the picture comes back slightly soft and needs its sharpening, and a faint weave survives at full zoom because taking the last of it would take real detail too. If the paper is still on your desk, rescan it properly. If it is gone, the archive is what it is, and measuring the screen beats guessing at it.