All demos

remocn ✕ Paper — Introducing shaders

Install this video

Adds the full composition, its remocn dependencies, and the prompt to your Remotion project via the shadcn registry.

$ pnpm dlx shadcn@latest add kapishdima/remocn-demo/paper-shaders

Render it locally

Renders the MP4 on your machine with the Remotion CLI.

$ pnpm dlx remotion render paper-shaders out/paper-shaders.mp4 --scale=2 --crf=15 --x264-preset=slower --jpeg-quality=95 --gl=angle

The prompt

Reconstructed draft

A reconstruction of the prompt this video was generated from.

Make a launch video for the new shader components we just added to remocn, built on top of Paper's shaders now that they've open-sourced them. Set it in Manrope over a shader-grain-gradient background. Open with a blur-out-up hook — "Paper just open-sourced their shaders" — then assemble a Remocn + Paper lockup and slide in "Introducing remocn shaders". The centerpiece should be twelve of the shaders shown fullscreen, hard-cutting one into the next with no labels — mesh gradient, warp, liquid metal, god rays, neuro noise, voronoi, dot orbit, metaballs, water, spiral, dithering, pulsing border, basically the whole set. Follow with a short line-by-line pitch (18 shaders on the GPU, deterministic every render), the install command, and close with a shader-swirl unwinding into the remocn wordmark, crediting Paper.

The code

The exact source the AI wrote — the same files the install command puts in your project.

import React from "react";
import {
  AbsoluteFill,
  Easing,
  Series,
  interpolate,
  spring,
  useCurrentFrame,
  useVideoConfig,
} from "remotion";
import {
  TransitionSeries,
  linearTiming,
  type TransitionPresentation,
  type TransitionPresentationComponentProps,
} from "@remotion/transitions";
import { loadFont as loadSans } from "@remotion/google-fonts/Manrope";
import { loadFont as loadMono } from "@remotion/google-fonts/GeistMono";

import { BlurOutUp } from "@/components/remocn/blur-out-up";
import { ShortSlideRight } from "@/components/remocn/short-slide-right";
import { LineByLineSlide } from "@/components/remocn/line-by-line-slide";
import { KineticCenterBuild } from "@/components/remocn/kinetic-center-build";

import { ShaderGrainGradient } from "@/components/remocn/shader-grain-gradient";
import { ShaderMeshGradient } from "@/components/remocn/shader-mesh-gradient";
import { ShaderWarp } from "@/components/remocn/shader-warp";
import { ShaderLiquidMetal } from "@/components/remocn/shader-liquid-metal";
import { ShaderGodRays } from "@/components/remocn/shader-god-rays";
import { ShaderNeuroNoise } from "@/components/remocn/shader-neuro-noise";
import { ShaderVoronoi } from "@/components/remocn/shader-voronoi";
import { ShaderDotOrbit } from "@/components/remocn/shader-dot-orbit";
import { ShaderMetaballs } from "@/components/remocn/shader-metaballs";
import { ShaderWater } from "@/components/remocn/shader-water";
import { ShaderSpiral } from "@/components/remocn/shader-spiral";
import { ShaderSwirl } from "@/components/remocn/shader-swirl";
import { ShaderDithering } from "@/components/remocn/shader-dithering";
import { ShaderPulsingBorder } from "@/components/remocn/shader-pulsing-border";

// Bind Manrope to the CSS variable the remocn typography components read.
const { fontFamily: SANS_FAMILY } = loadSans("normal", {
  subsets: ["latin"],
  weights: ["400", "500", "600", "700", "800"],
});
const { fontFamily: MONO_FAMILY } = loadMono("normal", {
  subsets: ["latin"],
  weights: ["400", "500"],
});

const SANS =
  "var(--font-geist-sans), -apple-system, BlinkMacSystemFont, sans-serif";
const MONO = `${MONO_FAMILY}, ui-monospace, SFMono-Regular, monospace`;

const INK = "#fafafa";
const MUTED = "rgba(250,250,250,0.62)";
const FAINT = "rgba(250,250,250,0.4)";
const PAPER_BLUE = "#81ADEC";

const clampOpts = {
  extrapolateLeft: "clamp" as const,
  extrapolateRight: "clamp" as const,
};

// ---------------------------------------------------------------------------
// Scene timings (frames @ 30fps). Transitions overlap.
// ---------------------------------------------------------------------------
const S_HOOK = 78; //      "Paper just open-sourced their shaders"
const S_LOCKUP = 112; //   Remocn + Paper lockup
const S_INTRO = 76; //     "Introducing remocn shaders"
const BEAT_F = 36; //      one fullscreen shader per hard cut
const S_FEATURES = 92; //  three feature lines
const S_INSTALL = 104; //  one command away
const S_OUTRO = 156; //    fade in → swirl unwinds → holds → winds away → wordmark

