Skip to content
GitHub repository
Paint

Blur, glows & shadows

A gaussian blur applied to each shape's distance field gives neon glows, soft drop shadows and motion blur without filters or extra passes.

Updated pixi-silk 0.1.0

Source src/demos/glow.demo.ts
import { SilkGraphics } from 'pixi-silk';
import { defineDemo } from './runtime';

// `blur` is a gaussian applied to the distance field: no filters, no extra passes.
export default defineDemo({
    size: [480, 220],
    background: 0x050507,
    controls: {
        blur: { type: 'range', label: 'blur (σ)', min: 0, max: 30, step: 0.5, value: 12, unit: 'px' },
    },
    setup({ stage, params, tick }) {
        const g = new SilkGraphics();

        stage.addChild(g);
        tick((t) => {
            const pulse = 0.8 + Math.sin(t * 2) * 0.2;

            g.clear();
            // neon: a blurred copy under a crisp stroke
            g.circle(110, 110, 60).stroke({ width: 8, color: 0xff375f, blur: params.blur * pulse, alpha: 0.9 });
            g.circle(110, 110, 60).stroke({ width: 3, color: 0xffd1dc });
            // soft shadow: offset, blurred, translucent
            g.roundRect(260, 60 + 18, 180, 100, 24, 0.6).fill({ color: 0x000000, alpha: 0.9, blur: params.blur });
            g.roundRect(260, 60, 180, 100, 24, 0.6).fill(0x2c2c2e);
            g.circle(300, 110, 16).fill({ color: 0x30d158, blur: params.blur * 0.4 });
            g.circle(300, 110, 7).fill(0xe8ffe8);
        });
    },
});

Every paint takes a blur (the gaussian standard deviation σ, in local units). Silk blurs the distance field, not the pixels. Coverage becomes 0.5 - 0.5 * erf(d / (sigma * sqrt(2))). The shape’s quad grows by 3σ. A blurred shape costs the same as any other primitive.

Glows#

Draw a blurred copy under a crisp one:

g.circle(110, 110, 60).stroke({ width: 8, color: 0xff375f, blur: 12, alpha: 0.9 }); // glow
g.circle(110, 110, 60).stroke({ width: 3, color: 0xffd1dc });                        // core

Soft shadows#

Offset, blur and fade a dark copy of the shape before drawing it:

g.roundRect(20, 20 + 18, 180, 100, 24, 0.6).fill({ color: 0x000000, alpha: 0.45, blur: 24 });
g.roundRect(20, 20, 180, 100, 24, 0.6).fill(0x1c1c1e);

The Live Activity cards in the Showcase use this pattern. Their shadow matches the reference image (σ 30.5 px, 37.5 px down, 45 % black).

Motion blur and soft ends#

A blurred stroke makes a cheap motion trail. Blurring the first and last ticks of a scale looks like depth of field. See the espresso timer on the Live Activities sheet. Silk dithers blurred paints like gradients, so large soft shadows don’t band.

API reference: FillStyle