Gradients
Linear, radial, conic, mesh and along-the-path gradients in OKLab with smooth easing, and half-float ramps with dither that stop dark gradients banding.
Updated pixi-silk 1.0.1View as Markdown
A gradient goes anywhere a colour goes: fill(gradient), stroke({ gradient }), or
fill({ gradient, color }), where color tints the ramp.
const SUNSET = linear([0xff375f, 0xffd60a, 0x0a84ff], { from: [0, 0], to: [1, 1], easing: 'smooth' });
g.roundRect(0, 0, 240, 120, 24, 0.6).fill(SUNSET);
That fills a card with a diagonal three-colour ramp. Build a gradient once and reuse it: each new gradient object bakes a new ramp.
Pick a kind#
linear(stops, { from: [0, 0], to: [1, 1] }); // diagonal across the shape's box
vertical(stops); // top to bottom of the shape
horizontal(stops); // left to right
radial(stops, { center: [0.3, 0.3], radius: 0.8, innerRadius: 0.2 });
conic(stops, { startAngle, sweep }); // around a centre
along(stops); // along a stroke, first point to last
On an arc or a sector, conic() spans the arc by default, so a ring’s ramp runs from cap to cap.
along() works on strokes of lines, polylines and paths.
Write the stops#
linear([0xff375f, 0x0a84ff]); // spread evenly
linear([[0, 0xff375f], [0.3, 0xffd60a], [1, 0x0a84ff]]);
linear([[0, 0xffffff, 1], [1, 0xffffff, 0]]); // [offset, color, alpha]
linear([{ offset: 0.5, color: '#30d158', alpha: 0.5 }]);
linear([[0, RED], [0.5, RED], [0.5, BLUE], [1, BLUE]]); // a hard stop
Offsets are clamped to 0 to 1 and made non-decreasing, as in CSS.
Options#
| Option | Default | What it does |
|---|---|---|
space | 'oklab' | Interpolation space: 'oklab', 'linear' or 'srgb' |
easing | 'linear' | 'smooth' runs a monotone spline through the stops. A function (t) => number remaps the ramp. |
extend | 'pad' | Outside 0 to 1: hold the end colours ('pad'), tile ('repeat') or mirror ('reflect') |
units | 'shape' | 'shape' measures points in fractions of each shape’s box. 'local' uses the graphics’ local units. |
OKLab is a perceptual space. A red-to-green ramp passes through yellow instead of a muddy brown,
and lightness changes evenly. Use 'linear' for light falloff and 'srgb' to match CSS.
Interpolation is premultiplied, so a colour fading to transparent never darkens.
easing: 'smooth' removes the bright and dark lines the eye sees at each stop of a linear ramp. It
never overshoots the stop colours.
With units: 'shape', one gradient fits every shape it paints. With units: 'local', one ramp
spans many shapes, such as the bars of a chart or a row of pills.
Mesh gradients#
import { mesh } from 'pixi-silk';
const beam = mesh(3, 3, [
[0xffd9ec, 0], [0xffd9ec, 0], [0xffd9ec, 0],
[0xffd9ec, 0.1], [0xffd9ec, 0.4], [0xffd9ec, 0.1],
[0xffd9ec, 0.5], [0xffd9ec, 1], [0xffd9ec, 0.5],
]);
g.rect(0, 0, 380, 220).fill(beam);
mesh(cols, rows, colors) takes a grid of control colours, row by row from the top left. Each is
a colour or [color, alpha]. The grid blends into one smooth surface in OKLab that passes through
every control colour. A grid has at most 16 by 16 colours.
The beam above fades towards the top and towards both sides, which no linear gradient can do. Pass
from and to with units: 'local' to share one mesh across several shapes.
Banding#
A dark, wide gradient has only a few 8-bit steps to cover a lot of pixels, so you see bands. Silk bakes each ramp into a shared 256-wide half-float atlas and adds about half a step of noise per pixel when it samples. The noise breaks the bands up below what the eye picks out.
Dither applies to gradients and blurs, never to flat colours. Its strength is the per-object
dither setting, in 8-bit steps: 1 by default, 0 to turn it off. Turn it off when you read pixels
back and need exact values.
API reference: linear, vertical, horizontal, radial, conic, along, mesh, Gradient, MeshGradient
An experiment on this page
Full-screen scenes. Each one also opens in Studio with its source.