Halftone gradient: how to turn a fade into dots
Turn a gradient into halftone dots in Photoshop's Bitmap mode, GIMP or Inkscape, or with a short script: how to size the dots and keep the fade even.
Published
A halftone gradient is a fade drawn in dots: large dots at the dark end, shrinking to nothing at the light end. In every program the method is the same two moves. Draw an ordinary gradient from black to white, then screen it, so that each dot takes its size from the grey underneath it.
| Program | Route | Dots are |
|---|---|---|
| Photoshop | Gradient, then Image > Mode > Bitmap with Halftone Screen | Pixels, pure black and white |
| GIMP | Gradient, then Filters > Distorts > Newsprint | Pixels |
| Inkscape | Gradient, then tiled clones with Trace | Vector circles |
| A script | Circles written straight to SVG | Vector circles |
Photoshop: Bitmap mode
This gives hard black dots with no grey edges. The menu steps are the ones in ScreenPrinting.com’s guide to converting a design into halftones in Photoshop, dated December 2022.
- Fill an image with a gradient from black to white, at the size you need.
- Choose Image > Mode > Grayscale.
- Choose Image > Mode > Bitmap. The guide notes that after this step “you won’t be able to make any changes to the image”, so finish the gradient first.
- Enter the output resolution. In the guide’s words, “the higher resolution you use for the conversion, the smoother the dot shape will be”.
- Choose Halftone Screen as the method and click OK.
- Enter the frequency in lines per inch, choose a round dot shape, enter the angle, and click OK. The guide uses an angle of 25 for screen printing film; the figure above is at 45.
Two sums help with the settings.
How many dots. The frequency is in lines of dots per inch, so a gradient 6 inches wide at 10 lines per inch has 60 dots along it, 2.5 mm apart: plain to see. ScreenPrinting.com gives 45 or 55 lines per inch as standard for film, where the dots are meant to be small. For a fade that is meant to read as a smooth tone in print, use the frequency your printer asks for.
How smooth. Each dot is built from pixels, and the number of different sizes it can take is roughly the square of the pixels across one cell. At an output resolution of 300 pixels per inch and a frequency of 10, a cell is 30 pixels wide: plenty. At 300 and 60, a cell is 5 pixels wide, the dots can take only about 25 sizes, and the fade moves in steps. Raise the resolution until a cell is at least 10 pixels wide. That is the same ratio ISO 12647-1, the printing standard, asks of a platesetter: an addressability “at least 10 times higher than the screen frequency”.
GIMP
The steps are from the GIMP 2.10 manual (Newsprint).
- Fill a layer with a black to white gradient.
- Choose Filters > Distorts > Newsprint.
- Set Color Model to Black on White.
- Set Pattern to Circle.
- Set Period, which the manual defines as “the number of pixels across one repetition of a base pattern”, and the Angle.
- For hard-edged dots, set the anti-alias oversampling factor under Quality to 1, which the manual says turns it off.
Inkscape
Inkscape makes the dots as vector circles from the start, with the Trace tab of the tiled clones dialog. The steps follow Tavmjong Bah’s guide to Inkscape 0.92 and 1.0 (Tiling: Trace). Vector halftone has the same route for a photo, and CorelDRAW’s.
- Draw a rectangle and fill it with a black to white gradient.
- Draw a small circle over its top-left corner and keep it selected.
- Open Edit > Clone > Create Tiled Clones and set the rows and columns, or the width and height, to cover the rectangle.
- On the Trace tab, tick Trace the drawing under the tiles, choose the colour or lightness as the input, and tick Size as the output. Use the invert option if the large dots appear at the light end.
- Click Create, then delete the rectangle.
Why the dots should follow the square root
If you build the dots yourself, by blend, by clones or by code, one detail decides whether the fade looks right. Tone is the share of the surface covered by ink, and a circle’s area goes with the square of its radius. For the tone to fall evenly, the radius has to follow the square root of the tone.
Make the radius fall in a straight line instead and the middle of the fade comes out too light: a dot at half the radius has only a quarter of the ink.
If a fade made with a blend or with tiled clones looks like this second figure, that is the likely reason. In Inkscape, the Trace tab has a Gamma correction for the input, which is the control to try.
This script writes the first figure’s kind of gradient as an SVG. Run as printed, it makes 476 circles, none of them touching. The largest dot covers 66% of its cell, so the fade runs from 66% ink to nothing. In a rendering of the result, cut into ten strips from left to right, the ink measured fell steadily from 62% to 3%, within two points of 66% times the tone in every strip.
const W = 200, H = 60, pitch = 5; // millimetres
const step = pitch / Math.SQRT2; // a 45 degree grid
const rMax = pitch * 0.46; // largest dot stops short of its neighbours
let dots = '';
for (let j = 0; j * step <= H; j++) {
for (let i = 0; i * step <= W; i++) {
if ((i + j) % 2) continue; // keep every other point: the grid turns 45 degrees
const x = i * step, y = j * step;
const tone = 1 - x / W; // 1 at the left edge, 0 at the right
const r = rMax * Math.sqrt(tone); // area follows tone, so radius follows its square root
if (r >= 0.3) dots += `<circle cx="${x.toFixed(2)}" cy="${y.toFixed(2)}" r="${r.toFixed(2)}"/>`;
}
}
const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="${W}mm" height="${H}mm" viewBox="0 0 ${W} ${H}">${dots}</svg>`;
Change the tone line to change the fade: 1 - y / H runs it from top to bottom, and 1 - Math.hypot(x - W / 2, y - H / 2) / (H / 2) makes it radial, with no dots drawn where the result is below zero.
Ready-made gradients
The free halftone set has these as finished vector files, in SVG and transparent PNG, with the area of every mark following the tone:
- the dot gradient, left to right on a 45° grid
- a fade from top to bottom on a straight grid
- a radial fade
- a hexagonal gradient, a square-dot gradient and a line gradient
When the fade looks wrong
| What you see | Cause | Fix |
|---|---|---|
| Steps or bands in the fade | Too few pixels in each dot | Raise the output resolution, or use larger dots |
| The dark end is solid black | The dots have merged | Start the gradient at a dark grey, not black |
| The light end stops suddenly | The smallest dots are under one pixel, or were removed in tracing | Use larger dots or a higher resolution |
| The middle looks too light | Dot radius falls in a straight line | Use the square root, or bend the input with a gamma or curve |