Skip to content
Stipple
Esc
↑↓navigate↵open⌘Jpreview
On this page

Patterns and fields

How a field and a pattern turn into dots, and the built-in ones.

A dither is two functions meeting at every cell. The field answers “how much ink here?” with a tone from 0 (paper) to 1 (ink). The pattern answers “how much ink does this cell need before it turns on?” with a threshold. The cell is ink when the tone is above the threshold.

Patterns

Pattern Look
DitherPatterns.Bayer2 Coarse four-step grid. Strong, graphic.
DitherPatterns.Bayer4 The default. Sixteen steps, a crosshatch feel.
DitherPatterns.Bayer8 Sixty-four steps and the smoothest ramps.
DitherPatterns.BlueNoise Irregular and grain-like, with no repeating texture. A 64 by 64 texture made offline.

A pattern implements DitherPattern.threshold(x, y), returning a value between 0 and 1 for any integer cell coordinates. You can write your own.

Fields

A field is a DitherField: tone(x, y, t, aspect). x and y run from 0 to 1 across the element, t is time in seconds and aspect is width over height. Fields are pure, so they work in common code and are easy to test.

Built-in Notes
solid(tone) The same tone everywhere.
linear(from, to, angleDegrees) A ramp that spans the element corner to corner at any angle.
radial(inner, outer, centerX, centerY, radius) A round gradient.
noise(scale, speed, level, contrast) Soft, drifting value noise.
waves(count, speed, angleDegrees, low, high) Diagonal bands that travel.
ripples(rings, speed, centerX, centerY, radius) Rings that spread and fade.
sweep(progress, softness) A soft edge that moves from left to right. progress can be a () -> Float that is read when the field is drawn.
dissolve(progress, scale, softness) A reveal in an irregular order: 0 everywhere at progress 0, 1 everywhere at 1. Use it with ditherMask.
mesh(tones, drift, speed) A grid of tones blended smoothly, for a soft surface with several highlights.

Static fields ignore t. Animated ones are frozen at the modifier’s stillTime when animation is off or reduced.

Mesh gradients

DitherFields.mesh takes a grid of tones, one list per row from top to bottom and one tone per column from left to right, with 2 to 8 rows and columns. The control points sit on a regular grid with the first and last at the edges, and each grid cell blends its four corners smoothly. A control point has exactly its tone, and no tone goes outside the range of the ones you gave.

val aurora = DitherFields.mesh(
    tones = listOf(
        listOf(0.1f, 0.5f, 0.9f, 0.4f),
        listOf(0.6f, 1f, 0.3f, 0.8f),
        listOf(0.2f, 0.4f, 0.7f, 0.1f),
    ),
    drift = 0.1f,
)
Box(Modifier.fillMaxWidth().height(280.dp).dither(aurora, animated = true, stillTime = 0.5f))

With a drift above 0 the interior points wander at speed cycles per second while the edge points stay put, so the surface moves and always covers the element. The drift needs animated = true on the modifier. A mesh has a shader, and changing its tones or drift does not compile a new program, so it is cheap to animate or to edit live.

Was this page helpful?