Redraw is currently in technical preview, available to start-react-native.dev subscribers. API is unstable.
Custom Feathers
A feather function writes the blur sigma per pixel instead of writing a
color or a stroke width. The renderer reads canvas.field as sigma during
the feather pass and produces analytical blur with a different radius at
every fragment.
This page is for the write your own sigma case. For ready-made feather modes (uniform, glow, sweep, radial, etc.) see Vector Feathering.
How it works
- You write a
fn(...)that setscanvas.fieldto your desired sigma per pixel. - You wrap it in
ApplyFeather(yourFn). That's the bridge between a userfnand the feather pipeline. - Pass the wrapped feather as the third argument to
addStrokeoraddFill.
import { fn, ApplyFeather, SingleStrokeBrush } from "redraw";
import { d, std } from "typegpu";
const VariableSigma = fn(
(canvas, ctx, props) => {
"use gpu";
canvas.field = std.mix(40.0, 2.0, props.focus); // 40 blurry, 2 sharp
},
{ focus: 0 },
);
const sigma = new VariableSigma({ focus: 1 });
const brush = new SingleStrokeBrush();
brush.addStroke(color, strokeWidth, new ApplyFeather(sigma));
ApplyFeather runs your fn first (writing the sigma to canvas.field)
and then applies a Feather.fromContext() blur using that sigma.
Recipe: orbiting focus
The "variable blur" demo runs a moving focal spot along the path: sharp at
one point on the curve, blurry everywhere else. The function uses ctx.t
to locate the current pixel along the arc length and props.time to slide
the focus along it:
const OrbitingFocus = fn(
(canvas, ctx, props) => {
"use gpu";
const center = std.mod(props.time * 0.25, 1.0);
const dist1 = std.abs(ctx.t - center);
const dist2 = 1.0 - dist1;
const dist = std.min(dist1, dist2); // wrap-around distance
const focus = std.exp(-std.pow(dist * 8.0, 2.0)); // gaussian peak
canvas.field = std.mix(40.0, 2.0, focus);
},
{ time: 0 },
{ maxCullDistance: 100 },
);
const focus = new OrbitingFocus({ time: 0 });
brush.addStroke(color, strokeWidth, new ApplyFeather(focus));
// Animate every frame:
focus.props.time = ctx.time;
When to reach for this
Most use cases are covered by the static factories in Vector Feathering:
| You want | Use |
|---|---|
| Uniform blur on a shape | Feather.blur(sigma) |
| Outer glow / inner shadow | Feather.glow(sigma) / .inner(sigma) |
| Drop shadow | Feather.outer(sigma) |
| Motion blur from a velocity vector | Feather.sweep(sigma, [dx, dy]) |
| Radial blur (lens-like) | Feather.radial(sigma, [cx, cy]) |
Variable per-pixel sigma from a fn | new ApplyFeather(yourFn) (this page) |
If your sigma can be expressed as a function of ctx.t, ctx.sdf, or
ctx.pos, the ApplyFeather path gives you per-pixel control. Common
recipes: depth-of-field along a curve, focus-pull animations, sigma that
ramps with arc length.