The Problem
The experiments landing page needed a visual signature that set it apart from the rest of the site. I had a React prototype using Framer Motion with complex SVG filters -- turbulence, displacement maps, colour matrices -- rendering caustic, iridescent floating objects. The challenge was porting all of that into the Svelte 5 and Tailwind stack without pulling in a heavy animation library.
What I Built
The effect is composed of four distinct components, each with a single responsibility:
- MaterialEngineFilters -- a hidden SVG
<defs>block containing the crinkle, grain, and bloom filter definitions. These accept aturbulenceScaleprop so they can be tuned at runtime. - IridescentObject -- a reusable floating shape (shards, circles, pills) that applies the SVG filters, dynamic linear gradients, and CSS drop-shadows. Movement is handled through CSS animations that respond to mouse position.
- CausticEnvironment -- the background layer rendering spectral palette blobs. It tracks mouse coordinates via a
$effectlistener onwindow.mousemoveand applies subtle parallax to the blobs. - CellophaneConfigPanel -- a floating sidebar visible only to authenticated admins, built with existing UI primitives (range sliders, labels). It controls reactive state for light angle, turbulence intensity, refraction, float speed, and amplitude.
The orchestration happens on the experiments page itself. A $state object holds all configuration parameters, the visual components read from it reactively, and the config panel writes to it. The existing page content (hero section, experiment cards) sits above the effect via z-index layering.
I deliberately avoided porting Framer Motion. Svelte's built-in reactivity and CSS keyframes handled everything the prototype needed. The mouse tracking required JavaScript, but all transform-based animations use CSS for better performance -- the browser's compositor handles them off the main thread.
Decisions
The spectral colours (like hsla(280, 85%, 75%, 0.45)) are hardcoded in the component style blocks rather than added as design tokens. These are specific to this isolated visual effect and do not belong in the global palette. The base layout still respects --bg-primary and --text-primary for everything outside the effect.
What is Next
The config panel state is ephemeral -- it resets on page reload. If I find myself frequently tuning specific parameter combinations, I will add persistence to PocketBase so admin-configured presets survive across sessions.