← All elements

Silk Flow

Abstract

Dozens of fine light strands flowing together in a silky iridescent wave.

Tip: pick a preset, use the custom picker, or paste your brand hex to preview this element in your colors. Some elements use a fixed multi-color palette.

Usage
Install: three @react-three/fiber @react-three/drei
Drop <SilkFlow /> inside your own <Canvas>.
Mood
Fluid, elegant, premium. Light woven like silk.
Colors
Strands graded cyan to violet to magenta, blended additively into a glowing ribbon.
Motion
The whole weave sways and breathes slowly.
View component source
import { useMemo, useRef } from 'react';
import { useFrame } from '@react-three/fiber';
import * as THREE from 'three';
import type { Group } from 'three';

const COUNT = 64;

class StrandCurve extends THREE.Curve<THREE.Vector3> {
  amp: number;
  phase: number;
  yoff: number;
  zoff: number;
  constructor(amp: number, phase: number, yoff: number, zoff: number) {
    super();
    this.amp = amp;
    this.phase = phase;
    this.yoff = yoff;
    this.zoff = zoff;
  }
  getPoint(t: number, target = new THREE.Vector3()) {
    const x = (t - 0.5) * 7;
    const env = Math.cos((t - 0.5) * Math.PI); // taper the ends
    const y = Math.sin(x * 0.85 + this.phase) * this.amp * env + this.yoff;
    const z = this.zoff + Math.cos(x * 0.6 + this.phase) * 0.2;
    return target.set(x, y, z);
  }
}

export default function SilkFlow({ scale = 1 }: { color?: string; scale?: number }) {
  const g = useRef<Group>(null);
  const strands = useMemo(() => {
    const c1 = new THREE.Color('#22e0ff');
    const c2 = new THREE.Color('#8a5cff');
    const c3 = new THREE.Color('#ff2fd0');
    const tmp = new THREE.Color();
    return Array.from({ length: COUNT }, (_, i) => {
      const t = i / (COUNT - 1);
      if (t < 0.5) tmp.copy(c1).lerp(c2, t * 2);
      else tmp.copy(c2).lerp(c3, (t - 0.5) * 2);
      const amp = 0.7 + Math.sin(i * 1.3) * 0.5;
      const phase = i * 0.11;
      const yoff = (t - 0.5) * 0.5;
      const zoff = (t - 0.5) * 0.9;
      const geo = new THREE.TubeGeometry(new StrandCurve(amp, phase, yoff, zoff), 140, 0.006, 6, false);
      return { geo, color: tmp.getStyle() };
    });
  }, []);
  useFrame((state) => {
    if (!g.current) return;
    g.current.rotation.z = Math.sin(state.clock.elapsedTime * 0.15) * 0.06;
    g.current.rotation.y = Math.sin(state.clock.elapsedTime * 0.1) * 0.12;
  });
  return (
    <group ref={g} scale={scale}>
      {strands.map((s, i) => (
        <mesh key={i} geometry={s.geo}>
          <meshBasicMaterial
            color={s.color}
            toneMapped={false}
            transparent
            opacity={0.85}
            blending={THREE.AdditiveBlending}
            depthWrite={false}
          />
        </mesh>
      ))}
    </group>
  );
}