Motivka

Kinetic Cellophane Effect for Experiments

Ported a React prototype of iridescent floating SVG shapes into the Svelte 5 stack, adding a caustic background effect with mouse interactivity and an admin-only configuration panel to the experiments landing page.

3 Phases
5 Tasks
1 Days

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 a turbulenceScale prop 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 $effect listener on window.mousemove and 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.

Features Delivered

Key Decisions

  • Used CSS animations instead of Framer Motion port — Svelte's reactivity and CSS keyframes covered all needs without adding a heavy animation dependency
  • Hardcoded spectral colours in component styles — Effect-specific colours do not belong in the global design token palette
  • Ephemeral config state (no persistence) — Local $state is sufficient for development tweaking; persistence deferred until proven necessary