Design
llms.txt digitalcrew.tech

Effects

SplashCursor

A WebGL fluid simulation that trails the pointer in coloured dye behind the page content.

MaxEffectsDigitalCrew.SplashCursor
SplashCursor · liveOpen

Guidelines#

Use it for ambient decoration only. Max mounts it app-wide with quality="low" when Settings › Appearance › Splash cursor is on, and behind the public booking page.

What you provide:

  • quality:
    • "high" (default): dye 768, simulation 128, 20 pressure iterations, pixel ratio up to 1.5.
    • "low": dye 512, simulation 128, 16 iterations, pixel ratio 1.
  • Optional overrides, with their defaults: CAPTURE_RESOLUTION 512, DENSITY_DISSIPATION 2.2, VELOCITY_DISSIPATION 2, PRESSURE 0.1, CURL 3, SPLAT_RADIUS 0.32, SPLAT_FORCE 6000, SHADING true, COLOR_UPDATE_SPEED 10, BACK_COLOR { r: 0.5, g: 0, b: 0 }, TRANSPARENT true. SIM_RESOLUTION, DYE_RESOLUTION and PRESSURE_ITERATIONS override the quality preset.

Anatomy: a pointer-events-none fixed inset-0 z-0 canvas. Sections with their own background cover it, and nothing underneath loses clicks.

Behaviour:

  • It never starts under prefers-reduced-motion (checked live), on coarse pointers, or on devices reporting 4 CPU cores or fewer, or 4 GB of memory or less.
  • The render loop stops 2s after the last input, once the dye has faded. It also pauses while the tab is hidden, and releases every GPU resource on unmount.
  • If WebGL setup fails, it silently switches itself off.

In this preview the canvas is contained by a transform on the 320px box, and a short scripted sweep runs on load. The headless renderer reports 4 cores, so there the effect switches itself off and the box stays blank.

Don't mount more than one.

Facts#

GroupEffects
ProductMax
Globalwindow.DigitalCrew.SplashCursor
Sourcemax-agent: src/components/ui/splash-cursor.tsx
Import in the appimport { SplashCursor } from "@/components/ui/splash-cursor";

API#

TypeScript declarations emitted from src/components/ui/splash-cursor.tsx.

export type SplashQuality = "high" | "low";
type SplashCursorProps = {
    quality?: SplashQuality;
    SIM_RESOLUTION?: number;
    DYE_RESOLUTION?: number;
    CAPTURE_RESOLUTION?: number;
    DENSITY_DISSIPATION?: number;
    VELOCITY_DISSIPATION?: number;
    PRESSURE?: number;
    PRESSURE_ITERATIONS?: number;
    CURL?: number;
    SPLAT_RADIUS?: number;
    SPLAT_FORCE?: number;
    SHADING?: boolean;
    COLOR_UPDATE_SPEED?: number;
    BACK_COLOR?: {
        r: number;
        g: number;
        b: number;
    };
    TRANSPARENT?: boolean;
};
declare function SplashCursor(props: SplashCursorProps): JSX.Element;
export { SplashCursor };

Example#

The code of the preview above: plain React.createElement against window.DigitalCrew, with realistic data.

<div id="root" class="p-6"></div>
<script>
(function () {
  var h = React.createElement, DC = window.DigitalCrew, I = DC.Icons;
  function App() {
    var ref = React.useRef(null);
    React.useEffect(function () {
      // One short scripted sweep so the panel is not blank before the viewer moves the pointer.
      var box = ref.current, step = 0, timer;
      function tick() {
        if (!box || step > 40) return;
        var r = box.getBoundingClientRect(), t = step / 40;
        window.dispatchEvent(new MouseEvent("mousemove", {
          clientX: r.left + r.width * (0.1 + 0.8 * t),
          clientY: r.top + r.height * (0.5 + 0.28 * Math.sin(t * Math.PI * 2))
        }));
        step += 1;
        timer = setTimeout(tick, 16);
      }
      timer = setTimeout(tick, 400);
      return function () { clearTimeout(timer); };
    }, []);
    // The transform makes this box the containing block for SplashCursor's fixed, inset-0 canvas.
    return h("div", { ref: ref, className: "relative h-[320px] overflow-hidden rounded-xl border bg-background [transform:translateZ(0)]" },
      h(DC.SplashCursor, { quality: "low" }),
      h("div", { className: "pointer-events-none relative z-10 flex h-full flex-col items-center justify-center gap-1 text-center" },
        h("p", { className: "text-sm font-medium" }, "Move your pointer across this panel"),
        h("p", { className: "max-w-xs text-xs text-muted-foreground" }, "Skipped under reduced motion, on touch screens, and on devices with 4 cores or 4 GB of memory or less.")));
  }
  ReactDOM.createRoot(document.getElementById("root")).render(h(DC.TooltipProvider, { delayDuration: 80 }, h(App)));
})();
</script>