Skip to content

Instantly share code, notes, and snippets.

@JonnyBurger
Created January 30, 2026 17:08
Show Gist options
  • Select an option

  • Save JonnyBurger/0e804eefbb8f096a71df6d8b5a7feb2e to your computer and use it in GitHub Desktop.

Select an option

Save JonnyBurger/0e804eefbb8f096a71df6d8b5a7feb2e to your computer and use it in GitHub Desktop.
Claude Code Lower Third prompt
❯ use remotion best practices. this is our youtube channel: https://www.youtube.com/@remotion_dev. use curl scrape youtube to find the avatar and the subscriber count. multiple subscriber
counts appear on the page, find the right one. make a white lower third that slides in from the bottom center. show the name, subscriber count and avatar. display a typical fixed width
black youtube subscribe button that changes from "Subscribe" to "Subscribed". use a ease-out animation and for pressing in the button and a spring animation with a slight bounce once the
button is released. fade out the lower third. render it as a transparent prores video.
⏺ Skill(remotion-best-practices)
⎿ Successfully loaded skill
⏺ Bash(curl -s 'https://www.youtube.com/@remotion_dev' -H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36' -H 'Accept-Language: en-US,e…)
⎿ grep: invalid option -- P
usage: grep [-abcdDEFGHhIiJLlMmnOopqRSsUVvwXxZz] [-A num] [-B num] [-C[num]]
[-e pattern] [-f file] [--binary-files=value] [--color=when]
… +2 lines (ctrl+o to expand)
⏺ Bash(curl -s 'https://www.youtube.com/@remotion_dev' -H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36' -H 'Accept-Language: en-US,e…)
⎿ grep: invalid option -- P
usage: grep [-abcdDEFGHhIiJLlMmnOopqRSsUVvwXxZz] [-A num] [-B num] [-C[num]]
[-e pattern] [-f file] [--binary-files=value] [--color=when]
… +2 lines (ctrl+o to expand)
⏺ Searched for 1 pattern (ctrl+o to expand)
⏺ Let me scrape YouTube properly and read the relevant skill files.
⏺ Bash(curl -s 'https://www.youtube.com/@remotion_dev' -H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36' -H 'Accept-Language: en-US,e…)
⎿ (No content)
⏺ Bash(cd /private/tmp/claude-501/-Users-jonathanburger-lower-third/12024f83-f9c6-44a3-ad9d-583e9ce5f156/scratchpad && python3 -c "
import re, json…)
⎿ Subscriber counts: []
Avatar: {"url":"https://yt3.googleusercontent.com/U1odzKpyZw7s4kcP0O1LwLXqmvoho1jqVxZ7EABsT8IRIEuzTF4Vwu-cdq3387GmzmQq9Fxhi4c=s900-c-k-c0x00ffffff-no-rj","width":900,"height":900}
Channel name: []
⎿ Shell cwd was reset to /Users/jonathanburger/lower-third
⏺ Read 5 files (ctrl+o to expand)
⏺ Now let me get the subscriber count text properly and the channel name.
⏺ Bash(cd /private/tmp/claude-501/-Users-jonathanburger-lower-third/12024f83-f9c6-44a3-ad9d-583e9ce5f156/scratchpad && python3 -c "
import re…)
⎿ Sub text: []
Sub accessible: []
Titles: ['Home', 'Videos', 'Shorts', 'Playlists', 'Posts']
⎿ Shell cwd was reset to /Users/jonathanburger/lower-third
⏺ Read 1 file (ctrl+o to expand)
⏺ Bash(cd /private/tmp/claude-501/-Users-jonathanburger-lower-third/12024f83-f9c6-44a3-ad9d-583e9ce5f156/scratchpad && python3 -c "
import re…)
⎿ r":4279440147},{"weightLabel":"FONT_WEIGHT_BOLD"}]},"subtitle":{"content":"⁨@JonnyBurger⁩ • ⁨4.11K subscribers⁩"},"trailingImage":{"sources":[{"clientResource":{"imageName":"yt-sys-icons:
chevron_righ
---
href="https://m.youtube.com/@remotion_dev"><title>Remotion - YouTube</title><meta name="description" content="Make videos programmatically."><meta nam
⎿ Shell cwd was reset to /Users/jonathanburger/lower-third
⏺ I see the data but it's showing a different channel reference. Let me find the main channel subscriber count.
⏺ Bash(cd /private/tmp/claude-501/-Users-jonathanburger-lower-third/12024f83-f9c6-44a3-ad9d-583e9ce5f156/scratchpad && python3 -c "
import re…)
⎿ '4.11K subscribers'
Remotion nearby: False
… +11 lines (ctrl+o to expand)
⎿ Shell cwd was reset to /Users/jonathanburger/lower-third
⏺ The Remotion channel has 2.26K subscribers. Avatar URL was already found. Now let me build everything.
⏺ Read 2 files (ctrl+o to expand)
⏺ Now let me write the composition and root files.
⏺ Write(src/Composition.tsx)
⎿ Added 159 lines, removed 1 line
1 +import {
2 + AbsoluteFill,
3 + Img,
4 + interpolate,
5 + spring,
6 + useCurrentFrame,
7 + useVideoConfig,
8 + Easing,
9 +} from "remotion";
10 +
11 +const AVATAR_URL =
12 + "https://yt3.googleusercontent.com/U1odzKpyZw7s4kcP0O1LwLXqmvoho1jqVxZ7EABsT8IRIEuzTF4Vwu-cdq3387GmzmQq9Fxhi4c=s900-c-k-c0x00ffffff-no-rj";
13 +const CHANNEL_NAME = "Remotion";
14 +const SUBSCRIBER_COUNT = "2.26K subscribers";
15 +
16 +const SUBSCRIBE_PRESS_FRAME = 45; // 1.5s in
17 +const CARD_PADDING = 20;
18 +
19 export const MyComposition = () => {
2 - return null;
20 + const frame = useCurrentFrame();
21 + const { fps, durationInFrames } = useVideoConfig();
22 +
23 + // Slide in from bottom (ease-out)
24 + const slideIn = interpolate(frame, [0, 0.5 * fps], [200, 0], {
25 + extrapolateRight: "clamp",
26 + extrapolateLeft: "clamp",
27 + easing: Easing.out(Easing.quad),
28 + });
29 +
30 + // Fade out near the end
31 + const fadeOut = interpolate(
32 + frame,
33 + [durationInFrames - 0.5 * fps, durationInFrames],
34 + [1, 0],
35 + {
36 + extrapolateRight: "clamp",
37 + extrapolateLeft: "clamp",
38 + },
39 + );
40 +
41 + // Subscribe button press animation
42 + // Ease-out for pressing in
43 + const pressIn = interpolate(
44 + frame,
45 + [SUBSCRIBE_PRESS_FRAME, SUBSCRIBE_PRESS_FRAME + 4],
46 + [1, 0.92],
47 + {
48 + extrapolateRight: "clamp",
49 + extrapolateLeft: "clamp",
50 + easing: Easing.out(Easing.quad),
51 + },
52 + );
53 +
54 + // Spring bounce back on release
55 + const releaseSpring = spring({
56 + frame: frame - (SUBSCRIBE_PRESS_FRAME + 4),
57 + fps,
58 + config: { damping: 8, stiffness: 200 },
59 + });
60 +
61 + const pressOut = interpolate(releaseSpring, [0, 1], [0.92, 1]);
62 +
63 + const buttonScale =
64 + frame < SUBSCRIBE_PRESS_FRAME
65 + ? 1
66 + : frame < SUBSCRIBE_PRESS_FRAME + 4
67 + ? pressIn
68 + : pressOut;
69 +
70 + const isSubscribed = frame >= SUBSCRIBE_PRESS_FRAME + 4;
71 +
72 + return (
73 + <AbsoluteFill>
74 + <div
75 + style={{
76 + position: "absolute",
77 + bottom: 60,
78 + left: "50%",
79 + transform: `translateX(-50%) translateY(${slideIn}px)`,
80 + opacity: fadeOut,
81 + display: "flex",
82 + alignItems: "center",
83 + gap: 16,
84 + backgroundColor: "white",
85 + borderRadius: 12,
86 + padding: CARD_PADDING,
87 + boxShadow: "0 4px 24px rgba(0,0,0,0.12)",
88 + }}
89 + >
90 + {/* Avatar */}
91 + <Img
92 + src={AVATAR_URL}
93 + style={{
94 + width: 56,
95 + height: 56,
96 + borderRadius: "50%",
97 + }}
98 + />
99 +
100 + {/* Name and subscribers */}
101 + <div
102 + style={{
103 + display: "flex",
104 + flexDirection: "column",
105 + gap: 2,
106 + marginRight: 16,
107 + }}
108 + >
109 + <span
110 + style={{
111 + fontSize: 18,
112 + fontWeight: 600,
113 + fontFamily: "sans-serif",
114 + color: "#0f0f0f",
115 + whiteSpace: "nowrap",
116 + }}
117 + >
118 + {CHANNEL_NAME}
119 + </span>
120 + <span
121 + style={{
122 + fontSize: 13,
123 + fontFamily: "sans-serif",
124 + color: "#606060",
125 + whiteSpace: "nowrap",
126 + }}
127 + >
128 + {SUBSCRIBER_COUNT}
129 + </span>
130 + </div>
131 +
132 + {/* Subscribe button */}
133 + <div
134 + style={{
135 + transform: `scale(${buttonScale})`,
136 + width: 110,
137 + height: 36,
138 + borderRadius: 18,
139 + backgroundColor: isSubscribed ? "#f2f2f2" : "#0f0f0f",
140 + display: "flex",
141 + alignItems: "center",
142 + justifyContent: "center",
143 + cursor: "pointer",
144 + }}
145 + >
146 + <span
147 + style={{
148 + fontSize: 14,
149 + fontWeight: 500,
150 + fontFamily: "sans-serif",
151 + color: isSubscribed ? "#0f0f0f" : "white",
152 + whiteSpace: "nowrap",
153 + }}
154 + >
155 + {isSubscribed ? "Subscribed" : "Subscribe"}
156 + </span>
157 + </div>
158 + </div>
159 + </AbsoluteFill>
160 + );
161 };
⏺ Now update Root.tsx to configure for transparent ProRes and extend the duration.
⏺ Write(src/Root.tsx)
⎿ Added 16 lines, removed 3 lines
1 import "./index.css";
2 import { Composition } from "remotion";
3 import { MyComposition } from "./Composition";
4 +import type { CalculateMetadataFunction } from "remotion";
5
6 +const calculateMetadata: CalculateMetadataFunction<
7 + Record<string, unknown>
8 +> = async () => {
9 + return {
10 + defaultCodec: "prores",
11 + defaultVideoImageFormat: "png",
12 + defaultPixelFormat: "yuva444p10le",
13 + defaultProResProfile: "4444",
14 + };
15 +};
16 +
17 export const RemotionRoot: React.FC = () => {
18 return (
19 <>
20 <Composition
21 id="MyComp"
22 component={MyComposition}
11 - durationInFrames={60}
23 + durationInFrames={120}
24 fps={30}
13 - width={1280}
14 - height={720}
25 + width={1920}
26 + height={1080}
27 + calculateMetadata={calculateMetadata}
28 />
29 </>
30 );
⏺ Now let me render it.
⏺ Bash(npx remotion render --image-format=png --pixel-format=yuva444p10le --codec=prores --prores-profile=4444 MyComp out/lower-third.mov 2>&1)
⎿ Bundling 6%
Bundling 19%
Bundling 58%
… +140 lines (ctrl+o to expand)
⎿ (timeout 5m)
⏺ Done. Here's what was built:
Scraped from YouTube (@remotion_dev):
- Avatar: YouTube profile image (900x900)
- Subscriber count: 2.26K subscribers
- Channel name: Remotion
Animation timeline (4 seconds at 30fps, 120 frames):
1. Frames 0-15: White card slides up from bottom center with Easing.out(Easing.quad)
2. Frame 45-49: Subscribe button scales down to 0.92 with ease-out (press effect)
3. Frame 49+: Spring bounce back (damping: 8, stiffness: 200) — button releases with slight bounce, text changes from "Subscribe" to "Subscribed" (black bg to gray bg)
4. Final 15 frames: Fade out opacity to 0
Output: out/lower-third.mov — 7.5 MB transparent ProRes 4444 (yuva444p10le, png image format) ready for import into video editing software.
✻ Cogitated for 1m 41s
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment