All demos

remocn ✕ shieldcn — New sponsor

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/sponsor-shieldcn

Render it locally

Renders the MP4 on your machine with the Remotion CLI.

$ pnpm dlx remotion render sponsor-shieldcn out/sponsor-shieldcn.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 sponsor shoutout for shieldcn, in their zinc-and-badge-green look, with a slow god-rays shader as a spotlight behind everything. Open on one of their real star-history badges springing in with a gentle float, then "Say hello to my new sponsor" resolving in with a soft blur. For the main beat, assemble a Remocn x shieldcn lockup — wordmark slides in, a cross fades between the two names, and their green shield-check chip springs into a ringed circle next to the name. For the finale, do a badge ballet — pull maybe ten of their real badges, have them resolve onto a slowly turning circle, let it do one full turn, then drop the badges into a row that rides off screen, closing on shieldcn.dev. Use remocn components for the badge and ring animations.

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, Img, Sequence, interpolate, spring, useCurrentFrame, useVideoConfig } from "remotion";
import { demoAsset } from "@/lib/demo-assets";
import {
  TransitionSeries,
  linearTiming,
  type TransitionPresentation,
  type TransitionPresentationComponentProps,
} from "@remotion/transitions";
import { loadFont as loadSans } from "@remotion/google-fonts/Geist";

import { ShaderGodRays } from "@/components/remocn/shader-god-rays";
import { SoftBlurIn } from "@/components/remocn/soft-blur-in";

// shieldcn speaks shadcn's language — Geist Sans everywhere.
const { fontFamily: SANS_FAMILY } = loadSans("normal", {
  subsets: ["latin"],
  weights: ["400", "500", "600", "700", "800"],
});

const SANS =
  "var(--font-geist-sans), -apple-system, BlinkMacSystemFont, sans-serif";

// shieldcn palette — the shadcn zinc register plus the badge-value green.
const BG = "#09090b";
const INK = "#fafafa";
const MUTED = "#a1a1aa";
const FAINT = "rgba(250,250,250,0.5)";

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

// ---------------------------------------------------------------------------
// Scene timings (frames @ 30fps). Transitions overlap.
// ---------------------------------------------------------------------------
const S_INTRO = 60; //   centered real badge, springing in with a float
const S_HOOK = 72; //    text animation
const S_MAIN = 112; //   Remocn ✕ shieldcn lockup
const S_BALLET = 220; // badge ballet: blur-formed circle → spin → row → exit

const T_X = 16; //  crossfade
const T_ZB = 18; // zoom-blur
const T_XB = 14; // crossfade into the ballet

export const SPONSOR_SHIELDCN_DURATION =
  S_INTRO + S_HOOK + S_MAIN + S_BALLET - (T_ZB + T_X + T_XB);

// The real shieldcn logo mark. When drawProgress is provided, the two paths
// draw themselves on as strokes and the fill resolves via fillProgress.
const LOGO_PATHS = [
  "M148.02,363.76c-4.48,0-8.64-2.42-10.86-6.32l-54.29-95.68c-2.15-3.8-2.15-8.52,0-12.32l54.29-95.68c2.21-3.9,6.37-6.32,10.86-6.32h18.51c4.44,0,8.45,2.28,10.73,6.09,2.27,3.82,2.37,8.43.25,12.33l-42.23,77.99c-3.98,7.36-3.98,16.14,0,23.49l22.22,41.02c4.25,7.85,12.43,12.8,21.36,12.92,0,0,45.08.61,45.11.61,8.68,0,16.83-4.64,21.26-12.12l24.87-41.99c2.23-3.77,6.34-6.11,10.72-6.12l19.47-.04c4.48,0,8.49,2.29,10.76,6.12,2.27,3.83,2.35,8.45.21,12.35l-42.2,77.17c-2.19,4-6.39,6.49-10.95,6.49h-110.08Z",
  "M346.7,363.69c-4.44,0-8.45-2.28-10.73-6.09-2.27-3.82-2.37-8.43-.25-12.33l42.23-77.99c3.98-7.35,3.98-16.14,0-23.49l-22.22-41.02c-4.25-7.85-12.44-12.8-21.36-12.92,0,0-46.51-.63-46.53-.63-8.88,0-17.12,4.81-21.48,12.54l-23.35,41.36c-2.2,3.9-6.36,6.34-10.84,6.35l-19.21.04c-4.48,0-8.49-2.29-10.76-6.12-2.27-3.83-2.35-8.45-.22-12.36l42.2-77.17c2.19-4.01,6.39-6.5,10.95-6.5h110.08c4.48,0,8.64,2.42,10.86,6.32l54.29,95.68c2.16,3.8,2.16,8.52,0,12.32l-54.29,95.68c-2.21,3.9-6.37,6.32-10.86,6.32h-18.51Z",
];

