All demos

remocn — A new logo

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/remocn-new-logo

Render it locally

Renders the MP4 on your machine with the Remotion CLI.

$ pnpm dlx remotion render remocn-new-logo out/remocn-new-logo.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.

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,
  Sequence,
  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 { ScaleDownFade } from "@/components/remocn/scale-down-fade";
import { pushThrough } from "@/components/remocn/push-through";
import { ShaderWarp } from "@/components/remocn/shader-warp";

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 = `${SANS_FAMILY}, -apple-system, BlinkMacSystemFont, sans-serif`;
const MONO = `${MONO_FAMILY}, ui-monospace, SFMono-Regular, monospace`;

// The shipped remocn.dev brand: warm obsidian + one lime accent.
const OBSIDIAN = "#141318";
const INK = "#f2f2f2";
const MUTED = "rgba(242,242,242,0.62)";
const FAINT = "rgba(242,242,242,0.4)";
const LIME = "#C3E88D";
// The logo's plate: black.
const PLATE = "#000000";
const HAIRLINE = "rgba(242,242,242,0.1)";

// X's own palette, used only inside the X mockup.
const X_INK = "#e7e9ea";
const X_MUTED = "#71767b";
const X_BLUE = "#1d9bf0";

// GitHub's own palette, used only inside the GitHub mockup.
const GH_BG = "rgba(13,17,23,0.96)";
const GH_INK = "#e6edf3";
const GH_MUTED = "#9198a1";

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

// The deep-blue paper.design warp preset that carries the whole video (and
// doubles as the X profile's banner image).
const WARP_PROPS = {
  speed: 2.8,
  colors: ["#000d14", "#001929", "#00263d", "#003557", "#005285"],
  proportion: 0.05,
  softness: 0.07,
  distortion: 0.29,
  swirl: 0.55,
  swirlIterations: 13.4,
  shape: "checks" as const,
  shapeScale: 0.34,
  scale: 0.8,
  rotation: 0,
};

// ---------------------------------------------------------------------------
// Scene timings (frames @ 30fps). Transitions overlap.
// ---------------------------------------------------------------------------
const S_HOOK = 124; //   two scale-down-fade lines
const S_CODE = 130; //   the logo's SVG source types itself
const S_MORPH = 530; //  one continuous shot: draw → X → GitHub → favicon →
//                       reveal, the mark tile morphing between every context

const T_X = 14; //       crossfade into the code scene
const T_PUSH = 40; //    push-through INTO the code

// ===========================================================================
// The mark — the R letterform on its black plate. The R path is inlined so
// the morph scene can draw it on (pathLength-normalized dashoffset).
// ===========================================================================
const R_VIEWBOX = "0 0 131.75 144.15";
const R_RATIO = 131.75 / 144.15;
const R_PATH =
  "M 0.35 7.55 L 0.35 0.35 L 86.55 0.35 C 110.55 1.35, 131.25 24.05, 131.25 47.55 C 131.25 71.05, 112.55 88.55, 93.55 94.35 L 131.75 144.15 L 101.55 144.15 C 96.55 144.15, 94.55 141.55, 93.25 138.45 L 63.95 104.05 C 60.55 98.75, 56.55 97.35, 51.55 97.35 L 43.05 97.35 C 35.05 98.05, 28.85 102.55, 28.85 112.05 L 28.85 144.15 L 0.35 144.15 L 0.35 106.05 C 0.35 88.05, 21.55 71.05, 32.55 69.65 L 86.55 66.55 C 96.55 65.55, 102.15 58.55, 102.15 47.55 C 102.15 36.55, 96.05 30.35, 84.55 29.25 L 28.55 29.05 C 16.55 28.05, 3.05 17.55, 0.35 7.55 Z";

// How many viewBox units get eroded off the letterform (half per edge), so
// the R's strokes sit at text weight next to the Manrope wordmark.
const R_THIN = 6;

// `thin` is a 0→1 progress: at 0 the mark is the original letterform with its
// persistent outline (the draw/fill animation plays on this, exactly as the
// original design), at 1 it is the eroded, text-weight letter with no
// outline. The morph scene blends it during the shrink into the X avatar,
// where the motion hides the weight change.
const RemocnMark: React.FC<{
  height: number;
  draw?: number;
  fill?: number;
  color?: string;
  thin?: number;
}> = ({ height, draw = 1, fill = 1, color = INK, thin = 1 }) => {
  const maskId = React.useId();
  return (
    <svg
      viewBox={R_VIEWBOX}
      width={height * R_RATIO}
      height={height}
      style={{ display: "block", color, overflow: "visible" }}
    >
      {/* The black stroke inside the mask eats thin·R_THIN/2 off every
          edge — a uniform geometric thinning that works over any background. */}
      <mask
        id={maskId}
        maskUnits="userSpaceOnUse"
        x={-10}
        y={-10}
        width={152}
        height={165}
      >
        <path
          d={R_PATH}
          fill="#ffffff"
          stroke="#000000"
          strokeWidth={R_THIN * thin}
          strokeLinejoin="round"
        />
      </mask>
      <rect
        x={-10}
        y={-10}
        width={152}
        height={165}
        fill="currentColor"
        opacity={fill}
        mask={`url(#${maskId})`}
      />
      {/* The outline: it traces the letter on during the draw, stays put
          while the color fills in beneath it, and only leaves as the mark
          thins down. */}
      <path
        d={R_PATH}
        pathLength={1}
        fill="none"
        stroke="currentColor"
        strokeWidth={2.2}
        strokeLinejoin="round"
        strokeDasharray={1}
        strokeDashoffset={1 - draw}
        opacity={1 - thin}
      />
    </svg>
  );
};

