StealThis .dev

Split Screen Comparison (Remotion)

A before/after split-screen reveal with animated divider, mock UI cards, and labeled sides — rendered with Remotion at 1280×720 30fps.

Open Remotion
remotion react typescript
Targets: TS React

Preview

Code

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

const BEFORE_LABEL = "Before";
const AFTER_LABEL = "After";
const TITLE = "Design Refresh";
const ACCENT = "#10b981";

const MockCard: React.FC<{ modern: boolean }> = ({ modern }) => {
  if (!modern) {
    return (
      <div
        style={{
          width: 280,
          padding: 24,
          backgroundColor: "#d4d4d4",
          border: "2px solid #999",
          fontFamily: "serif",
        }}
      >
        <div style={{ width: 240, height: 120, backgroundColor: "#bbb", marginBottom: 12 }} />
        <div
          style={{
            fontFamily: "Times New Roman, serif",
            fontSize: 18,
            color: "#333",
            fontWeight: "bold",
            marginBottom: 8,
          }}
        >
          Old Design
        </div>
        <div
          style={{
            width: "100%",
            height: 8,
            backgroundColor: "#ccc",
            marginBottom: 6,
            borderRadius: 0,
          }}
        />
        <div
          style={{
            width: "80%",
            height: 8,
            backgroundColor: "#ccc",
            marginBottom: 6,
            borderRadius: 0,
          }}
        />
        <div style={{ width: "60%", height: 8, backgroundColor: "#ccc", borderRadius: 0 }} />
        <div
          style={{
            marginTop: 16,
            padding: "8px 16px",
            backgroundColor: "#666",
            color: "#fff",
            fontFamily: "serif",
            fontSize: 14,
            display: "inline-block",
          }}
        >
          Click Here
        </div>
      </div>
    );
  }

  return (
    <div
      style={{
        width: 280,
        padding: 24,
        backgroundColor: "rgba(255,255,255,0.05)",
        borderRadius: 16,
        border: "1px solid rgba(255,255,255,0.1)",
      }}
    >
      <div
        style={{
          width: 240,
          height: 120,
          borderRadius: 12,
          background: `linear-gradient(135deg, ${ACCENT}40, #6366f140)`,
          marginBottom: 16,
        }}
      />
      <div
        style={{
          fontFamily: "system-ui, sans-serif",
          fontSize: 18,
          color: "#ffffff",
          fontWeight: 700,
          marginBottom: 10,
        }}
      >
        New Design
      </div>
      <div
        style={{
          width: "100%",
          height: 6,
          backgroundColor: "rgba(255,255,255,0.1)",
          marginBottom: 6,
          borderRadius: 3,
        }}
      />
      <div
        style={{
          width: "80%",
          height: 6,
          backgroundColor: "rgba(255,255,255,0.1)",
          marginBottom: 6,
          borderRadius: 3,
        }}
      />
      <div
        style={{
          width: "60%",
          height: 6,
          backgroundColor: "rgba(255,255,255,0.1)",
          borderRadius: 3,
        }}
      />
      <div
        style={{
          marginTop: 16,
          padding: "10px 20px",
          backgroundColor: ACCENT,
          color: "#fff",
          fontFamily: "system-ui, sans-serif",
          fontSize: 14,
          fontWeight: 600,
          borderRadius: 8,
          display: "inline-block",
        }}
      >
        Get Started
      </div>
    </div>
  );
};

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

  // Split line animates from left to center
  const splitPos = spring({
    frame: Math.max(0, frame - 15),
    fps,
    from: 0,
    to: 0.5,
    config: { damping: 20, stiffness: 60 },
  });

  const titleOpacity = interpolate(frame, [0, 15], [0, 1], {
    extrapolateLeft: "clamp",
    extrapolateRight: "clamp",
  });
  const titleScale = spring({
    frame,
    fps,
    from: 0.85,
    to: 1,
    config: { damping: 14, stiffness: 100 },
  });

  const labelOpacity = interpolate(frame, [30, 50], [0, 1], {
    extrapolateLeft: "clamp",
    extrapolateRight: "clamp",
  });

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

  const fadeOut = interpolate(frame, [160, 180], [1, 0], {
    extrapolateLeft: "clamp",
    extrapolateRight: "clamp",
  });

  return (
    <AbsoluteFill style={{ backgroundColor: "#0a0a0f", opacity: fadeOut }}>
      {/* Title */}
      <div
        style={{
          position: "absolute",
          top: 30,
          left: 0,
          right: 0,
          textAlign: "center",
          opacity: titleOpacity,
          transform: `scale(${titleScale})`,
          zIndex: 10,
        }}
      >
        <span
          style={{
            fontFamily: "system-ui, sans-serif",
            fontWeight: 800,
            fontSize: 40,
            color: "#ffffff",
            letterSpacing: -1,
          }}
        >
          {TITLE}
        </span>
      </div>

      {/* Before side */}
      <div
        style={{
          position: "absolute",
          top: 0,
          left: 0,
          width: `${splitPos * 100}%`,
          height: "100%",
          backgroundColor: "#e8e8e8",
          overflow: "hidden",
          display: "flex",
          alignItems: "center",
          justifyContent: "center",
        }}
      >
        <div style={{ opacity: beforeOpacity }}>
          <MockCard modern={false} />
        </div>
        <div style={{ position: "absolute", top: 100, left: 20, opacity: labelOpacity }}>
          <span
            style={{
              fontFamily: "system-ui, sans-serif",
              fontWeight: 700,
              fontSize: 16,
              color: "#666",
              textTransform: "uppercase",
              letterSpacing: 2,
            }}
          >
            {BEFORE_LABEL}
          </span>
        </div>
      </div>

      {/* After side */}
      <div
        style={{
          position: "absolute",
          top: 0,
          right: 0,
          width: `${(1 - splitPos) * 100}%`,
          height: "100%",
          backgroundColor: "#0a0a0f",
          overflow: "hidden",
          display: "flex",
          alignItems: "center",
          justifyContent: "center",
        }}
      >
        <div style={{ opacity: afterOpacity }}>
          <MockCard modern />
        </div>
        <div style={{ position: "absolute", top: 100, right: 20, opacity: labelOpacity }}>
          <span
            style={{
              fontFamily: "system-ui, sans-serif",
              fontWeight: 700,
              fontSize: 16,
              color: ACCENT,
              textTransform: "uppercase",
              letterSpacing: 2,
            }}
          >
            {AFTER_LABEL}
          </span>
        </div>
      </div>

      {/* Split line */}
      <div
        style={{
          position: "absolute",
          top: 0,
          bottom: 0,
          left: `${splitPos * 100}%`,
          width: 3,
          backgroundColor: ACCENT,
          zIndex: 10,
          boxShadow: `0 0 20px ${ACCENT}80`,
        }}
      >
        {/* Handle */}
        <div
          style={{
            position: "absolute",
            top: "50%",
            left: "50%",
            transform: "translate(-50%, -50%)",
            width: 40,
            height: 40,
            borderRadius: "50%",
            backgroundColor: ACCENT,
            display: "flex",
            alignItems: "center",
            justifyContent: "center",
            boxShadow: `0 0 20px ${ACCENT}60`,
          }}
        >
          <span style={{ fontFamily: "system-ui, sans-serif", fontSize: 16, color: "#fff" }}>

          </span>
        </div>
      </div>
    </AbsoluteFill>
  );
};

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

Split-screen comparison that reveals a before/after view with an animated green divider sliding to center. The “before” side shows an old-style card and the “after” side shows a modern dark card. Customize TITLE, labels, and MockCard components.