const ShieldcnLogo: React.FC<{
  size?: number;
  color?: string;
  drawProgress?: number;
  fillProgress?: number;
}> = ({ size = 96, color = INK, drawProgress, fillProgress }) => {
  const drawing = drawProgress !== undefined;
  return (
    <svg
      xmlns="http://www.w3.org/2000/svg"
      viewBox="0 0 512 512"
      width={size}
      height={size}
      aria-hidden
    >
      {LOGO_PATHS.map((d, i) => (
        <path
          key={i}
          d={d}
          fill={color}
          {...(drawing
            ? {
                pathLength: 1,
                strokeDasharray: 1,
                strokeDashoffset: 1 - drawProgress,
                stroke: color,
                strokeWidth: 10,
                strokeLinecap: "round" as const,
                fillOpacity: fillProgress ?? 1,
              }
            : {})}
        />
      ))}
    </svg>
  );
};

const RealBadge: React.FC<{ name: string; height?: number }> = ({
  name,
  height = 26,
}) => (
  <Img
    src={demoAsset(`shieldcn/${name}.svg`)}
    style={{ height, width: "auto", display: "block", flex: "none" }}
  />
);

// ===========================================================================
// Scene 1 — Intro. A single real shieldcn badge, centered, springing in with
// a gentle float — the badge plays the role sponsor-ln gave the emoji.
// ===========================================================================
const IntroScene: React.FC = () => {
  const frame = useCurrentFrame();
  const { fps } = useVideoConfig();
  const s = spring({
    frame,
    fps,
    config: { damping: 12, stiffness: 150, mass: 0.9 },
  });
  const scale = interpolate(s, [0, 1], [0.4, 1]);
  const float = Math.sin(frame / 14) * 8;
  return (
    <AbsoluteFill style={{ alignItems: "center", justifyContent: "center" }}>
      <div
        style={{
          opacity: s,
          transform: `translateY(${float}px) scale(${scale})`,
          filter: "drop-shadow(0 22px 44px rgba(0,0,0,0.5))",
        }}
      >
        <RealBadge name="stars-shieldcn" height={84} />
      </div>
    </AbsoluteFill>
  );
};

// ===========================================================================
// Scene 2 — Hook. Text animation via remocn's soft-blur-in typography.
// ===========================================================================
const HookScene: React.FC = () => (
  <AbsoluteFill style={{ padding: "0 90px" }}>
    <SoftBlurIn
      text="Say hello to my new sponsor"
      fontSize={54}
      fontWeight={600}
      color={INK}
      blur={14}
    />
  </AbsoluteFill>
);

// ===========================================================================
// Scene 3 — Main. The sponsor lockup, same choreography as sponsor-ln:
//   1. The shield mark + name pop in, centered, and hold briefly.
//   2. The (mark + name) group slides right while the Remocn wordmark pushes
//      in from the left and a cross appears between them.
// ===========================================================================
const MARK_SIZE = 168; //      DOM (entrance) size of the shield chip
const MARK_FINAL_SIZE = 84; // shrinks to this once settled inline
const FINAL_SCALE = MARK_FINAL_SIZE / MARK_SIZE;

// Final-layout horizontal centres (canvas is 1280 wide → centre at 640).
const REMOCN_X = 420; // centre of the "Remocn" wordmark
// Right edge of "Remocn" as rendered at fontSize 76 / weight 700 (measured).
const REMOCN_RIGHT = REMOCN_X + 141;
const MARK_X = 750; // centre of the shield chip
// The SVG box carries ~14px of dead space around the drawn mark at inline
// size, so a tiny gap keeps the name visually snug against the logo.
const NAME_GAP = 2;
const CROSS_X = Math.round(
  (REMOCN_RIGHT + (MARK_X - MARK_FINAL_SIZE / 2)) / 2,
);
const ROW_Y = 350; // vertical centre of the row

