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.

A halftone gradient on a 45° grid. The area of each dot follows the tone, so the fade looks even from end to end.
ProgramRouteDots are
PhotoshopGradient, then Image > Mode > Bitmap with Halftone ScreenPixels, pure black and white
GIMPGradient, then Filters > Distorts > NewsprintPixels
InkscapeGradient, then tiled clones with TraceVector circles
A scriptCircles written straight to SVGVector 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.

  1. Fill an image with a gradient from black to white, at the size you need.
  2. Choose Image > Mode > Grayscale.
  3. 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.
  4. Enter the output resolution. In the guide’s words, “the higher resolution you use for the conversion, the smoother the dot shape will be”.
  5. Choose Halftone Screen as the method and click OK.
  6. 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).

  1. Fill a layer with a black to white gradient.
  2. Choose Filters > Distorts > Newsprint.
  3. Set Color Model to Black on White.
  4. Set Pattern to Circle.
  5. Set Period, which the manual defines as “the number of pixels across one repetition of a base pattern”, and the Angle.
  6. 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.

  1. Draw a rectangle and fill it with a black to white gradient.
  2. Draw a small circle over its top-left corner and keep it selected.
  3. Open Edit > Clone > Create Tiled Clones and set the rows and columns, or the width and height, to cover the rectangle.
  4. 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.
  5. 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.

The same grid with the radius falling in a straight line. Compare the middle with the figure at the top of the page.

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:

When the fade looks wrong

What you seeCauseFix
Steps or bands in the fadeToo few pixels in each dotRaise the output resolution, or use larger dots
The dark end is solid blackThe dots have mergedStart the gradient at a dark grey, not black
The light end stops suddenlyThe smallest dots are under one pixel, or were removed in tracingUse larger dots or a higher resolution
The middle looks too lightDot radius falls in a straight lineUse the square root, or bend the input with a gamma or curve