{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "paper-shaders",
  "title": "remocn ✕ Paper — Introducing shaders",
  "description": "remocn demo composition \"remocn ✕ Paper — Introducing shaders\" — installs the full Remotion composition. Generated with AI from the prompt in demos/paper-shaders/prompt.md.",
  "dependencies": [
    "@paper-design/shaders-react",
    "@remotion/google-fonts",
    "@remotion/transitions",
    "remotion"
  ],
  "registryDependencies": [
    "https://remocn.dev/r/blur-out-up.json",
    "https://remocn.dev/r/kinetic-center-build.json",
    "https://remocn.dev/r/line-by-line-slide.json",
    "https://remocn.dev/r/shader-dithering.json",
    "https://remocn.dev/r/shader-dot-orbit.json",
    "https://remocn.dev/r/shader-god-rays.json",
    "https://remocn.dev/r/shader-grain-gradient.json",
    "https://remocn.dev/r/shader-liquid-metal.json",
    "https://remocn.dev/r/shader-mesh-gradient.json",
    "https://remocn.dev/r/shader-metaballs.json",
    "https://remocn.dev/r/shader-neuro-noise.json",
    "https://remocn.dev/r/shader-pulsing-border.json",
    "https://remocn.dev/r/shader-spiral.json",
    "https://remocn.dev/r/shader-swirl.json",
    "https://remocn.dev/r/shader-voronoi.json",
    "https://remocn.dev/r/shader-warp.json",
    "https://remocn.dev/r/shader-water.json",
    "https://remocn.dev/r/short-slide-right.json"
  ],
  "files": [
    {
      "path": "src/demos/paper-shaders/index.tsx",
      "content": "import React from \"react\";\nimport {\n  AbsoluteFill,\n  Easing,\n  Series,\n  interpolate,\n  spring,\n  useCurrentFrame,\n  useVideoConfig,\n} from \"remotion\";\nimport {\n  TransitionSeries,\n  linearTiming,\n  type TransitionPresentation,\n  type TransitionPresentationComponentProps,\n} from \"@remotion/transitions\";\nimport { loadFont as loadSans } from \"@remotion/google-fonts/Manrope\";\nimport { loadFont as loadMono } from \"@remotion/google-fonts/GeistMono\";\n\nimport { BlurOutUp } from \"@/components/remocn/blur-out-up\";\nimport { ShortSlideRight } from \"@/components/remocn/short-slide-right\";\nimport { LineByLineSlide } from \"@/components/remocn/line-by-line-slide\";\nimport { KineticCenterBuild } from \"@/components/remocn/kinetic-center-build\";\n\nimport { ShaderGrainGradient } from \"@/components/remocn/shader-grain-gradient\";\nimport { ShaderMeshGradient } from \"@/components/remocn/shader-mesh-gradient\";\nimport { ShaderWarp } from \"@/components/remocn/shader-warp\";\nimport { ShaderLiquidMetal } from \"@/components/remocn/shader-liquid-metal\";\nimport { ShaderGodRays } from \"@/components/remocn/shader-god-rays\";\nimport { ShaderNeuroNoise } from \"@/components/remocn/shader-neuro-noise\";\nimport { ShaderVoronoi } from \"@/components/remocn/shader-voronoi\";\nimport { ShaderDotOrbit } from \"@/components/remocn/shader-dot-orbit\";\nimport { ShaderMetaballs } from \"@/components/remocn/shader-metaballs\";\nimport { ShaderWater } from \"@/components/remocn/shader-water\";\nimport { ShaderSpiral } from \"@/components/remocn/shader-spiral\";\nimport { ShaderSwirl } from \"@/components/remocn/shader-swirl\";\nimport { ShaderDithering } from \"@/components/remocn/shader-dithering\";\nimport { ShaderPulsingBorder } from \"@/components/remocn/shader-pulsing-border\";\n\n// Bind Manrope to the CSS variable the remocn typography components read.\nconst { fontFamily: SANS_FAMILY } = loadSans(\"normal\", {\n  subsets: [\"latin\"],\n  weights: [\"400\", \"500\", \"600\", \"700\", \"800\"],\n});\nconst { fontFamily: MONO_FAMILY } = loadMono(\"normal\", {\n  subsets: [\"latin\"],\n  weights: [\"400\", \"500\"],\n});\n\nconst SANS =\n  \"var(--font-geist-sans), -apple-system, BlinkMacSystemFont, sans-serif\";\nconst MONO = `${MONO_FAMILY}, ui-monospace, SFMono-Regular, monospace`;\n\nconst INK = \"#fafafa\";\nconst MUTED = \"rgba(250,250,250,0.62)\";\nconst FAINT = \"rgba(250,250,250,0.4)\";\nconst PAPER_BLUE = \"#81ADEC\";\n\nconst clampOpts = {\n  extrapolateLeft: \"clamp\" as const,\n  extrapolateRight: \"clamp\" as const,\n};\n\n// ---------------------------------------------------------------------------\n// Scene timings (frames @ 30fps). Transitions overlap.\n// ---------------------------------------------------------------------------\nconst S_HOOK = 78; //      \"Paper just open-sourced their shaders\"\nconst S_LOCKUP = 112; //   Remocn + Paper lockup\nconst S_INTRO = 76; //     \"Introducing remocn shaders\"\nconst BEAT_F = 36; //      one fullscreen shader per hard cut\nconst S_FEATURES = 92; //  three feature lines\nconst S_INSTALL = 104; //  one command away\nconst S_OUTRO = 156; //    fade in → swirl unwinds → holds → winds away → wordmark\n\nconst T_ZB = 18; // zoom-blur\nconst T_X = 16; // crossfade\n\n// ===========================================================================\n// The Paper logo mark (provided brand asset).\n// ===========================================================================\nconst PaperMark: React.FC<{ size: number }> = ({ size }) => (\n  <svg viewBox=\"0 0 39 39\" fill=\"none\" width={size} height={size}>\n    <path\n      d=\"M39 24H24V6H6V24H24V39H0V6H6V0H39V24Z\"\n      fill={PAPER_BLUE}\n    />\n  </svg>\n);\n\n// ===========================================================================\n// Fullscreen shader showcase — hard cuts, no transitions on purpose.\n// ===========================================================================\ntype Beat = { name: string; node: React.ReactNode };\n\nconst BEATS: Beat[] = [\n  {\n    name: \"shader-mesh-gradient\",\n    node: (\n      <ShaderMeshGradient\n        speed={2}\n        colors={[\"#12102a\", \"#3b2a80\", \"#7c5cff\", \"#c9b8ff\"]}\n        distortion={0.8}\n        swirl={0.2}\n      />\n    ),\n  },\n  {\n    name: \"shader-warp\",\n    node: (\n      <ShaderWarp\n        speed={2}\n        colors={[\"#041418\", \"#0d4f5c\", \"#2fb7c4\", \"#c8f4f7\"]}\n        swirl={0.6}\n      />\n    ),\n  },\n  {\n    name: \"shader-liquid-metal\",\n    node: (\n      <ShaderLiquidMetal speed={2} colorBack=\"#17171c\" colorTint=\"#c9c9d6\" />\n    ),\n  },\n  {\n    name: \"shader-god-rays\",\n    node: (\n      <ShaderGodRays\n        speed={2}\n        colorBack=\"#0d0a05\"\n        colorBloom=\"#6b4e17\"\n        colors={[\"#ffe4b3\", \"#f6b352\", \"#fff6e0\", \"#8a6a2a\"]}\n        intensity={1}\n        bloom={0.55}\n      />\n    ),\n  },\n  {\n    name: \"shader-neuro-noise\",\n    node: (\n      <ShaderNeuroNoise\n        speed={2}\n        colorFront=\"#7ef0c0\"\n        colorMid=\"#14503a\"\n        colorBack=\"#03100a\"\n        contrast={0.4}\n      />\n    ),\n  },\n  {\n    name: \"shader-voronoi\",\n    node: (\n      <ShaderVoronoi\n        speed={2}\n        colors={[\"#0e2a55\", \"#2b6cb0\", \"#66b2f0\", \"#cfe8ff\"]}\n        colorGap=\"#04080f\"\n        glow={0.15}\n      />\n    ),\n  },\n  {\n    name: \"shader-dot-orbit\",\n    node: (\n      <ShaderDotOrbit\n        speed={2}\n        colorBack=\"#0a0a12\"\n        colors={[\"#81adec\", \"#5a8fd8\", \"#e8f0fb\", \"#33538a\"]}\n      />\n    ),\n  },\n  {\n    name: \"shader-metaballs\",\n    node: (\n      <ShaderMetaballs\n        speed={2}\n        colorBack=\"#150a16\"\n        colors={[\"#ff6b9d\", \"#ff9e64\", \"#ffd166\", \"#c084fc\"]}\n      />\n    ),\n  },\n  {\n    name: \"shader-water\",\n    node: (\n      <ShaderWater\n        speed={2}\n        colorBack=\"#06121c\"\n        colorHighlight=\"#aee3f2\"\n        highlights={0.12}\n        caustic={0.12}\n      />\n    ),\n  },\n  {\n    name: \"shader-spiral\",\n    node: <ShaderSpiral speed={2} colorBack=\"#0a0a0a\" colorFront=\"#ececf2\" />,\n  },\n  {\n    name: \"shader-dithering\",\n    node: (\n      <ShaderDithering\n        speed={2}\n        colorBack=\"#0a0a10\"\n        colorFront={PAPER_BLUE}\n        size={3}\n      />\n    ),\n  },\n  {\n    name: \"shader-pulsing-border\",\n    node: (\n      <ShaderPulsingBorder\n        speed={2}\n        colorBack=\"#0b0b10\"\n        colors={[\"#81adec\", \"#a9c7f2\", \"#4a76b8\", \"#dfe9fa\"]}\n        intensity={0.35}\n        bloom={0.4}\n        thickness={0.12}\n      />\n    ),\n  },\n];\n\nconst S_SHOWCASE = BEATS.length * BEAT_F;\n\nconst ShowcaseScene: React.FC = () => (\n  <AbsoluteFill style={{ background: \"#0a0a0a\" }}>\n    <Series>\n      {BEATS.map((beat) => (\n        <Series.Sequence key={beat.name} durationInFrames={BEAT_F}>\n          <AbsoluteFill style={{ background: \"#0a0a0a\" }}>\n            {beat.node}\n          </AbsoluteFill>\n        </Series.Sequence>\n      ))}\n    </Series>\n  </AbsoluteFill>\n);\n\n// ===========================================================================\n// Scene 1 — Hook.\n// ===========================================================================\nconst HookScene: React.FC = () => (\n  <AbsoluteFill style={{ padding: \"0 90px\" }}>\n    <BlurOutUp\n      text=\"Paper just open-sourced their shaders\"\n      fontSize={58}\n      fontWeight={600}\n      color={INK}\n      staggerDelay={2}\n    />\n  </AbsoluteFill>\n);\n\n// ===========================================================================\n// Scene 2 — Lockup. Remocn + Paper, staggered entrances on a static flex row,\n// then a quiet subtitle resolves underneath.\n// ===========================================================================\nconst LockupScene: React.FC = () => {\n  const frame = useCurrentFrame();\n  const { fps } = useVideoConfig();\n\n  // Remocn slides in from the left.\n  const remocnIn = spring({\n    frame,\n    fps,\n    config: { damping: 15, stiffness: 120, mass: 0.9 },\n  });\n  const remocnX = interpolate(remocnIn, [0, 1], [-44, 0]);\n\n  // The plus pops between them.\n  const plusIn = spring({\n    frame: frame - 10,\n    fps,\n    config: { damping: 11, stiffness: 170, mass: 0.7 },\n  });\n  const plusOpacity = interpolate(frame, [10, 22], [0, 1], clampOpts);\n  const plusScale = interpolate(plusIn, [0, 1], [0.4, 1]);\n\n  // The Paper mark + word slide in from the right.\n  const paperIn = spring({\n    frame: frame - 16,\n    fps,\n    config: { damping: 14, stiffness: 130, mass: 0.9 },\n  });\n  const paperX = interpolate(paperIn, [0, 1], [44, 0]);\n  const paperOpacity = interpolate(frame, [16, 30], [0, 1], clampOpts);\n\n  // The mark breathes very slightly once settled.\n  const markFloat = Math.sin((frame - 40) / 22) * 3;\n\n  // Subtitle resolves after the lockup settles.\n  const subOpacity = interpolate(frame, [52, 70], [0, 1], clampOpts);\n  const subY = interpolate(frame, [52, 72], [14, 0], {\n    ...clampOpts,\n    easing: Easing.out(Easing.cubic),\n  });\n  const subBlur = interpolate(frame, [52, 70], [8, 0], clampOpts);\n\n  return (\n    <AbsoluteFill style={{ alignItems: \"center\", justifyContent: \"center\" }}>\n      <div\n        style={{\n          display: \"flex\",\n          alignItems: \"center\",\n          gap: 36,\n          transform: \"translateY(-26px)\",\n        }}\n      >\n        <span\n          style={{\n            fontFamily: SANS,\n            fontWeight: 700,\n            fontSize: 76,\n            letterSpacing: \"-0.03em\",\n            color: INK,\n            opacity: remocnIn,\n            transform: `translateX(${remocnX}px)`,\n            whiteSpace: \"nowrap\",\n          }}\n        >\n          Remocn\n        </span>\n        <span\n          style={{\n            fontFamily: SANS,\n            fontWeight: 400,\n            fontSize: 54,\n            color: FAINT,\n            opacity: plusOpacity,\n            transform: `scale(${plusScale})`,\n          }}\n        >\n          +\n        </span>\n        <div\n          style={{\n            display: \"flex\",\n            alignItems: \"center\",\n            gap: 22,\n            opacity: paperOpacity,\n            transform: `translateX(${paperX}px)`,\n          }}\n        >\n          <div style={{ transform: `translateY(${frame > 40 ? markFloat : 0}px)` }}>\n            <PaperMark size={78} />\n          </div>\n          <span\n            style={{\n              fontFamily: SANS,\n              fontWeight: 700,\n              fontSize: 76,\n              letterSpacing: \"-0.03em\",\n              color: INK,\n              whiteSpace: \"nowrap\",\n            }}\n          >\n            Paper\n          </span>\n        </div>\n      </div>\n\n      <span\n        style={{\n          position: \"absolute\",\n          top: 448,\n          fontFamily: SANS,\n          fontWeight: 500,\n          fontSize: 28,\n          color: MUTED,\n          opacity: subOpacity,\n          transform: `translateY(${subY}px)`,\n          filter: `blur(${subBlur}px)`,\n        }}\n      >\n        Their shaders, now wrapped for Remotion\n      </span>\n    </AbsoluteFill>\n  );\n};\n\n// ===========================================================================\n// Scene 3 — Introducing. The line slides in from the left via short-slide-right.\n// ===========================================================================\nconst IntroScene: React.FC = () => (\n  <AbsoluteFill>\n    <ShortSlideRight\n      text=\"Introducing Remocn Shaders\"\n      fontSize={66}\n      fontWeight={700}\n      color={INK}\n    />\n  </AbsoluteFill>\n);\n\n// ===========================================================================\n// Scene 5 — Features. Three lines, left-aligned block, centered.\n// ===========================================================================\nconst FeaturesScene: React.FC = () => (\n  <AbsoluteFill>\n    <LineByLineSlide\n      text={\n        \"18 shaders on the GPU\\nFrozen to the current frame\\nDeterministic on every render\"\n      }\n      fontSize={52}\n      fontWeight={600}\n      color={INK}\n    />\n  </AbsoluteFill>\n);\n\n// ===========================================================================\n// Scene 6 — Install. Kinetic headline, then the command resolves below.\n// ===========================================================================\nconst InstallScene: React.FC = () => {\n  const frame = useCurrentFrame();\n  const cmdOpacity = interpolate(frame, [52, 68], [0, 1], clampOpts);\n  const cmdY = interpolate(frame, [52, 72], [16, 0], {\n    ...clampOpts,\n    easing: Easing.out(Easing.cubic),\n  });\n  const cmdBlur = interpolate(frame, [52, 68], [6, 0], clampOpts);\n  return (\n    <AbsoluteFill>\n      <div\n        style={{\n          position: \"absolute\",\n          inset: 0,\n          transform: \"translateY(-42px)\",\n        }}\n      >\n        <KineticCenterBuild\n          text=\"Any shader, one command\"\n          fontSize={56}\n          fontWeight={700}\n          color={INK}\n        />\n      </div>\n      <div\n        style={{\n          position: \"absolute\",\n          left: 0,\n          right: 0,\n          top: 418,\n          display: \"flex\",\n          justifyContent: \"center\",\n          opacity: cmdOpacity,\n          transform: `translateY(${cmdY}px)`,\n          filter: `blur(${cmdBlur}px)`,\n        }}\n      >\n        <span style={{ fontFamily: MONO, fontSize: 28, color: MUTED }}>\n          <span style={{ color: FAINT }}>$ </span>\n          npx shadcn add{\" \"}\n          <span style={{ color: PAPER_BLUE }}>@remocn/shader-warp</span>\n        </span>\n      </div>\n    </AbsoluteFill>\n  );\n};\n\n// ===========================================================================\n// Scene 7 — Outro. The swirl opens up — twist animates 0 → 1 — and once the\n// bands have wound in, the wordmark resolves at the center.\n// ===========================================================================\nconst OutroScene: React.FC = () => {\n  const frame = useCurrentFrame();\n  const { fps } = useVideoConfig();\n\n  // The swirl reads rich below twist ≈ 0.4 and collapses into colorBack near 1.\n  // The scene fades in over the near-black twist=1 field first; only once the\n  // fade has settled does the twist unwind 1 → 0, emerging out of the dark.\n  // It holds open for a beat, then winds back 0 → 1, clearing the stage for\n  // the wordmark. The easing applies to each segment of the keyframe range.\n  const twist = interpolate(frame, [14, 48, 72, 112], [1, 0, 0, 1], {\n    ...clampOpts,\n    easing: Easing.bezier(0.42, 0, 0.58, 1),\n  });\n\n  const pop = spring({\n    frame: frame - 102,\n    fps,\n    config: { damping: 14, stiffness: 130, mass: 0.9 },\n  });\n  const scale = interpolate(pop, [0, 1], [0.92, 1]);\n  const blur = interpolate(frame, [102, 118], [10, 0], clampOpts);\n\n  const creditOpacity = interpolate(frame, [122, 140], [0, 1], clampOpts);\n\n  return (\n    <AbsoluteFill\n      style={{\n        alignItems: \"center\",\n        justifyContent: \"center\",\n        background: \"#0a0a0a\",\n      }}\n    >\n      <ShaderSwirl\n        twist={twist}\n        colors={[\"#1d2c4a\", \"#3f6cb0\", \"#81adec\"]}\n        colorBack=\"#0a0a0e\"\n        bandCount={10}\n        softness={0.35}\n      />\n      <span\n        style={{\n          fontFamily: SANS,\n          fontWeight: 800,\n          fontSize: 92,\n          letterSpacing: \"-0.03em\",\n          color: INK,\n          opacity: pop,\n          transform: `scale(${scale})`,\n          filter: `blur(${blur}px)`,\n          position: \"relative\",\n        }}\n      >\n        Remocn\n      </span>\n      <div\n        style={{\n          position: \"absolute\",\n          bottom: 44,\n          display: \"flex\",\n          alignItems: \"center\",\n          gap: 12,\n          opacity: creditOpacity,\n        }}\n      >\n        <PaperMark size={20} />\n        <span style={{ fontFamily: SANS, fontSize: 20, color: FAINT }}>\n          Shaders by Paper\n        </span>\n      </div>\n    </AbsoluteFill>\n  );\n};\n\n// ===========================================================================\n// Transition presentations.\n// ===========================================================================\ntype EmptyProps = Record<string, never>;\n\nconst Crossfade: React.FC<TransitionPresentationComponentProps<EmptyProps>> = ({\n  children,\n  presentationProgress,\n  presentationDirection,\n}) => {\n  const entering = presentationDirection === \"entering\";\n  const opacity = entering ? presentationProgress : 1 - presentationProgress;\n  return <AbsoluteFill style={{ opacity }}>{children}</AbsoluteFill>;\n};\nconst crossfade = (): TransitionPresentation<EmptyProps> => ({\n  component: Crossfade,\n  props: {},\n});\n\nconst ZoomBlur: React.FC<TransitionPresentationComponentProps<EmptyProps>> = ({\n  children,\n  presentationProgress,\n  presentationDirection,\n}) => {\n  const entering = presentationDirection === \"entering\";\n  const p = presentationProgress;\n  const style: React.CSSProperties = entering\n    ? {\n        opacity: p,\n        transform: `scale(${0.9 + p * 0.1})`,\n        filter: p < 1 ? `blur(${(1 - p) * 16}px)` : undefined,\n      }\n    : {\n        opacity: 1 - p,\n        transform: `scale(${1 + p * 0.12})`,\n        filter: p > 0 ? `blur(${p * 16}px)` : undefined,\n      };\n  return <AbsoluteFill style={style}>{children}</AbsoluteFill>;\n};\nconst zoomBlur = (): TransitionPresentation<EmptyProps> => ({\n  component: ZoomBlur,\n  props: {},\n});\n\n// ===========================================================================\n// Composition root.\n// ===========================================================================\nexport const PAPER_SHADERS_DURATION =\n  S_HOOK +\n  S_LOCKUP +\n  S_INTRO +\n  S_SHOWCASE +\n  S_FEATURES +\n  S_INSTALL +\n  S_OUTRO -\n  (T_ZB + T_X + T_X + T_X);\n\nexport const PaperShadersDemo: React.FC = () => {\n  return (\n    <AbsoluteFill\n      style={\n        {\n          \"--font-geist-sans\": SANS_FAMILY,\n          background: \"#0a0a0a\",\n        } as React.CSSProperties\n      }\n    >\n      {/* One of the new shaders carries the whole video as its backdrop. */}\n      <ShaderGrainGradient speed={0.5} />\n      {/* Scrim to keep the foreground copy readable. */}\n      <AbsoluteFill\n        style={{\n          background:\n            \"radial-gradient(120% 120% at 50% 42%, rgba(10,10,10,0.35) 0%, rgba(10,10,10,0.8) 100%)\",\n        }}\n      />\n\n      <TransitionSeries>\n        {/* 1 — Hook */}\n        <TransitionSeries.Sequence durationInFrames={S_HOOK}>\n          <HookScene />\n        </TransitionSeries.Sequence>\n        <TransitionSeries.Transition\n          timing={linearTiming({ durationInFrames: T_ZB })}\n          presentation={zoomBlur()}\n        />\n\n        {/* 2 — Remocn + Paper lockup */}\n        <TransitionSeries.Sequence durationInFrames={S_LOCKUP}>\n          <LockupScene />\n        </TransitionSeries.Sequence>\n        <TransitionSeries.Transition\n          timing={linearTiming({ durationInFrames: T_X })}\n          presentation={crossfade()}\n        />\n\n        {/* 3 — Introducing */}\n        <TransitionSeries.Sequence durationInFrames={S_INTRO}>\n          <IntroScene />\n        </TransitionSeries.Sequence>\n\n        {/* 4 — Fullscreen shader showcase, hard cut in and out */}\n        <TransitionSeries.Sequence durationInFrames={S_SHOWCASE}>\n          <ShowcaseScene />\n        </TransitionSeries.Sequence>\n\n        {/* 5 — Features */}\n        <TransitionSeries.Sequence durationInFrames={S_FEATURES}>\n          <FeaturesScene />\n        </TransitionSeries.Sequence>\n        <TransitionSeries.Transition\n          timing={linearTiming({ durationInFrames: T_X })}\n          presentation={crossfade()}\n        />\n\n        {/* 6 — Install */}\n        <TransitionSeries.Sequence durationInFrames={S_INSTALL}>\n          <InstallScene />\n        </TransitionSeries.Sequence>\n        <TransitionSeries.Transition\n          timing={linearTiming({ durationInFrames: T_X })}\n          presentation={crossfade()}\n        />\n\n        {/* 7 — Outro: fade to the dark twist=1 field, then the swirl unwinds */}\n        <TransitionSeries.Sequence durationInFrames={S_OUTRO}>\n          <OutroScene />\n        </TransitionSeries.Sequence>\n      </TransitionSeries>\n    </AbsoluteFill>\n  );\n};\n",
      "type": "registry:component",
      "target": "demos/paper-shaders/index.tsx"
    },
    {
      "path": "src/demos/paper-shaders/prompt.md",
      "content": "<!-- TODO(draft): placeholder written by AI — replace with the real prompt used to generate this video -->\n\nMake 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.\n",
      "type": "registry:file",
      "target": "demos/paper-shaders/prompt.md"
    }
  ],
  "docs": "Register the composition in your Remotion Root:\n\n  import { PaperShadersDemo } from \"@/demos/paper-shaders\";\n  <Composition id=\"paper-shaders\" component={PaperShadersDemo} durationInFrames={984} fps={30} width={1280} height={720} />\n\nRequires Tailwind v4 wired into Remotion (@remotion/tailwind-v4).\nRender locally: npx remotion render paper-shaders out/paper-shaders.mp4 --gl=angle",
  "type": "registry:block"
}