// Frame the group starts sliding right (and Remocn / cross start entering) —
// after the logo has finished drawing itself on.
const SPLIT_AT = 60;

// The bare logo mark, exactly as the shieldcn site renders it: currentColor
// (foreground ink), no chip, no border. Draws itself on as a stroke.
const ShieldMark: React.FC<{ draw: number; fill: number }> = ({
  draw,
  fill,
}) => (
  <div
    style={{
      width: MARK_SIZE,
      height: MARK_SIZE,
      display: "flex",
      alignItems: "center",
      justifyContent: "center",
      filter: "drop-shadow(0 18px 40px rgba(0,0,0,0.5))",
    }}
  >
    <ShieldcnLogo size={MARK_SIZE} drawProgress={draw} fillProgress={fill} />
  </div>
);

const MainScene: React.FC = () => {
  const frame = useCurrentFrame();
  const { fps } = useVideoConfig();

  // 1 — The logo draws itself on, centered; the fill and the name resolve
  // while the stroke completes. Starts after the incoming crossfade ends so
  // the stroke never draws over the outgoing hook text.
  const draw = interpolate(frame, [16, 46], [0, 1], {
    ...clampOpts,
    easing: Easing.inOut(Easing.cubic),
  });
  const fill = interpolate(frame, [38, 54], [0, 1], clampOpts);
  const nameIn = interpolate(frame, [30, 46], [0, 1], clampOpts);

  // 2 — The group slides from screen centre (640) to its final slot (MARK_X).
  const slide = spring({
    frame: frame - SPLIT_AT,
    fps,
    config: { damping: 16, stiffness: 110, mass: 1 },
  });
  const groupX = interpolate(slide, [0, 1], [640 - MARK_X, 0]);
  // As it slides right, the chip shrinks down to the inline size.
  const shrink = interpolate(slide, [0, 1], [1, FINAL_SCALE], clampOpts);
  const markScale = shrink;
  // "shieldcn" follows the chip's (shrinking) right edge at a fixed gap.
  const nameLeft = MARK_X + (MARK_SIZE * shrink) / 2 + NAME_GAP;

  // Remocn pushes in from the left, in sync with the slide.
  const remocnOpacity = interpolate(
    frame,
    [SPLIT_AT + 2, SPLIT_AT + 24],
    [0, 1],
    clampOpts,
  );
  const remocnX = interpolate(frame, [SPLIT_AT + 2, SPLIT_AT + 26], [-36, 0], {
    ...clampOpts,
    easing: Easing.out(Easing.cubic),
  });

  // The cross appears between them, slightly after the split begins.
  const crossSpring = spring({
    frame: frame - (SPLIT_AT + 8),
    fps,
    config: { damping: 11, stiffness: 170, mass: 0.7 },
  });
  const crossOpacity = interpolate(
    frame,
    [SPLIT_AT + 8, SPLIT_AT + 20],
    [0, 1],
    clampOpts,
  );
  const crossScale = interpolate(crossSpring, [0, 1], [0.5, 1]);

  return (
    <AbsoluteFill>
      {/* Remocn wordmark — enters from the left. */}
      <span
        style={{
          position: "absolute",
          left: REMOCN_X,
          top: ROW_Y,
          transform: `translate(-50%, -50%) translateX(${remocnX}px)`,
          fontFamily: SANS,
          fontWeight: 700,
          fontSize: 76,
          letterSpacing: "-0.03em",
          color: INK,
          opacity: remocnOpacity,
          whiteSpace: "nowrap",
        }}
      >
        Remocn
      </span>

      {/* Cross — appears between the two. */}
      <span
        style={{
          position: "absolute",
          left: CROSS_X,
          top: ROW_Y,
          transform: `translate(-50%, -50%) scale(${crossScale})`,
          fontFamily: SANS,
          fontWeight: 400,
          fontSize: 52,
          color: FAINT,
          opacity: crossOpacity,
        }}
      >

      </span>

      {/* Shield mark — draws on centred, then slides right with the name. */}
      <div
        style={{
          position: "absolute",
          left: MARK_X,
          top: ROW_Y,
          transform: `translate(-50%, -50%) translateX(${groupX}px) scale(${markScale})`,
        }}
      >
        <ShieldMark draw={draw} fill={fill} />
      </div>

      {/* Sponsor name — sits to the right of the chip and travels with it. */}
      <span
        style={{
          position: "absolute",
          left: nameLeft,
          top: ROW_Y,
          transform: `translate(0, -50%) translateX(${groupX}px)`,
          fontFamily: SANS,
          fontWeight: 500,
          fontSize: 44,
          color: INK,
          letterSpacing: "-0.02em",
          opacity: nameIn,
        }}
      >
        shieldcn
      </span>
    </AbsoluteFill>
  );
};