// The R + wordmark lockup used inside the X banner.
const MiniLockup: React.FC<{ markH: number; fontSize: number }> = ({
  markH,
  fontSize,
}) => (
  <div style={{ display: "flex", alignItems: "flex-end" }}>
    <div style={{ marginBottom: Math.round(fontSize * 0.115) }}>
      <RemocnMark height={markH} color="#ffffff" />
    </div>
    <span
      style={{
        lineHeight: 1,
        fontFamily: SANS,
        fontWeight: 400,
        fontSize,
        letterSpacing: "-0.03em",
        color: "#ffffff",
      }}
    >
      emocn
    </span>
  </div>
);

// ---------------------------------------------------------------------------
// Tiny stroke icons for the mockup chrome.
// ---------------------------------------------------------------------------
const Icon: React.FC<{ size?: number; children: React.ReactNode }> = ({
  size = 16,
  children,
}) => (
  <svg
    viewBox="0 0 24 24"
    width={size}
    height={size}
    fill="none"
    stroke="currentColor"
    strokeWidth={1.8}
    strokeLinecap="round"
    strokeLinejoin="round"
    style={{ display: "block", flexShrink: 0 }}
  >
    {children}
  </svg>
);

const ArrowLeftIcon = () => (
  <Icon size={20}>
    <path d="M19 12H5" />
    <path d="m12 19-7-7 7-7" />
  </Icon>
);
const SearchIcon = () => (
  <Icon size={18}>
    <circle cx={11} cy={11} r={7} />
    <path d="m21 21-4.5-4.5" />
  </Icon>
);
const GrokIcon = () => (
  <Icon size={18}>
    <circle cx={12} cy={12} r={9} />
    <path d="m6 18 12-12" />
  </Icon>
);
const BriefcaseIcon = () => (
  <Icon>
    <rect x={2.5} y={7} width={19} height={13} rx={2} />
    <path d="M16 7V5a2 2 0 0 0-2-2h-4a2 2 0 0 0-2 2v2" />
  </Icon>
);
const PinIcon = () => (
  <Icon>
    <path d="M20 10c0 6-8 12-8 12s-8-6-8-12a8 8 0 0 1 16 0Z" />
    <circle cx={12} cy={10} r={3} />
  </Icon>
);
const LinkIcon = () => (
  <Icon>
    <path d="M9 17H7A5 5 0 0 1 7 7h2" />
    <path d="M15 7h2a5 5 0 1 1 0 10h-2" />
    <path d="M8 12h8" />
  </Icon>
);
const CalendarIcon = () => (
  <Icon>
    <rect x={3} y={4} width={18} height={17} rx={2} />
    <path d="M16 2v4M8 2v4M3 10h18" />
  </Icon>
);
const UsersIcon = () => (
  <Icon size={14}>
    <path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2" />
    <circle cx={9} cy={7} r={4} />
    <path d="M22 21v-2a4 4 0 0 0-3-3.87" />
    <path d="M16 3.13a4 4 0 0 1 0 7.75" />
  </Icon>
);
const MailIcon = () => (
  <Icon size={14}>
    <rect x={2} y={4} width={20} height={16} rx={2} />
    <path d="m22 7-10 5L2 7" />
  </Icon>
);
const XLogoIcon: React.FC<{ size?: number }> = ({ size = 13 }) => (
  <svg
    viewBox="0 0 24 24"
    width={size}
    height={size}
    fill="currentColor"
    style={{ display: "block", flexShrink: 0 }}
  >
    <path d="M18.901 1.153h3.68l-8.04 9.19L24 22.846h-7.406l-5.8-7.584-6.638 7.584H.474l8.6-9.83L0 1.154h7.594l5.243 6.932ZM17.61 20.644h2.039L6.486 3.24H4.298Z" />
  </svg>
);
const VerifiedSeal: React.FC<{ size?: number }> = ({ size = 17 }) => (
  <svg
    width={size}
    height={size}
    viewBox="0 0 22 22"
    aria-label="Verified account"
    role="img"
    data-testid="icon-verified"
  >
    <g>
      <path
        d="M20.396 11c-.018-.646-.215-1.275-.57-1.816-.354-.54-.852-.972-1.438-1.246.223-.607.27-1.264.14-1.897-.131-.634-.437-1.218-.882-1.687-.47-.445-1.053-.75-1.687-.882-.633-.13-1.29-.083-1.897.14-.273-.587-.704-1.086-1.245-1.44S11.647 1.62 11 1.604c-.646.017-1.273.213-1.813.568s-.969.854-1.24 1.44c-.608-.223-1.267-.272-1.902-.14-.635.13-1.22.436-1.69.882-.445.47-.749 1.055-.878 1.688-.13.633-.08 1.29.144 1.896-.587.274-1.087.705-1.443 1.245-.356.54-.555 1.17-.574 1.817.02.647.218 1.276.574 1.817.356.54.856.972 1.443 1.245-.224.606-.274 1.263-.144 1.896.13.634.433 1.218.877 1.688.47.443 1.054.747 1.687.878.633.132 1.29.084 1.897-.136.274.586.705 1.084 1.246 1.439.54.354 1.17.551 1.816.569.647-.016 1.276-.213 1.817-.567s.972-.854 1.245-1.44c.604.239 1.266.296 1.903.164.636-.132 1.22-.447 1.68-.907.46-.46.776-1.044.908-1.681s.075-1.299-.165-1.903c.586-.274 1.084-.705 1.439-1.246.354-.54.551-1.17.569-1.816zM9.662 14.85l-3.429-3.428 1.293-1.302 2.072 2.072 4.4-4.794 1.347 1.246z"
        fill={X_BLUE}
      ></path>
    </g>
  </svg>
);

