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 onwindowinstead of the canvas, andsplatOnHoversplats on plain movement without a click. - z-index.
9999draws the splash over everything, including modals. Lower it (for examplez-index: 0on a page with an opaque background layer) to draw it behind content. - Transparency.
transparentcomposites the dye with premultiplied alpha over your page, so no background colour is needed. Dye fades throughdensityDissipation; lower it for longer trails. - Cost. One fixed canvas at the viewport size.
bloomandsunraysare off to keep it cheap;maxPixelRatiodefaults 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. UseisWebGLAvailable()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 ownmatchMediacheck.- The effect is decorative: the wrapper is
aria-hiddenand 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.