# Hazeglow
> Soft, grainy, animated gradient backgrounds and mesh gradients for React, drawn on the GPU with WebGL2. Zero dependencies, about 13 kB gzipped. Free and MIT licensed.
Design a gradient visually at https://hazeglow.dev/generator and copy its config. Formerly published as `@scoobynko/afterglow` with the component `Afterglow`; configs are unchanged.
## Install
npm i hazeglow
## React
```tsx
import { Hazeglow, parseConfig, presets } from "hazeglow";
export function Hero() {
return (
);
}
```
- Props: `config` (required), `className`, `style`, `onUnsupported()` (called when WebGL2 is missing; keep a CSS background behind the canvas as a fallback).
- The canvas fills its parent. It pauses when off screen and draws a single still frame when `prefers-reduced-motion` is set.
- Browsers allow about 16 WebGL canvases per page. Keep live gradients few; use images of a frame elsewhere.
## Without React
```ts
import { createRenderer, parseConfig } from "hazeglow/core";
const renderer = createRenderer(canvas);
if (renderer) {
const config = parseConfig(input);
let time = 0;
let last = performance.now();
const tick = (now: number) => {
time += Math.min((now - last) / 1000, 0.1) * config.speed;
last = now;
renderer.render(config, time);
requestAnimationFrame(tick);
};
requestAnimationFrame(tick);
}
```
`createRenderer` returns `{ render(config, time, pointer?), resize(width, height), dispose(), maxSize }`, or `null` without WebGL2. Resize in device pixels and render right after resizing.
## Config (version 1)
Always pass a config through `parseConfig(input)`. It never throws, clamps numbers to their ranges and fills anything missing with defaults.
- `shape`: "pill" | "band" | "blob" | "ring" | "mesh"
- `center` [x, y] 0 to 1, `size` [w, h] 0 to 1.5, `roundness` 0 to 1, `softness` 0 to 1, `rotation` 0 to 360, `rampDirection` 0 to 1, `angle` 0 to 360, `warp` 0 to 1, `warpScale` 0.5 to 4
- `palette`: 2 to 10 colors, `background`: one color. A color is hex ("#rgb" or "#rrggbb"), `oklch(L C H)` or `var(--token)` (see Colors)
- `mesh`: up to 16 points `[x, y, color]`, used when `shape` is "mesh"
- `grain` 0 to 1, `motion` "none" | "drift" | "breathe" | "flow", `speed` 0 to 2, `loop` 0 to 30 seconds (0 never repeats)
- `hover` 0 to 1 (0 is off), `hoverMode` "pull" | "push"
- `effect` "none" | "dither" | "ascii" | "halftone" | "pixelate" | "glass", `effectSize` 2 to 64, `effectAmount` 0 to 1
Presets: `dusk`, `pearl`, `ember`, `horizon`, `ultraviolet`, `candy` via `import { presets }`. `randomConfig(seed)` makes a random config. `encodeConfig` and `decodeConfig` turn a config into a URL-safe string and back.
## Colors
From 0.3.0, `palette`, `background` and mesh point colors can be hex, `oklch(L C H)` or a CSS variable such as `var(--brand)` or `var(--accent, #f59a22)`. The engine reads variables from the canvas's computed style, so a theme set on any ancestor works, and it redraws by itself when the `class`, `style` or `data-theme` attribute on `` or `prefers-color-scheme` changes. Always give a fallback in `var()`, because a missing variable draws black.
`resolveConfig(config, element)` returns a copy with every `var()` replaced by the color it holds. Use it before `encodeConfig` (so a share link carries colors, not variable names) and anywhere there is no DOM, like a worker. `isColor(value)` checks the syntax and `colorToOklab(color, element?)` returns Oklab `[L, a, b]` or `null`.
## Share links
https://hazeglow.dev/generator#c=&w=&h=
## Notes for coding agents
- Prefer a config designed in the generator over inventing colors.
- Hex works everywhere. Use `oklch()` or `var(--token)` when the gradient should follow the site's theme (needs hazeglow 0.3.0 or newer).
- Do not stack CSS gradients on top of the canvas; put content above it instead.