# Fills & strokes

> Paint styles, stroke alignment (inside, center or outside), fill and stroke without a seam, and hairlines that fade instead of breaking up.

Source: https://pixi-silk.schmooky.dev/docs/fills-and-strokes/

## Paint styles

`fill()` and `stroke()` accept a colour, a gradient or a style object:

```ts
g.circle(0, 0, 20).fill(0xff375f);                                   // any Pixi ColorSource
g.circle(0, 0, 20).fill('rgba(255, 55, 95, 0.5)');
g.circle(0, 0, 20).fill(linear([0x0a84ff, 0xbf5af2]));               // a gradient
g.circle(0, 0, 20).fill({ color: 0xff375f, alpha: 0.5, blur: 6 });   // FillStyle
g.circle(0, 0, 20).stroke({ width: 4, color: 0xffffff, cap: 'round', alignment: 'inside' });
```

| `FillStyle` | |
|---|---|
| `color` | any Pixi `ColorSource` (with a gradient, it tints the ramp) |
| `alpha` | multiplies the colour's alpha |
| `gradient` | a `linear`, `radial`, `conic` or `along` gradient |
| `blur` | gaussian standard deviation in local units ([Blur](/docs/blur-and-shadows/)) |

`StrokeStyle` adds `width` (a number, or an array for [tapering](/docs/lines-and-paths/#tapered-strokes)),
`alignment`, `cap`, `dash`, `dashOffset` and `dashFit`.

## Alignment

Live demo `alignment` (source):

```ts
import { Text } from 'pixi.js';
import { SilkGraphics } from 'pixi-silk';
import { defineDemo } from './runtime';

const MODES = ['inside', 'center', 'outside'] as const;

export default defineDemo({
    size: [480, 200],
    controls: {
        width: { type: 'range', label: 'stroke width', min: 1, max: 24, step: 1, value: 12, unit: 'px' },
    },
    setup({ stage, params, tick }) {
        const g = new SilkGraphics();

        stage.addChild(g);
        MODES.forEach((mode, i) => {
            const label = new Text({ text: mode, style: { fill: 0x8e8e93, fontSize: 13, fontFamily: 'system-ui' } });

            label.anchor.set(0.5, 0);
            label.position.set(90 + i * 150, 168);
            stage.addChild(label);
        });
        tick(() => {
            g.clear();
            MODES.forEach((alignment, i) => {
                const x = 40 + i * 150;

                // fill + stroke on the same shape share one instance and never leave a seam
                g.roundRect(x, 40, 100, 100, 22, 0.6)
                    .fill(0x2c2c2e)
                    .stroke({ width: params.width, color: 0x30d158, alpha: 0.85, alignment });
                g.roundRect(x, 40, 100, 100, 22, 0.6).stroke({ width: 1, color: 0xffffff, alpha: 0.6 }); // the geometric edge
            });
        });
    },
});
```

Strokes on closed shapes can sit inside the edge, centred on it, or outside. `alignment` takes the
words or Pixi's numbers (`1` inside, `0.5` center, `0` outside). Strokes on open shapes (lines,
arcs) are always centred.

## Fill + stroke in one primitive

When `stroke()` directly follows `fill()`, both paints go into the same primitive. The fragment
shader splits each pixel's coverage exactly into three parts: the stroke band, the fill outside it,
and the fill under it. This avoids the halo you get when you draw an inner stroke on top of an
anti-aliased fill edge.

## Hairlines

Live demo `hairlines` (source):

```ts
import { SilkGraphics } from 'pixi-silk';
import { defineDemo } from './runtime';

// Lines thinner than a device pixel keep one pixel of width and fade instead of breaking up.
export default defineDemo({
    size: [480, 220],
    controls: {
        scale: { type: 'range', label: 'width scale', min: 0.05, max: 2, step: 0.01, value: 0.5 },
    },
    setup({ stage, params, tick }) {
        const g = new SilkGraphics();

        stage.addChild(g);
        tick((t) => {
            g.clear();
            for (let i = 0; i < 24; i++) {
                const a = -Math.PI / 2 + (i / 23) * Math.PI + Math.sin(t * 0.3) * 0.05;
                const w = (0.1 + i * 0.12) * params.scale;

                g.line(240, 200, 240 + Math.cos(a) * 220, 200 + Math.sin(a) * 190).stroke({
                    width: w,
                    color: 0xffffff,
                });
            }
        });
    },
});
```

A stroke cannot be drawn thinner than one device pixel. So Silk keeps a thinner stroke one pixel
wide and scales its alpha by `width / 1px`. The total ink stays proportional to the width. Lines
never break into dots, and zooming out keeps every line visible. The threshold is the `minStrokePx`
setting (default 1).

## Per-object settings

```ts
const g = new SilkGraphics({ dither: 1, aaWidth: 1, minStrokePx: 1 });

g.aaWidth = 1.5;     // softer edges (device pixels of AA ramp)
g.dither = 0;        // turn gradient / blur dithering off
```

Defaults live in `SilkGraphics.defaults`.
