Skip to content
pixi-silk
GitHub repository
Paint

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.

Switch the colour space and the easing.
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#

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

Switch between mesh and linear. Drag a yellow handle up or down.
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#

Turn dither off, then on. Reveal multiplies the contrast by 8.

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

Go deeper

An experiment on this page

Full-screen scenes. Each one also opens in Studio with its source.