crucıble

Solvent

Interactive fluid ink — a real GPU stable-fluids solver (semi-Lagrangian advection, vorticity confinement, Jacobi pressure projection) driving a persistent two-pigment dye field: cursor strokes push force and pigment into the fluid, so magenta and cyan inks swirl, fold through violet where they meet, and keep marbling long after the cursor stops. A seeded virtual cursor keeps it alive when idle and hands over to the real pointer with an exponential tween; two pigment blooms are pre-seeded so it is never blank on load. Half-res HalfFloat sim FBOs, adaptive Jacobi budget, DPR-clamped, offscreen-paused, reduced-motion-safe (a frozen ink marbling).

oglpro

Installation

CLI

npx shadcn@latest add @crucible/solvent

Manual — install dependencies, then copy the source

npm install ogl

Props

PropTypeDefaultDescription
colors[string, string, string]iridescent magenta / cyan meeting through violetThree ink tones: [pigmentA, pigmentB, midpoint]. Splats alternate slowly between the two pigments, and wherever the inks fold into each other the blend passes through the midpoint — real marbling reads.
forcenumberStir strength — how hard cursor strokes push the fluid, 0–3. 1 = default.
splatRadiusnumber0.11Splat brush radius as a fraction of the container height.
dissipationnumber0.9Fraction of ink kept after one second, 0.5–0.995. Higher holds marbling longer after the cursor stops; lower clears faster.
swirlnumberVorticity-confinement boost, 0–3 — how much the flow curls back on itself into eddies instead of smearing straight. 1 = default.
iterationsnumber20Jacobi pressure-solve iterations, 4–40. Higher is a crisper, more incompressible flow. Automatically capped at 12 on high-DPR displays and small viewports.
autoDemo{ speed?: number; intensity?: number; resumeDelay?: number } | false{ speed: 1, intensity: 0.85, resumeDelay: 2.5 }Idle auto-demo: a seeded virtual cursor wanders the canvas and keeps the ink alive when nobody is interacting. speed scales its wander rate, intensity its ink output (both around 1), resumeDelay is the seconds of pointer stillness before it fades back in. Pass false to disable.
interactivebooleantrueRespond to the real pointer. When false only the auto-demo stirs.
speednumberSimulation speed multiplier. 1 = default.
pausedbooleanFreeze the simulation, holding the current frame.
Also accepts all props of React.ComponentPropsWithoutRef<"div">they pass through to the underlying element.

Honors prefers-reduced-motion with a designed static fallback, pauses offscreen and on hidden tabs, and passes className through.