Redraw is currently in technical preview, available to wcandillon.dev subscribers. API is unstable.
Paints
A Paint describes how a draw is shaded: its color, optional color
filters, an optional stroke, an optional feather, and a blend mode. You
pass one to every drawing call (draw, drawPath, fill).
import { Feather, Paint } from "redraw";
const paint = new Paint()
.setColor("#3FCEBC")
.setFeather(Feather.glow(20));
canvas.draw(new Circle([200, 200], 80), paint);
Bindings
Every slot of a Paint holds a binding: a GPU function paired with the props it is invoked with. There are three equivalent ways to set one:
// Sugar for solid colors (shorthand for addShader(new ColorFill("black"))):
paint.setColor("black");
// A prebuilt binding object:
paint.addShader(new GradientAlongPath(["red", "blue"]));
// Or the raw (function, props) pair, for functions you authored yourself.
// 0.2 palette cycles per second (time is in milliseconds):
paint.addShader(PathGradient, { shift: time * 0.0002 });
paint.setStroke(HelloStroke, { width: 25 });
Each setter checks the function's kind: addShader takes color
functions, setStroke takes a strokeWidth function, and setFeather
takes a feather function. Any custom function must also be declared in
the canvas's Library.
API
| Method | What it does |
|---|---|
setColor(color) | Sugar for a solid base color: any CSS-style color or [r, g, b, a] array (shorthand for addShader(new ColorFill(color))). |
addShader(binding) or addShader(fn, props) | Append a color shader: the base color first (a ColorFill, a gradient, an ImageShader, or a custom color function), then any filters, each reading and overwriting the running color. Every draw needs at least one. |
setStroke(...) | Stroke instead of filling: the shape's SDF is banded by the returned width. See custom strokes. |
setFeather(...) | Feather the coverage analytically. See Vector Feathering. |
blendMode = BlendMode.Screen | Composite mode (SrcOver default, Multiply, Screen, ...). |
All setters return this, so they chain.
Solid colors
setColor accepts any CSS-style color (hex, rgb(), rgba(), named
colors) or an [r, g, b, a] array:
const background = new Paint().setColor("rgb(36,43,56)");
canvas.fill(background);
Paints are plain objects: build the constant ones once at module scope and reuse them across frames.
Color filters
Color filters are color-kind functions that compose in order after the
base color: append them with addShader. The built-ins mirror the usual
suspects:
import { ColorMatrix, Duotone, Grain, Posterize } from "redraw";
paint.addShader(ColorMatrix.grayscale());
paint.addShader(new Grain(0.1));
| Filter | What it does |
|---|---|
new ColorMatrix(values) | A 4x5 color matrix. Factories: grayscale(), sepia(), saturate(s), hueRotate(a), brightness(b), contrast(c), invert(). |
new Grain(intensity, seed?) | Film grain hashed by screen position. |
new Posterize(levels) | Reduce each channel to levels bands. |
new Duotone(dark, light) | Map luminance onto a two-color ramp. |
Unlike ColorFill, the color-filter functions are not part of std:
pass the ones you use to createLibrary (each class exposes its
function as .fn, and colorFilters exports them all):
import { colorFilters, createLibrary } from "redraw";
const library = createLibrary(device, [...colorFilters]);
// or just the one you need: createLibrary(device, [Grain.fn])
You can also author your own with the color() helper.
Blend modes
paint.blendMode selects how the draw composites over what's already on
the canvas. The breathe demo screen-blends glowing circles so the
overlaps bloom:
import { BlendMode, Circle, Feather, Paint } from "redraw";
const paint = new Paint()
.setColor(i % 2 ? c1 : c2)
.setFeather(Feather.glow(100));
paint.blendMode = BlendMode.Screen;
canvas.draw(new Circle([x, y], radius), paint);
Culling and maxCullDistance
A stroke or a feather spreads coverage past the geometry's bounds. Each
binding declares how far, in device pixels, through maxCullDistance;
the draw's bounding box is padded by the paint's total so tile binning
never culls the spilled band. The built-in Feather factories compute
it from sigma automatically; a custom feather sets maxCullDistance in
its options, and a custom stroke declares maxStrokeWidth (the pad is
half of it; see Custom Effects).
If the edge of a wide stroke or glow looks clipped along tile
boundaries, this is the knob.