// Readability scrim over the backdrop shader.
const Scrim: React.FC<{ strength?: number }> = ({ strength = 1 }) => (
  <AbsoluteFill
    style={{
      background: `radial-gradient(120% 120% at 50% 42%, rgba(20,19,24,${
        0.3 * strength
      }) 0%, rgba(20,19,24,${0.78 * strength}) 100%)`,
    }}
  />
);

// ===========================================================================
// Scene 1 — Hook. Two lines land solo: the setup for the meta joke.
// ===========================================================================
const HookScene: React.FC = () => (
  <AbsoluteFill>
    <Sequence durationInFrames={62}>
      <ScaleDownFade
        text="Remocn needed a new logo"
        fontSize={54}
        fontWeight={400}
        color={INK}
      />
    </Sequence>
    <Sequence from={62} durationInFrames={62}>
      <ScaleDownFade
        text="So we wrote one"
        fontSize={54}
        fontWeight={400}
        color={INK}
      />
    </Sequence>
  </AbsoluteFill>
);

// ===========================================================================
// Scene 2 — The writing. The logo's SVG source types itself out — the black
// plate rect, then the R path in lime — closed by a block caret. The camera
// will push INTO this code.
// ===========================================================================
type CodeSegment = { t: string; c?: string };

const CODE_SEGMENTS: CodeSegment[] = [
  { t: '<svg viewBox="0 0 100 100">\n' },
  { t: '  <rect width="100" height="100" rx="14" fill="#000000" />\n' },
  { t: "  <path\n" },
  { t: '    fill="#ffffff"\n' },
  { t: "    d=" },
  { t: '"M 0.35 7.55 L 0.35 0.35 L 86.55 0.35\n', c: LIME },
  { t: '       C 110.55 1.35, 131.25 24.05, 131.25 47.55 …"', c: LIME },
  { t: "\n  />\n" },
  { t: "</svg>" },
];

const CODE_TOTAL = CODE_SEGMENTS.reduce((n, s) => n + s.t.length, 0);
const TYPE_START = 14;
const TYPE_SPEED = 3.4; // chars per frame

const CodeScene: React.FC = () => {
  const frame = useCurrentFrame();

  const typed = Math.max(
    0,
    Math.min(CODE_TOTAL, Math.floor((frame - TYPE_START) * TYPE_SPEED)),
  );
  const typingDone = typed >= CODE_TOTAL;
  const caretOn = typingDone ? Math.floor(frame / 15) % 2 === 0 : true;

  const fileTagOpacity = interpolate(frame, [4, 14], [0, 1], clampOpts);

  const caret = (
    <span
      key="caret"
      style={{
        display: "inline-block",
        width: 12,
        height: 24,
        verticalAlign: "-4px",
        background: caretOn ? MUTED : "transparent",
      }}
    />
  );

  const rendered: React.ReactNode[] = [];
  let offset = 0;
  let caretPlaced = false;
  CODE_SEGMENTS.forEach((seg, i) => {
    const visible = Math.max(0, Math.min(seg.t.length, typed - offset));
    if (visible > 0) {
      rendered.push(
        <span key={i} style={{ color: seg.c ?? MUTED }}>
          {seg.t.slice(0, visible)}
        </span>,
      );
    }
    offset += seg.t.length;
    if (!caretPlaced && typed < offset) {
      rendered.push(caret);
      caretPlaced = true;
    }
  });
  if (!caretPlaced) rendered.push(caret);

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