Lines & paths
Anti-aliased lines and polylines with monotone or Catmull-Rom smoothing, Bezier paths, tapered strokes and translucent joints without double blending.
Updated pixi-silk 0.1.0
Source src/demos/polyline.demo.ts
import { SilkGraphics } from 'pixi-silk';
import { defineDemo } from './runtime';
export default defineDemo({
size: [480, 220],
controls: {
width: { type: 'range', label: 'width', min: 0.25, max: 16, step: 0.25, value: 6, unit: 'px' },
smooth: { type: 'select', label: 'smooth', options: ['none', 'monotone', 'catmull'], value: 'none' },
alpha: { type: 'range', label: 'alpha', min: 0.1, max: 1, step: 0.05, value: 0.6 },
},
setup({ stage, params, tick }) {
const g = new SilkGraphics();
stage.addChild(g);
tick((t) => {
const pts: number[] = [];
for (let i = 0; i <= 12; i++) pts.push(30 + i * 35, 110 + Math.sin(i * 1.3 + t) * 60 * Math.cos(i * 0.7));
g.clear();
// translucent on purpose: every pixel is shaded once, so joints never double-blend
g.polyline(pts, { smooth: params.smooth === 'none' ? undefined : (params.smooth as 'monotone') }).stroke({
width: params.width,
color: 0x0a84ff,
alpha: params.alpha,
cap: 'round',
});
for (let i = 0; i < pts.length; i += 2) g.circle(pts[i], pts[i + 1], 3).fill(0xffffff);
});
},
});Lines and polylines#
g.line(x0, y0, x1, y1).stroke({ width: 2, color: 0xffffff });
g.polyline([x0, y0, x1, y1, x2, y2]).stroke({ width: 3, color: 0x0a84ff, cap: 'round' });
g.polyline(points, { closed: true }).stroke(...); // closed loop
g.polyline(points, { smooth: 'monotone' }).stroke(...); // charts: never overshoots the data
g.polyline(points, { smooth: 'catmull' }).stroke(...); // free curves through every point
Points can be a flat array [x0, y0, x1, y1, ...], a typed array, or an array of { x, y }.
Each segment is its own instance. For every pixel, only the nearest segment shades it. So a translucent polyline has uniform alpha at its joints instead of darker blobs. For the same reason, joints are always round. Round is the only join that is continuous in distance.
Paths#
The canvas-style path API works for strokes:
g.moveTo(10, 80)
.quadraticCurveTo(60, 10, 110, 80)
.bezierCurveTo(140, 120, 180, 20, 220, 60)
.lineTo(260, 60)
.stroke({ width: 3, color: 0xff9f0a, cap: 'round' });
Silk flattens curves adaptively into segments. Free-form paths are stroke-only. For filled
regions, use closed primitives or area().
Tapered strokes#
Pass an array as width. With [start, end], Silk interpolates the width along the path length.
With one value per point, each value sets the width at its vertex. Each segment becomes an uneven
capsule, so the taper is smooth through joints.
Source src/demos/taper.demo.ts
import { along, SilkGraphics } from 'pixi-silk';
import { defineDemo } from './runtime';
const INK = along([0xff9f0a, 0xff375f, 0xbf5af2]);
export default defineDemo({
size: [480, 220],
controls: {
start: { type: 'range', label: 'start width', min: 0, max: 30, step: 0.5, value: 2 },
end: { type: 'range', label: 'end width', min: 0, max: 30, step: 0.5, value: 22 },
},
setup({ stage, params, tick }) {
const g = new SilkGraphics();
stage.addChild(g);
tick((t) => {
const pts: number[] = [];
for (let i = 0; i <= 40; i++) {
const u = i / 40;
pts.push(40 + u * 400, 110 + Math.sin(u * 7 + t) * 50 * (1 - u * 0.5));
}
g.clear();
// `width: [start, end]` tapers along the path; an array per point also works
g.polyline(pts).stroke({ width: [params.start, params.end], cap: 'round', gradient: INK });
});
},
});g.polyline(points).stroke({ width: [2, 22], cap: 'round', gradient: along([0xff9f0a, 0xbf5af2]) });
along() is a gradient that follows the stroke from its first point to its last.
Hairlines#
Strokes thinner than one device pixel stay one pixel wide and fade in proportion. They do not break up into dots. See Fills & strokes.
API reference: SilkGraphics, along