const T_ZB = 18; // zoom-blur
const T_X = 16; // crossfade

// ===========================================================================
// The Paper logo mark (provided brand asset).
// ===========================================================================
const PaperMark: React.FC<{ size: number }> = ({ size }) => (
  <svg viewBox="0 0 39 39" fill="none" width={size} height={size}>
    <path
      d="M39 24H24V6H6V24H24V39H0V6H6V0H39V24Z"
      fill={PAPER_BLUE}
    />
  </svg>
);

// ===========================================================================
// Fullscreen shader showcase — hard cuts, no transitions on purpose.
// ===========================================================================
type Beat = { name: string; node: React.ReactNode };

const BEATS: Beat[] = [
  {
    name: "shader-mesh-gradient",
    node: (
      <ShaderMeshGradient
        speed={2}
        colors={["#12102a", "#3b2a80", "#7c5cff", "#c9b8ff"]}
        distortion={0.8}
        swirl={0.2}
      />
    ),
  },
  {
    name: "shader-warp",
    node: (
      <ShaderWarp
        speed={2}
        colors={["#041418", "#0d4f5c", "#2fb7c4", "#c8f4f7"]}
        swirl={0.6}
      />
    ),
  },
  {
    name: "shader-liquid-metal",
    node: (
      <ShaderLiquidMetal speed={2} colorBack="#17171c" colorTint="#c9c9d6" />
    ),
  },
  {
    name: "shader-god-rays",
    node: (
      <ShaderGodRays
        speed={2}
        colorBack="#0d0a05"
        colorBloom="#6b4e17"
        colors={["#ffe4b3", "#f6b352", "#fff6e0", "#8a6a2a"]}
        intensity={1}
        bloom={0.55}
      />
    ),
  },
  {
    name: "shader-neuro-noise",
    node: (
      <ShaderNeuroNoise
        speed={2}
        colorFront="#7ef0c0"
        colorMid="#14503a"
        colorBack="#03100a"
        contrast={0.4}
      />
    ),
  },
  {
    name: "shader-voronoi",
    node: (
      <ShaderVoronoi
        speed={2}
        colors={["#0e2a55", "#2b6cb0", "#66b2f0", "#cfe8ff"]}
        colorGap="#04080f"
        glow={0.15}
      />
    ),
  },
  {
    name: "shader-dot-orbit",
    node: (
      <ShaderDotOrbit
        speed={2}
        colorBack="#0a0a12"
        colors={["#81adec", "#5a8fd8", "#e8f0fb", "#33538a"]}
      />
    ),
  },
  {
    name: "shader-metaballs",
    node: (
      <ShaderMetaballs
        speed={2}
        colorBack="#150a16"
        colors={["#ff6b9d", "#ff9e64", "#ffd166", "#c084fc"]}
      />
    ),
  },
  {
    name: "shader-water",
    node: (
      <ShaderWater
        speed={2}
        colorBack="#06121c"
        colorHighlight="#aee3f2"
        highlights={0.12}
        caustic={0.12}
      />
    ),
  },
  {
    name: "shader-spiral",
    node: <ShaderSpiral speed={2} colorBack="#0a0a0a" colorFront="#ececf2" />,
  },
  {
    name: "shader-dithering",
    node: (
      <ShaderDithering
        speed={2}
        colorBack="#0a0a10"
        colorFront={PAPER_BLUE}
        size={3}
      />
    ),
  },
  {
    name: "shader-pulsing-border",
    node: (
      <ShaderPulsingBorder
        speed={2}
        colorBack="#0b0b10"
        colors={["#81adec", "#a9c7f2", "#4a76b8", "#dfe9fa"]}
        intensity={0.35}
        bloom={0.4}
        thickness={0.12}
      />
    ),
  },
];

const S_SHOWCASE = BEATS.length * BEAT_F;

const ShowcaseScene: React.FC = () => (
  <AbsoluteFill style={{ background: "#0a0a0a" }}>
    <Series>
      {BEATS.map((beat) => (
        <Series.Sequence key={beat.name} durationInFrames={BEAT_F}>
          <AbsoluteFill style={{ background: "#0a0a0a" }}>
            {beat.node}
          </AbsoluteFill>
        </Series.Sequence>
      ))}
    </Series>
  </AbsoluteFill>
);

