remocn icons — Introducing remocn icons
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-iconsRender it locally
Renders the MP4 on your machine with the Remotion CLI.
$ pnpm dlx remotion render remocn-icons out/remocn-icons.mp4 --scale=2 --crf=15 --x264-preset=slower --jpeg-quality=95 --gl=angleThe prompt
Reconstructed draftA 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 { evolvePath, getLength, getPointAtLength } from "@remotion/paths";
import { loadFont as loadSans } from "@remotion/google-fonts/Manrope";
import { loadFont as loadMono } from "@remotion/google-fonts/GeistMono";
import { ShaderNeuroNoise } from "@/components/remocn/shader-neuro-noise";
import { SoftBlurIn } from "@/components/remocn/soft-blur-in";
import { ScaleDownFade } from "@/components/remocn/scale-down-fade";
import { KineticCenterBuild } from "@/components/remocn/kinetic-center-build";
import { RolodexFlip } from "@/components/remocn/rolodex-flip";
import { pushThrough } from "@/components/remocn/push-through";
import { whipPan } from "@/components/remocn/whip-pan";
import { focusPull } from "@/components/remocn/focus-pull";
import { BellIcon, BellIconStatic } from "@/components/remocn/icon-bell";
import { HeartIcon, HeartIconStatic } from "@/components/remocn/icon-heart";
import { StarIcon, StarIconStatic } from "@/components/remocn/icon-star";
import { ZapIcon } from "@/components/remocn/icon-zap";
import { SearchIcon } from "@/components/remocn/icon-search";
import { RocketIcon } from "@/components/remocn/icon-rocket";
import { SendIcon } from "@/components/remocn/icon-send";
import { PlayIcon } from "@/components/remocn/icon-play";
import { SettingsIcon } from "@/components/remocn/icon-settings";
import { LockIcon } from "@/components/remocn/icon-lock";
import { CloudIcon } from "@/components/remocn/icon-cloud";
import { CameraIcon } from "@/components/remocn/icon-camera";
import { MailIcon } from "@/components/remocn/icon-mail";
import { CheckCircleIcon } from "@/components/remocn/icon-check-circle";
import { SunIcon } from "@/components/remocn/icon-sun";
import { MoonIcon } from "@/components/remocn/icon-moon";
import { PartyPopperIcon } from "@/components/remocn/icon-party-popper";
import { TrendingUpIcon } from "@/components/remocn/icon-trending-up";
import { WalletIcon } from "@/components/remocn/icon-wallet";
import { CrownIcon } from "@/components/remocn/icon-crown";
import { TrophyIcon } from "@/components/remocn/icon-trophy";
import { FlameIcon } from "@/components/remocn/icon-flame";
import { SparklesIcon } from "@/components/remocn/icon-sparkles";
import { TimerIcon } from "@/components/remocn/icon-timer";
const { fontFamily: SANS_FAMILY } = loadSans("normal", {
subsets: ["latin"],
weights: ["400", "500", "600"],
});
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";
const clampOpts = {
extrapolateLeft: "clamp" as const,
extrapolateRight: "clamp" as const,
};
// The neuro-noise field that carries the whole video — a web of fine living
// strokes, the same material the icons are made of. Obsidian family with a
// faint lime undertone, pushed far back by opacity + the scrim.
const NOISE_PROPS = {
speed: 0.4,
colorBack: "#141318",
colorMid: "#26252f",
colorFront: "#7d8672",
brightness: 0.06,
contrast: 0.32,
};
// ---------------------------------------------------------------------------
// Scene timings (frames @ 30fps). Transitions overlap.
// ---------------------------------------------------------------------------
const S_HOOK_A = 72; // "Everything in your video moves" — the lush entrance
const S_HOOK_B = 66; // "Except the icons" — deliberately dead
const S_REVEAL = 196; // the bell draws itself, then the name lands
const S_POS = 96; // "100 Lucide icons, rewritten for video"
const S_GALLERY = 132; // 24 icons stroke-draw in a diagonal wave, then act
const S_ACTS = 464; // "Then it acts" + one stroke morphs through ten icons
const S_INSTALL = 180; // the typed command with a rolodex of icon names
const S_OUTRO = 162; // the new-logo lockup animation, inherited
const T_CROSS_A = 16; // hookA → hookB (fade-through: out, then in)
const T_PUSH_IN = 32; // hookB → reveal
const T_CROSS = 14; // reveal → positioning
const T_WHIP = 18; // positioning → gallery
const T_FOCUS = 24; // acts → install
const T_PUSH_OUT = 32; // install → outro
// 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 A. The lushest text entrance remocn ships, deliberately:
// this frame is the world where everything moves.
// ===========================================================================
const HookMovesScene: React.FC = () => (
<AbsoluteFill>
<SoftBlurIn
text="Everything in your video moves"
fontSize={54}
fontWeight={400}
color={INK}
/>
</AbsoluteFill>
);
// ===========================================================================
// Scene 2 — Hook B. The line itself is dead: no entrance, no easing, no
// life — the joke is the grammar, over three frozen gray outlines. The
// crossfade in is the only motion it gets.
// ===========================================================================
const HookStillScene: React.FC = () => (
<AbsoluteFill style={{ alignItems: "center", justifyContent: "center" }}>
<div
style={{
display: "flex",
flexDirection: "column",
alignItems: "center",
gap: 46,
}}
>
<span
style={{
fontFamily: SANS,
fontWeight: 400,
fontSize: 54,
color: INK,
lineHeight: 1,
}}
>
Except the icons
</span>
<div style={{ display: "flex", gap: 34 }}>
<StarIconStatic size={44} color={FAINT} strokeWidth={1.8} />
<BellIconStatic size={44} color={FAINT} strokeWidth={1.8} />
<HeartIconStatic size={44} color={FAINT} strokeWidth={1.8} />
</div>
</div>
</AbsoluteFill>
);
// ===========================================================================
// Scene 3 — The reveal. One huge bell stroke-draws itself on alone, gets its
// grand beat, rings once — then glides up and shrinks as the name resolves
// tight beneath it. The product demonstrates itself before it is named.
// ===========================================================================
const RevealScene: React.FC = () => {
const frame = useCurrentFrame();
// The bell's grand beat ends around frame 102 (draw + ring at speed 0.55);
// then it glides up into the lockup as the name lands.
const glide = interpolate(frame, [104, 130], [0, 1], {
...clampOpts,
easing: Easing.inOut(Easing.cubic),
});
const bellY = -10 - glide * 62;
const bellScale = 1 - glide * 0.52;
const subOpacity = interpolate(frame, [152, 172], [0, 1], clampOpts);
return (
<AbsoluteFill>
{/* the bell */}
<AbsoluteFill style={{ alignItems: "center", justifyContent: "center" }}>
<div
style={{
transform: `translateY(${bellY}px) scale(${bellScale})`,
}}
>
<Sequence from={18} layout="none">
<BellIcon size={170} color={INK} strokeWidth={1.6} speed={0.55} />
</Sequence>
</div>
</AbsoluteFill>
{/* the name — resolves tight beneath the glided bell */}
<Sequence from={108}>
<AbsoluteFill style={{ transform: "translateY(40px)" }}>
<SoftBlurIn
text="Remocn Icons"
fontSize={64}
fontWeight={400}
color={INK}
/>
</AbsoluteFill>
</Sequence>
{/* the category line */}
<AbsoluteFill style={{ alignItems: "center", justifyContent: "center" }}>
<span
style={{
transform: "translateY(104px)",
fontFamily: SANS,
fontWeight: 400,
fontSize: 21,
color: FAINT,
opacity: subOpacity,
}}
>
A new icon set for Remotion
</span>
</AbsoluteFill>
</AbsoluteFill>
);
};
// ===========================================================================
// Scene 4 — The positioning, assembling word by word. The plain number 100
// rides in as just another word.
// ===========================================================================
const PositioningScene: React.FC = () => (
<AbsoluteFill>
<Sequence from={8}>
<KineticCenterBuild
text="100 Lucide icons, rewritten for video"
fontSize={52}
fontWeight={400}
color={INK}
measureScale={1.08}
/>
</Sequence>
</AbsoluteFill>
);
// ===========================================================================
// Scene 5 — The gallery. 24 of the 100, all running the same real registry
// code: each icon stroke-draws on in a diagonal wave and then fires its own
// action — the wall never sits still.
// ===========================================================================
const GALLERY_ICONS: ReadonlyArray<
React.ComponentType<{
animation?: "draw" | "action" | "both";
size?: number;
color?: string;
strokeWidth?: number;
speed?: number;
}>
> = [
PlayIcon,
BellIcon,
HeartIcon,
StarIcon,
SearchIcon,
RocketIcon,
ZapIcon,
SendIcon,
SettingsIcon,
LockIcon,
CloudIcon,
CameraIcon,
MailIcon,
CheckCircleIcon,
SunIcon,
MoonIcon,
PartyPopperIcon,
TrendingUpIcon,
WalletIcon,
CrownIcon,
TrophyIcon,
FlameIcon,
SparklesIcon,
TimerIcon,
];
const GALLERY_COLS = 6;
const GALLERY_SIZE = 58;
const GalleryScene: React.FC = () => {
const frame = useCurrentFrame();
const lineOpacity = interpolate(frame, [22, 42], [0, 1], clampOpts);
return (
<AbsoluteFill style={{ alignItems: "center", justifyContent: "center" }}>
<div
style={{
display: "flex",
flexDirection: "column",
alignItems: "center",
gap: 54,
transform: "translateY(-8px)",
}}
>
<div
style={{
display: "grid",
gridTemplateColumns: `repeat(${GALLERY_COLS}, ${GALLERY_SIZE}px)`,
columnGap: 60,
rowGap: 42,
}}
>
{GALLERY_ICONS.map((Icon, i) => {
const row = Math.floor(i / GALLERY_COLS);
const col = i % GALLERY_COLS;
const delay = 12 + (row + col) * 5;
return (
<div
key={i}
style={{
width: GALLERY_SIZE,
height: GALLERY_SIZE,
display: "flex",
alignItems: "center",
justifyContent: "center",
}}
>
<Sequence from={delay} layout="none">
<Icon
animation="both"
size={GALLERY_SIZE}
color={INK}
strokeWidth={1.8}
/>
</Sequence>
</div>
);
})}
</div>
<span
style={{
fontFamily: SANS,
fontWeight: 400,
fontSize: 24,
color: MUTED,
opacity: lineOpacity,
}}
>
Every icon draws itself on
</span>
</div>
</AbsoluteFill>
);
};
// ===========================================================================
// Scene 6 — Then it acts. ONE continuous lime stroke carries the whole
// scene: it draws on as the heart, performs its little action, then morphs
// into the next icon, and the next — ten icons, one stroke, never cutting.
// Each station's mono name crossfades beneath. The finale lands the claim
// this shot just proved: the paths land in your repo, so you can write
// motion the registry never shipped.
// ===========================================================================
// Path data from the installed icon components (components/remocn/icon-*.tsx
// and the remocn-icons registry).
const HEART_PATH =
"M2 9.5a5.5 5.5 0 0 1 9.591-3.676.56.56 0 0 0 .818 0A5.49 5.49 0 0 1 22 9.5c0 2.29-1.5 4-3 5.5l-5.492 5.313a2 2 0 0 1-3 .019L5 15c-1.5-1.5-3-3.2-3-5.5";
const STAR_PATH =
"M11.525 2.295a.53.53 0 0 1 .95 0l2.31 4.679a2.123 2.123 0 0 0 1.595 1.16l5.166.756a.53.53 0 0 1 .294.904l-3.736 3.638a2.123 2.123 0 0 0-.611 1.878l.882 5.14a.53.53 0 0 1-.771.56l-4.618-2.428a2.122 2.122 0 0 0-1.973 0L6.396 21.01a.53.53 0 0 1-.77-.56l.881-5.139a2.122 2.122 0 0 0-.611-1.879L2.16 9.795a.53.53 0 0 1 .294-.906l5.165-.755a2.122 2.122 0 0 0 1.597-1.16z";
const ZAP_PATH =
"M4 14a1 1 0 0 1-.78-1.63l9.9-10.2a.5.5 0 0 1 .86.46l-1.92 6.02A1 1 0 0 0 13 10h7a1 1 0 0 1 .78 1.63l-9.9 10.2a.5.5 0 0 1-.86-.46l1.92-6.02A1 1 0 0 0 11 14z";
const FLAME_PATH =
"M12 3q1 4 4 6.5t3 5.5a1 1 0 0 1-14 0 5 5 0 0 1 1-3 1 1 0 0 0 5 0c0-2-1.5-3-1.5-5q0-2 2.5-4";
const MOON_PATH =
"M20.985 12.486a9 9 0 1 1-9.473-9.472c.405-.022.617.46.402.803a6 6 0 0 0 8.268 8.268c.344-.215.825-.004.803.401";
const CLOUD_PATH = "M17.5 19H9a7 7 0 1 1 6.71-9h1.79a4.5 4.5 0 1 1 0 9Z";
const BUBBLE_PATH =
"M2.992 16.342a2 2 0 0 1 .094 1.167l-1.065 3.29a1 1 0 0 0 1.236 1.168l3.413-.998a2 2 0 0 1 1.099.092 10 10 0 1 0-4.777-4.719";
const SHIELD_PATH =
"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z";
const BOOKMARK_PATH =
"M17 3a2 2 0 0 1 2 2v15a1 1 0 0 1-1.496.868l-4.512-2.578a2 2 0 0 0-1.984 0l-4.512 2.578A1 1 0 0 1 5 20V5a2 2 0 0 1 2-2z";
const PLAY_PATH =
"M5 5a2 2 0 0 1 3.008-1.728l11.997 6.998a2 2 0 0 1 .003 3.458l-12 7A2 2 0 0 1 5 19z";
// ---------------------------------------------------------------------------
// Ring morphing. `interpolatePath` maps command-by-command from each path's
// own start point, so shapes whose parameterizations start on opposite sides
// (or wind the other way) twist through figure-eights mid-morph. Instead we
// resample every glyph into a fixed-size ring of points, normalize the
// winding, and rotate each morph target's ring to the phase that minimizes
// travel — then a plain point lerp reads as one shape flowing into the next.Showing the first 400 of 1279 lines. View the full file on GitHub.