StealThis .dev
Remotion Medium

Gradient Wave (Remotion)

Abstract animated gradient wave background with layered sine curves and gradient text title — rendered with Remotion at 1280×720 30fps.

Open Remotion
remotion react typescript
Targets: TS React

Preview

Code

import {
  AbsoluteFill,
  Composition,
  interpolate,
  useCurrentFrame,
  useVideoConfig,
  Easing,
} from "remotion";

const WAVE_COUNT = 5;
const COLORS = ["#6366f1", "#8b5cf6", "#a855f7", "#d946ef", "#ec4899"];
const TITLE = "Gradient Wave";
const SUBTITLE = "Abstract motion background";

function wave(
  x: number,
  time: number,
  amplitude: number,
  frequency: number,
  phase: number
): number {
  return amplitude * Math.sin(frequency * x + time + phase);
}

const WaveLayer: React.FC<{ index: number; frame: number; width: number; height: number }> = ({
  index,
  frame,
  width,
  height,
}) => {
  const time = frame * 0.04;
  const baseY = height * 0.45 + index * 40;
  const color = COLORS[index % COLORS.length];
  const opacity = 0.15 + index * 0.05;

  const points: string[] = [];
  const steps = 80;

  for (let i = 0; i <= steps; i++) {
    const x = (i / steps) * width;
    const normalizedX = i / steps;
    const y =
      baseY +
      wave(normalizedX * Math.PI * 2, time + index * 0.8, 30 + index * 10, 2, index * 1.2) +
      wave(normalizedX * Math.PI * 4, time * 1.3 + index * 0.5, 15, 3, index * 0.7);
    points.push(`${x},${y}`);
  }

  // Close path at bottom
  const d = `M${points[0]} ${points.map((p) => `L${p}`).join(" ")} L${width},${height} L0,${height} Z`;

  return <path d={d} fill={color} opacity={opacity} />;
};

export const GradientWave: React.FC = () => {
  const frame = useCurrentFrame();
  const { fps, width, height } = useVideoConfig();

  const titleOpacity = interpolate(frame, [10, 30], [0, 1], {
    extrapolateLeft: "clamp",
    extrapolateRight: "clamp",
  });
  const titleY = interpolate(frame, [10, 30], [20, 0], {
    extrapolateLeft: "clamp",
    extrapolateRight: "clamp",
    easing: Easing.out(Easing.quad),
  });

  const subtitleOpacity = interpolate(frame, [20, 40], [0, 1], {
    extrapolateLeft: "clamp",
    extrapolateRight: "clamp",
  });

  // Overlay fades in
  const overlayOpacity = interpolate(frame, [0, 20], [0, 1], {
    extrapolateLeft: "clamp",
    extrapolateRight: "clamp",
  });

  return (
    <AbsoluteFill style={{ backgroundColor: "#0a0a14" }}>
      {/* Waves */}
      <svg
        width={width}
        height={height}
        style={{ position: "absolute", inset: 0, opacity: overlayOpacity }}
      >
        {Array.from({ length: WAVE_COUNT }).map((_, i) => (
          <WaveLayer key={i} index={i} frame={frame} width={width} height={height} />
        ))}
      </svg>

      {/* Top gradient overlay for text readability */}
      <div
        style={{
          position: "absolute",
          top: 0,
          left: 0,
          right: 0,
          height: "60%",
          background: "linear-gradient(180deg, #0a0a14 30%, transparent 100%)",
        }}
      />

      {/* Title */}
      <div style={{ position: "absolute", top: 120, left: 0, right: 0, textAlign: "center" }}>
        <div style={{ opacity: titleOpacity, transform: `translateY(${titleY}px)` }}>
          <span
            style={{
              fontFamily: "system-ui, sans-serif",
              fontWeight: 900,
              fontSize: 64,
              color: "#ffffff",
              letterSpacing: -2,
              background: `linear-gradient(135deg, ${COLORS[0]}, ${COLORS[4]})`,
              WebkitBackgroundClip: "text",
              WebkitTextFillColor: "transparent",
            }}
          >
            {TITLE}
          </span>
        </div>
        <div style={{ opacity: subtitleOpacity, marginTop: 12 }}>
          <span
            style={{
              fontFamily: "system-ui, sans-serif",
              fontWeight: 400,
              fontSize: 22,
              color: "rgba(255,255,255,0.5)",
            }}
          >
            {SUBTITLE}
          </span>
        </div>
      </div>
    </AbsoluteFill>
  );
};

export const RemotionRoot: React.FC = () => {
  return (
    <Composition
      id="GradientWave"
      component={GradientWave}
      durationInFrames={300}
      fps={30}
      width={1280}
      height={720}
    />
  );
};

Five layered sine-wave SVG paths that continuously animate, creating an abstract gradient ocean effect. Features a gradient-clipped title. Customize COLORS, WAVE_COUNT, TITLE, and SUBTITLE.