// ===========================================================================
// Scene 1 — Hook.
// ===========================================================================
const HookScene: React.FC = () => (
  <AbsoluteFill style={{ padding: "0 90px" }}>
    <BlurOutUp
      text="Paper just open-sourced their shaders"
      fontSize={58}
      fontWeight={600}
      color={INK}
      staggerDelay={2}
    />
  </AbsoluteFill>
);

// ===========================================================================
// Scene 2 — Lockup. Remocn + Paper, staggered entrances on a static flex row,
// then a quiet subtitle resolves underneath.
// ===========================================================================
const LockupScene: React.FC = () => {
  const frame = useCurrentFrame();
  const { fps } = useVideoConfig();

  // Remocn slides in from the left.
  const remocnIn = spring({
    frame,
    fps,
    config: { damping: 15, stiffness: 120, mass: 0.9 },
  });
  const remocnX = interpolate(remocnIn, [0, 1], [-44, 0]);

  // The plus pops between them.
  const plusIn = spring({
    frame: frame - 10,
    fps,
    config: { damping: 11, stiffness: 170, mass: 0.7 },
  });
  const plusOpacity = interpolate(frame, [10, 22], [0, 1], clampOpts);
  const plusScale = interpolate(plusIn, [0, 1], [0.4, 1]);

  // The Paper mark + word slide in from the right.
  const paperIn = spring({
    frame: frame - 16,
    fps,
    config: { damping: 14, stiffness: 130, mass: 0.9 },
  });
  const paperX = interpolate(paperIn, [0, 1], [44, 0]);
  const paperOpacity = interpolate(frame, [16, 30], [0, 1], clampOpts);

  // The mark breathes very slightly once settled.
  const markFloat = Math.sin((frame - 40) / 22) * 3;

  // Subtitle resolves after the lockup settles.
  const subOpacity = interpolate(frame, [52, 70], [0, 1], clampOpts);
  const subY = interpolate(frame, [52, 72], [14, 0], {
    ...clampOpts,
    easing: Easing.out(Easing.cubic),
  });
  const subBlur = interpolate(frame, [52, 70], [8, 0], clampOpts);

  return (
    <AbsoluteFill style={{ alignItems: "center", justifyContent: "center" }}>
      <div
        style={{
          display: "flex",
          alignItems: "center",
          gap: 36,
          transform: "translateY(-26px)",
        }}
      >
        <span
          style={{
            fontFamily: SANS,
            fontWeight: 700,
            fontSize: 76,
            letterSpacing: "-0.03em",
            color: INK,
            opacity: remocnIn,
            transform: `translateX(${remocnX}px)`,
            whiteSpace: "nowrap",
          }}
        >
          Remocn
        </span>
        <span
          style={{
            fontFamily: SANS,
            fontWeight: 400,
            fontSize: 54,
            color: FAINT,
            opacity: plusOpacity,
            transform: `scale(${plusScale})`,
          }}
        >
          +
        </span>
        <div
          style={{
            display: "flex",
            alignItems: "center",
            gap: 22,
            opacity: paperOpacity,
            transform: `translateX(${paperX}px)`,
          }}
        >
          <div style={{ transform: `translateY(${frame > 40 ? markFloat : 0}px)` }}>
            <PaperMark size={78} />
          </div>
          <span
            style={{
              fontFamily: SANS,
              fontWeight: 700,
              fontSize: 76,
              letterSpacing: "-0.03em",
              color: INK,
              whiteSpace: "nowrap",
            }}
          >
            Paper
          </span>
        </div>
      </div>

      <span
        style={{
          position: "absolute",
          top: 448,
          fontFamily: SANS,
          fontWeight: 500,
          fontSize: 28,
          color: MUTED,
          opacity: subOpacity,
          transform: `translateY(${subY}px)`,
          filter: `blur(${subBlur}px)`,
        }}
      >
        Their shaders, now wrapped for Remotion
      </span>
    </AbsoluteFill>
  );
};

// ===========================================================================
// Scene 3 — Introducing. The line slides in from the left via short-slide-right.
// ===========================================================================
const IntroScene: React.FC = () => (
  <AbsoluteFill>
    <ShortSlideRight
      text="Introducing Remocn Shaders"
      fontSize={66}
      fontWeight={700}
      color={INK}
    />
  </AbsoluteFill>
);

// ===========================================================================
// Scene 5 — Features. Three lines, left-aligned block, centered.
// ===========================================================================
const FeaturesScene: React.FC = () => (
  <AbsoluteFill>
    <LineByLineSlide
      text={
        "18 shaders on the GPU\nFrozen to the current frame\nDeterministic on every render"

Showing the first 400 of 668 lines. View the full file on GitHub.