// ===========================================================================
// Scene 4 — Finale. The badge ballet from the shieldcn demo, with a more
// cinematic formation: instead of a scattered pop, each badge resolves out of
// blur + opacity already on the circle, drifting inward from a wider radius
// while the whole ring slowly winds into place. Then the full turn, the fall
// into a row, and the ride off-screen — closed by the shieldcn.dev tag.
// ===========================================================================
const BALLET_BADGES = [
  "npm-react",
  "stars-nextjs",
  "views-shieldcn",
  "license-shieldcn",
  "stars-shieldcn",
  "npm-react-secondary",
  "npm-react-outline",
  "npm-react-ghost",
  "npm-react-destructive",
  "npm-typescript",
];

const BALLET_N = BALLET_BADGES.length;
const BALLET_R = 190; //  circle radius
const BALLET_SLOT = 116; // row slot width

// Ballet timeline (scene-local frames)
const B_FORM_STAGGER = 3; // per-badge delay of the blur resolve
const B_FORM_DUR = 26; //   per-badge blur/opacity resolve length
const B_WIND: [number, number] = [0, 64]; // ring slowly winds into place
const B_SPIN: [number, number] = [66, 116];
const B_ALIGN: [number, number] = [116, 138];
const B_EXIT: [number, number] = [140, 160];
// The outro rides in on the tail of the exiting badge row.
const OUTRO_FROM = 154;

const BadgeBallet: React.FC = () => {
  const frame = useCurrentFrame();
  const ease = Easing.inOut(Easing.cubic);
  // The ring settles from a slight counter-rotation while badges resolve.
  const wind = interpolate(frame, B_WIND, [-0.4, 0], {
    ...clampOpts,
    easing: Easing.out(Easing.cubic),
  });
  const rot = interpolate(frame, B_SPIN, [0, Math.PI * 2], {
    ...clampOpts,
    easing: ease,
  });
  const aP = interpolate(frame, B_ALIGN, [0, 1], {
    ...clampOpts,
    easing: ease,
  });
  const eP = interpolate(frame, B_EXIT, [0, 1], {
    ...clampOpts,
    easing: Easing.in(Easing.cubic),
  });
  return (
    <>
      {BALLET_BADGES.map((name, i) => {
        const t0 = 4 + i * B_FORM_STAGGER;
        // Cinematic formation — each badge resolves out of blur + opacity,
        // drifting inward from a wider radius onto its circle slot.
        const form = interpolate(frame, [t0, t0 + B_FORM_DUR], [0, 1], {
          ...clampOpts,
          easing: Easing.out(Easing.cubic),
        });
        const radius = BALLET_R * (1.35 - 0.35 * form);
        const blurVal = (1 - form) * 18;
        const formScale = 1.16 - 0.16 * form;

        const base = -Math.PI / 2 + (i * 2 * Math.PI) / BALLET_N;
        const cx = Math.cos(base + wind + rot) * radius;
        const cy = Math.sin(base + wind + rot) * radius;
        // circle (carrying the spin) → row → exit
        const rowX = (i - (BALLET_N - 1) / 2) * BALLET_SLOT;
        const x = cx + (rowX - cx) * aP - eP * 1750;
        const y = cy + (0 - cy) * aP;
        return (
          <div
            key={name}
            style={{
              position: "absolute",
              left: "50%",
              top: "50%",

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