Currently, the <DitherGradient> component renders via a <canvas> element and is restricted to four cardinal directions ("up" | "down" | "left" | "right"). If we were to extend this for precise 360deg steps, calculating arbitrary angles for gradients pixel by pixel can be computationally expensive, and rotating a canvas introduces browser anti aliasing that causes some blurriness because of anti-aliasing.
|
for (let y = 0; y < rows; y++) { |
|
for (let x = 0; x < cols; x++) { |
|
// t runs 0 at the `from` edge → 1 at the `to` edge. |
|
const t = |
|
spec.direction === "up" |
|
? 1 - (y + 0.5) / rows |
|
: spec.direction === "down" |
|
? (y + 0.5) / rows |
|
: spec.direction === "left" |
|
? 1 - (x + 0.5) / cols |
|
: (x + 0.5) / cols |
|
const density = 1 - t |
Basically, to support an arbitrary angles using this canvas approach, we would have to replace those simple fractions with Math.sin and Math.cos for every single cell in that loop. Computing this before calling fillRect causes main thread blocking.
Proposed Solution (Procedural SVG Filters)
I had been working on a standalone tool that achieves this effect perfectly by offloading the quantization and dithering math directly to the browser's native SVG rendering engine.
So, instead of iterating, we can use a lightweight SVG node:
- Define the angle natively via an SVG
<linearGradient>.
- Embed the Bayer matrix (e.g., 4x4) and tile it using
feTile.
- Use
feComposite (with arithmetic operators) and feComponentTransfer (with discrete tables) to mathematically threshold the gradient against the Bayer map natively.
Benefits:
- We get Arbitrary Angles, so full 360-degree support.
- No anti-aliasing as we wouldn't need to transform the canvas
- Bypasses the nested JS for loop, moving heavy computation to the GPU/native renderer.
I already have a working proof of concept for this exact math and would love to help contribute it as a PR, either as a standalone component or an enhancement to the existing one. Let me know what the team thinks!
Currently, the
<DitherGradient>component renders via a<canvas>element and is restricted to four cardinal directions ("up" | "down" | "left" | "right"). If we were to extend this for precise 360deg steps, calculating arbitrary angles for gradients pixel by pixel can be computationally expensive, and rotating a canvas introduces browser anti aliasing that causes some blurriness because of anti-aliasing.dither-kit/registry/dither-kit/gradient.tsx
Lines 68 to 79 in 9fb0b14
Basically, to support an arbitrary angles using this canvas approach, we would have to replace those simple fractions with
Math.sinandMath.cosfor every single cell in that loop. Computing this before callingfillRectcauses main thread blocking.Proposed Solution (Procedural SVG Filters)
I had been working on a standalone tool that achieves this effect perfectly by offloading the quantization and dithering math directly to the browser's native SVG rendering engine.
So, instead of iterating, we can use a lightweight SVG node:
<linearGradient>.feTile.feComposite(with arithmetic operators) andfeComponentTransfer(with discrete tables) to mathematically threshold the gradient against the Bayer map natively.Benefits:
I already have a working proof of concept for this exact math and would love to help contribute it as a PR, either as a standalone component or an enhancement to the existing one. Let me know what the team thinks!