Splash cursor for Svelte

A full-viewport fluid trail that follows the pointer over your page. The Svelte take on the React “splash cursor” effect, running on WebGL.

Then move the pointer anywhere on this page. Respects prefers-reduced-motion (renders nothing).

Install with shadcn-svelte

npx shadcn-svelte@latest add https://svelte-fluid.dev/r/splash-cursor.json

This adds one small wrapper component (SplashCursor.svelte) to your components folder and installs svelte-fluid from npm. Render it once, for example in the root +layout.svelte:

<script lang="ts">
  import SplashCursor from '$lib/components/splash-cursor/SplashCursor.svelte';
</script>

<SplashCursor />

Any <Fluid> prop passes through, so you can retune it: <SplashCursor splatRadius={0.12} densityDissipation={1} />.

Copy-paste snippet

No registry needed. Requires npm install svelte-fluid and Svelte 5.

<script lang="ts">
  import { Fluid } from 'svelte-fluid';
</script>

<div class="splash-cursor" aria-hidden="true">
  <Fluid transparent pointerTarget="window" splatOnHover bloom={false} sunrays={false}
    densityDissipation={2} velocityDissipation={2} pressure={0.1} curl={3} splatRadius={0.25} />
</div>

<style>
  .splash-cursor { position: fixed; inset: 0; z-index: 9999; pointer-events: none; }
</style>

Notes

  • Pointer events. The wrapper is pointer-events: none, so clicks, hover, text selection and scrolling reach your page untouched. pointerTarget="window" makes the engine listen on window instead of the canvas, and splatOnHover splats on plain movement without a click.
  • z-index. 9999 draws the splash over everything, including modals. Lower it (for example z-index: 0 on a page with an opaque background layer) to draw it behind content.
  • Transparency. transparent composites the dye with premultiplied alpha over your page, so no background colour is needed. Dye fades through densityDissipation; lower it for longer trails.
  • Cost. One fixed canvas at the viewport size. bloom and sunrays are off to keep it cheap; maxPixelRatio defaults to 2.

Fallback behavior

  • No WebGL, or no half-float texture support: <Fluid> renders its inert fallback, which is transparent here, so the page is unchanged and nothing intercepts input. Use isWebGLAvailable() if you want to branch on support yourself.
  • prefers-reduced-motion: reduce: the shadcn wrapper renders nothing. In the raw snippet, wrap the block in your own matchMedia check.
  • The effect is decorative: the wrapper is aria-hidden and never takes focus.
  • SSR-safe: the engine starts in onMount; the server renders only the empty wrapper.

See Components for FluidBackground (fixed fluid behind content with exclusion zones) and Configuration for every tunable.