Skip to content
pixi-silk
GitHub repository
Learn

Getting started

Install pixi-silk next to pixi.js v8, create an app with createSilkApp and draw a squircle card, a gradient gauge and an area chart with SilkGraphics.

Updated pixi-silk 1.0.1View as Markdown

pixi-silk adds SilkGraphics, a PixiJS v8 display object with an API close to Graphics. You add it to the stage like any other container.

The scene this page builds.

Install#

npm install pixi-silk pixi.js

pixi.js 8.6 or newer is a peer dependency, so your app owns the renderer. The package is ESM with TypeScript declarations and has no side effects, so a bundler keeps only what you import.

Create the app#

import { createSilkApp } from 'pixi-silk';

const { app } = await createSilkApp({
    parent: document.getElementById('stage')!, // the canvas fills this element
    background: 0x000000,
});

createSilkApp wraps new Application() and app.init(). It turns MSAA off, sizes the canvas so one canvas pixel is one device pixel, and follows changes of the device pixel ratio. You can keep your own Application instead. Resolution lists the settings that matter.

Draw#

Shape methods add to the active path. fill() and stroke() paint it, and every call chains.

import { conic, SilkGraphics, vertical } from 'pixi-silk';

const GAUGE = conic([0x30d158, 0xffd60a, 0xff453a]); // spans the arc it is drawn on
const g = new SilkGraphics();

g.roundRect(10, 10, 340, 140, 22, 0.6).fill(0x1c1c1e); // radius 22, squircle smoothing 0.6
g.arcSweep(80, 80, 44, -Math.PI / 2, Math.PI * 1.5).stroke({ width: 12, cap: 'round', gradient: GAUGE });
g.area([120, 100, 170, 80, 220, 92, 270, 60, 320, 70], 130, { smooth: 'monotone' })
    .fill(vertical([[0, 0xff9f0a, 0.8], [1, 0xff9f0a, 0]]))
    .stroke({ width: 2, color: 0xff9f0a, cap: 'round' }); // a stroke right after a fill draws the top line

app.stage.addChild(g);

The card is a squircle, the gauge is one arc with a conic gradient along it, and the chart is a smoothed area with a fading fill.

Animate#

Redraw every frame. clear() keeps the GPU buffers, so a rebuild only rewrites numbers.

app.ticker.add((ticker) => {
    const t = ticker.lastTime / 1000;

    g.clear();
    g.arcSweep(80, 80, 44, -Math.PI / 2, Math.PI * 2 * (0.5 + 0.4 * Math.sin(t)))
        .stroke({ width: 12, cap: 'round', gradient: GAUGE });
});

Create gradients once and reuse them. Ramps with the same stops share one row of the gradient atlas, and a reused object skips parsing the stops again.

What it does differently#

Graphics turns shapes into triangles when you draw, and only MSAA smooths their edges. SilkGraphics draws each shape as a quad. The fragment shader evaluates the shape’s exact distance function at every pixel and turns the distance into coverage.

Two things follow from that. Edges stay sharp at any zoom and in filters and render textures, because nothing was tessellated. And one SilkGraphics renders in one instanced draw call, whatever it holds. Call enableSilkBatching() before you create objects to draw runs of them in one call too. Anti-aliasing compared sets this against MSAA, @pixi/graphics-smooth and supersampling, and How it works walks through the shader.

It renders with WebGL2 by default. Renderers shows how to turn on WebGPU.

Where it stops#

You wantUse
Texture or pattern fillsa Pixi Sprite or TilingSprite
A whole SVG filePixi’s Graphics.svg(). svgPath(d) takes one path’s d attribute
TextSilkText from pixi-silk/text, a separate entry point

Where next#

API reference: SilkGraphics, createSilkApp, conic, vertical, enableSilkBatching