Rendering
Resolution & DPR
createSilkApp maps the canvas 1:1 to device pixels, even at fractional DPRs, follows DPR changes and renders filters at screen resolution.
Updated pixi-silk 0.1.0
Live demo
Source src/demos/resolution.demo.ts
import { SilkGraphics } from 'pixi-silk';
import { defineDemo } from './runtime';
// A Siemens star is the hardest test for anti-aliasing: every wedge narrows below a pixel.
export default defineDemo({
size: [480, 240],
controls: {
resolution: { type: 'select', label: 'resolution', options: ['device', '1x', '2x', '3x'], value: 'device' },
},
setup({ silk, stage, params, tick }) {
const g = new SilkGraphics();
let applied = '';
stage.addChild(g);
for (let i = 0; i < 36; i++) {
const a = (i / 36) * Math.PI * 2;
g.sector(240, 120, 110, a, a + Math.PI / 36).fill(0xffffff);
}
g.circle(240, 120, 110).stroke({ width: 1, color: 0x636366 });
tick(() => {
if (params.resolution === applied) return;
applied = params.resolution;
// createSilkApp sizes the backing store in device pixels; force 1x/2x/3x to compare
silk.setResolution(applied === 'device' ? null : Number(applied[0]));
});
},
});Smooth edges need two things: exact coverage (Silk’s shader) and a canvas whose pixels are the
screen’s pixels. createSilkApp handles the canvas:
import { createSilkApp } from 'pixi-silk';
const silk = await createSilkApp({
parent: document.getElementById('stage')!,
background: 0x000000,
maxResolution: 3, // cap the DPR (memory / fill rate)
// fixedResolution: 2, // force a resolution instead
// touchAction: 'none', // canvases that handle drags themselves
});
silk.app; // the Pixi Application
silk.width; // CSS pixels
silk.resolution; // device pixels per CSS pixel
silk.setResolution(1); // compare at 1x (null returns to devicePixelRatio)
silk.destroy();
What it sets up#
- It sizes the backing store with
ResizeObserveranddevice-pixel-content-box. Every canvas pixel maps to exactly one screen pixel, even at a DPR of 1.5 or 2.625. RoundingcssWidth * dpryourself can be off by one. If it is, the browser resamples the whole canvas and blurs every edge. - DPR changes (dragging the window to another display, browser zoom) resize the backing store.
A
matchMedialistener handles Safari, which has no device-pixel-content-box. - MSAA is off (
antialias: false). Silk’s coverage is analytic, so MSAA would only cost bandwidth. - Pixel rounding is off (
roundPixels: false), so sub-pixel motion stays smooth. - Filters render at screen resolution. Pixi filters default to resolution 1, which blurs
everything behind a filter on retina screens.
createSilkAppsetsFilter.defaultOptions.resolutionto'inherit'(opt out withinheritFilterResolution: false). - Touch scrolling works. Pixi sets
touch-action: noneon its canvas, which blocks page scrolling on phones. The default here ispan-y pinch-zoom.
Using your own Application#
If you create the application yourself, use the same settings:
await app.init({
preference: 'webgl',
antialias: false,
autoDensity: true,
resolution: window.devicePixelRatio,
roundPixels: false,
});
Filter.defaultOptions.resolution = 'inherit';
Render textures and filters#
A SilkGraphics in a render texture or behind a filter is anti-aliased at that target’s
resolution, because Silk’s coverage uses screen-space derivatives. Give render textures the
renderer’s resolution to keep them sharp on retina screens. The
render texture lab page shows the difference.
API reference: createSilkApp, SilkAppOptions, SilkApp