Redraw is currently in technical preview, available to wcandillon.dev subscribers. API is unstable.
Vector Feathering
Most 2D pipelines blur things in raster: sample neighboring pixels, weight them, sum. That's an expensive operation, and quality degrades at small radii.
Redraw blurs analytically. The feathering step modulates the alpha coverage applied to a shape, computed per pixel from the shape's signed distance: no offscreen layers, no sampling, O(1) per pixel. That's what makes a few effects raster pipelines reach to post-process for practical at interactive frame rates: motion blur from a velocity vector, backdrop filters that compose, glow that scales without artifacting.
import { Circle, Feather, Paint } from "redraw";
const paint = new Paint()
.setColor("#3FCEBC")
.setFeather(Feather.glow(50));
canvas.draw(new Circle([cx, cy], 120), paint);
A feather is a falloff curve (gaussian or linear) combined with a mode (where the falloff is applied around the edge).
There are two curves:
enum FeatherCurve {
Gaussian, // erf-based bell, most natural
Linear, // simple linear falloff
}
Default is Gaussian. Every factory below accepts a FeatherCurve as
its last argument: Feather.glow(50, FeatherCurve.Linear).
And seven modes:
| Factory | Mode | What it produces |
|---|---|---|
Feather.blur(sigma, curve?) | Uniform | Symmetric blur on both sides of the edge. |
Feather.glow(sigma, curve?) | Glow | Sharp inside, soft outward (outer halo). |
Feather.inner(sigma, curve?) | Inner | Sharp outside, soft inward (inner shadow). |
Feather.outer(sigma, curve?) | Outer | Transparent inside, soft outside (drop-shadow shape). |
Feather.inset(sigma, curve?) | Inset | Soft outward, clipped to the original boundary (inset highlight). |
Feather.radial(sigma, [cx, cy], curve?) | Radial | Blur radius grows with distance from a center point. |
Feather.sweep(sigma, [dx, dy], curve?) | Sweep | Sigma varies along a direction vector (motion blur, glass). |
(Feather.linear(sigma) is shorthand for a uniform linear-falloff
feather.)
The feather is part of the paint, and the canvas is immediate mode: to animate a blur, build the feather with the new sigma when you record the frame. For sigma that varies per pixel, author your own feather function instead: see Custom Effects: Feather.
Analytical motion blur
Motion blur is normally a post-processing pass. In Redraw it's a property of the paint: set the direction to your velocity vector and sigma to its magnitude, and the renderer produces motion blur at the correct intensity per pixel:
export function render(canvas: Canvas, info: FrameInfo) {
// ... integrate the velocity (vx, vy) ...
const speed = Math.hypot(vx, vy);
const paint = new Paint()
.setColor("#0a0a1a")
.setFeather(
Feather.sweep(Math.min(speed * 0.25, 50), [-vx / speed, -vy / speed]),
);
canvas.draw(new Circle([cx, cy], 120), paint);
}
Fused backdrop filters
canvas.pushLayer({ clip, feather, tint }) opens a layer: a region
whose draws are feathered only where the region reads inside, plus an
optional tint filled over the region when the layer pops. The region is
an analytic field re-evaluated per pixel, so a layer costs no offscreen
surface and no rasterization; nested layers intersect and their feathers
compose.
canvas.pushLayer({
clip: { fn: CircleClip, props: { center: clipCenter, radius } },
feather: 50,
tint: new ColorFill("rgba(255, 255, 255, 0.3)"),
});
// ... draw the content this layer feathers ...
canvas.popLayer(); // applies the tint and pops
The LayerDescriptor:
| Field | Type | What it does |
|---|---|---|
clip | a clip-kind function binding | The layer region. Same contract as canvas.clip: props are read under each draw's transform. |
feather | number | Gaussian sigma feathering the draws inside the layer, applied only where the region reads inside. 0 disables it. |
tint | a color-kind binding (optional) | Filled over the region (antialias-clipped) when the layer pops, e.g. new ColorFill("rgba(0, 0, 255, 0.5)"). |
The region is a clip-kind function; author one with the clip()
helper (see Drawings). The demo
below drifts a gradient disc and the layer region apart, which makes the
backdrop machinery visible: the feather applies only where the region
reads inside.