Your first widget
Build a pill button with SilkGraphics: a soft glow from a blurred fill, springs for hover and press, and a SilkText label. Then compare it with SilkButton.
Updated pixi-silk 1.0.1View as Markdown
This tutorial builds a pill button that glows, brightens on hover and dips when pressed. It uses
SilkGraphics for the shape and the glow, two springs for motion and SilkText for the label.
Draw the button#
A button is a container with a graphics object and a label in it. The pivot sits at the centre,
so the press can scale it in place. paint() redraws the shape for a hover and a press amount
from 0 to 1.
import { Container } from 'pixi.js';
import { SilkGraphics } from 'pixi-silk';
import { SilkText } from 'pixi-silk/text';
const W = 180;
const H = 56;
const button = new Container();
const shape = new SilkGraphics();
const label = new SilkText({ text: 'Play', fontSize: 20, fontWeight: 600, fill: 0xffffff, anchor: 0.5 });
label.position.set(W / 2, H / 2);
button.addChild(shape, label);
button.pivot.set(W / 2, H / 2);
button.position.set(200, 120);
app.stage.addChild(button);
function paint(hover: number, press: number) {
shape.clear();
shape.pill(0, 0, W, H).fill({ color: 0x0a84ff, alpha: 0.4 + 0.5 * hover, blur: 10 + 8 * hover, spread: 2, blend: 'add' });
shape.pill(0, 0, W, H).fill(press > 0.5 ? 0x0060df : 0x0a84ff);
}
paint(0, 0);
SilkText comes from pixi-silk/text, a separate entry point. Its glyphs are distance fields
too, so the label stays sharp when the button scales.
The glow#
paint() draws the pill twice. The first copy is the glow: blur softens it, spread grows it
before the blur so it keeps a bright core, and blend: 'add' brightens the background instead of
covering it. Both pills render in the object’s one draw call, with no filter and no extra texture.
Add hover and press#
Spring from the root import moves a value towards a target. This function wires pointer events
to two springs and repaints while they move.
import type { Container, Ticker } from 'pixi.js';
import { Spring } from 'pixi-silk';
export function animate(button: Container, paint: (hover: number, press: number) => void, ticker: Ticker) {
const hover = new Spring(0, 300, 30); // value, stiffness, damping
const press = new Spring(0, 700, 32);
button.eventMode = 'static';
button.cursor = 'pointer';
button.on('pointerover', () => (hover.target = 1));
button.on('pointerout', () => {
hover.target = 0;
press.target = 0;
});
button.on('pointerdown', () => (press.target = 1));
button.on('pointerup', () => {
press.target = 0;
console.log('pressed');
});
ticker.add((t) => {
if (hover.settled && press.settled) return;
const h = hover.step(t.deltaMS / 1000);
const p = press.step(t.deltaMS / 1000);
paint(h, p);
button.scale.set(1 - 0.05 * p);
});
}
Call animate(button, paint, app.ticker). Hovering raises the glow over a few frames. Pressing
shrinks the button by 5% and darkens it, and releasing springs it back. The ticker skips the repaint once both springs rest.
Pointer events test the pill shape, not a bounding box, so the corners outside the rounded ends do not react.
The ready-made button#
SilkButton from pixi-silk/ui/button does all of this and handles the cases an app needs.
import { SilkButton } from 'pixi-silk/ui/button';
const play = new SilkButton({ label: 'Play', width: 180, height: 56, onPress: () => console.log('pressed') });
play.position.set(200, 200);
app.stage.addChild(play);
It ignores input while it animates in or out, while it is disabled and while state.busy shows
a spinner. A press that slides off still counts when it is released close by, and holdMs adds a
long press. Write your own button when you need a look it does not draw, and use SilkButton for
the input rules.
Where next#
- Glass & glow has a glowing button demo and the
pixi-silk/glowrecipes. - Radial menus & buttons lists every
SilkButtonoption. - Interaction & motion covers hit testing and springs.
API reference: SilkGraphics, Spring, SilkText, SilkButton