# Ink — WebGL Background for React

> Dye blooming through still water: a shared domain-warped fbm curls slow billowing folds while two inks bleed from drifting sources, pooling dense and dissolving into ragged tendrils with delicate luminous fronts, mixing into a pale bloom where they overlap. The pointer curls a soft eddy. Original OGL fragment shader, hard-dithered, DPR-clamped, offscreen-paused, reduced-motion-safe; a ghost pointer keeps it moving when untouched.

- Page: https://crucibleui.com/components/backgrounds/ink
- Category: [backgrounds](https://crucibleui.com/backgrounds)
- Tier: pro (one-time purchase, see https://crucibleui.com/pricing)
- Engine: ogl
- npm dependencies: ogl

## Install

```bash
npx shadcn@latest add @crucible/ink
```

Pro install requires adding the token-bearing `@crucible` registry to components.json (issued at https://crucibleui.com/account after purchase).

Import from `@/components/crucible/ink`.

Backgrounds render `absolute inset-0 -z-10`. Their parent must be a stacking context — wrap in `relative isolate` and put your content in a `relative z-10` child, or the background disappears behind an opaque ancestor.

## Props (`InkProps`)

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `colors?` | `[string, string, string, string]` | — | Four tones: `[water, inkA, inkB, bloom]`. `water` is the still dark ground the dye diffuses through (near-black, faintly tinted), `inkA` and `inkB` are the two inks bleeding in, and `bloom` is the pale light that catches on the delicate diffusing fronts and where the two inks overlap. Any hex string. |
| `speed?` | `number` | `1` | Diffusion / billow speed multiplier. 1 = default (very slow), 2 = twice as fast. |
| `intensity?` | `number` | `1` | Saturation / opacity of the dye, 0–2. 1 = default. Lower reads as thinner, more dilute ink; higher lets it pool richer and darker. |
| `swirl?` | `number` | `1` | Strength of the swirl the pointer injects into the water, 0–1. 0 leaves the ink to billow on its own; 1 lets the cursor curl a soft eddy through it. |
| `idle?` | `boolean` | `true` | Idle autoplay: after ~1.5s with no real pointer, a seeded ghost pointer curls the eddy through the water on a slow drift so it never looks dead in thumbnails and heroes; the real pointer takes over instantly. Never runs under reduced motion. |
| `paused?` | `boolean` | — | Freeze the animation at its current frame. |

Also accepts: `React.ComponentPropsWithoutRef<"div">`.

## Usage example

`DemoScene` is a site-only preview harness, not part of the component — replace it with your own layout.

```tsx
import { Ink } from "@/components/crucible/ink";
import { DemoScene } from "@/components/registry/demo-scene";

export default function InkDemo() {
  return (
    <DemoScene
      background={<Ink />}
      headline="Let it settle"
      subhead="Two inks bleeding through still water behind your content. Move your cursor to swirl it."
    />
  );
}
```

## Accessibility & performance

Honors `prefers-reduced-motion` with a designed static frame or fade (never a blank box) and passes `className` through. Pauses offscreen and on hidden tabs, clamps device pixel ratio, and tears down its GL/canvas context on unmount.
