---
title: Patterns and fields
description: 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.

<iframe class="live-example" src="https://stipple-demo.vercel.app/?embed=1&animated=1#patterns" title="Live example: patterns" loading="lazy" allow="autoplay"></iframe>

## 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. |

<iframe class="live-example" src="https://stipple-demo.vercel.app/?embed=1&animated=1#fields" title="Live example: fields" loading="lazy" allow="autoplay"></iframe>

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.

```kotlin
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.

<iframe class="live-example" src="https://stipple-demo.vercel.app/?embed=1&animated=1#mesh" title="Live example: mesh" loading="lazy" allow="autoplay"></iframe>
