Skip to main content

YouTube Subscribe Nudge

youtube-subscribe-nudge.tsx
import {loadFont} from '@remotion/google-fonts/Inter'; import {Audio} from '@remotion/media'; import {ding, mouseClick} from '@remotion/sfx'; import React from 'react'; import { CanvasImage, Easing, Interactive, interpolate, useCurrentFrame, } from 'remotion'; loadFont('normal', { subsets: ['latin'], weights: ['500', '600', '700', '800'], }); export const YouTubeSubscribeNudge: React.FC = () => { const frame = useCurrentFrame(); return ( <div style={{ height: 240, position: 'relative', width: 760, }} > <Audio durationInFrames={12} from={61} name="Subscribe click" src={mouseClick} trimBefore={3} volume={0.5} /> <Audio durationInFrames={39} from={81} name="Bell chime" src={ding} trimBefore={4} volume={0.24} /> <div style={{ height: 240, opacity: interpolate(frame, [0, 16, 104, 119], [0, 1, 1, 0], { easing: [ Easing.bezier(0.65, 0, 0.35, 1), Easing.linear, Easing.bezier(0.7, 0, 0.84, 0), ], extrapolateLeft: 'clamp', extrapolateRight: 'clamp', }), position: 'relative', scale: interpolate(frame, [0, 24, 104, 119], [0.98, 1, 1, 0.97], { easing: [ Easing.bezier(0.65, 0, 0.35, 1), Easing.linear, Easing.bezier(0.7, 0, 0.84, 0), ], extrapolateLeft: 'clamp', extrapolateRight: 'clamp', output: 'perceptual-scale', }), transformOrigin: '50% 50%', translate: interpolate( frame, [0, 24, 104, 119], ['0px 14px', '0px 0px', '0px 0px', '0px 20px'], { easing: [ Easing.bezier(0.65, 0, 0.35, 1), Easing.linear, Easing.bezier(0.7, 0, 0.84, 0), ], extrapolateLeft: 'clamp', extrapolateRight: 'clamp', }, ), width: 760, }} > <Interactive.Div name="Card" style={{ alignItems: 'center', backgroundColor: '#15161a', border: '1px solid rgba(255, 255, 255, 0.09)', borderRadius: 26, boxShadow: '0 18px 40px rgba(0, 0, 0, 0.26)', boxSizing: 'border-box', display: 'flex', height: 144, left: 0, padding: '0 24px', position: 'absolute', top: 48, width: 760, }} > <CanvasImage fit="cover" height={88} name="Avatar" src="https://remotion.media/elements/social-endcard-remotion-logo.png" style={{ backgroundColor: '#ffffff', border: '1px solid rgba(255, 255, 255, 0.12)', borderRadius: 999, boxSizing: 'border-box', display: 'block', flex: '0 0 auto', height: 88, marginRight: 18, width: 88, }} width={88} /> <div style={{ flex: '1 1 auto', marginRight: 32, minWidth: 0, }} > <Interactive.Div name="Channel name" style={{ color: '#ffffff', fontFamily: 'Inter', fontSize: 34, fontWeight: 800, lineHeight: 1.1, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', }} > Remotion </Interactive.Div> <Interactive.Div name="Channel handle" style={{ color: '#a9abb3', fontFamily: 'Inter', fontSize: 21, fontWeight: 600, lineHeight: 1.15, marginTop: 6, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', }} > @remotion </Interactive.Div> </div> <div style={{ flex: '0 0 auto', height: 62, marginRight: 16, position: 'relative', scale: interpolate(frame, [58, 61, 68], [1, 0.94, 1], { easing: [ Easing.bezier(0.4, 0, 1, 1), Easing.spring({ damping: 9, mass: 0.45, overshootClamping: false, stiffness: 180, }), ], extrapolateLeft: 'clamp', extrapolateRight: 'clamp', output: 'perceptual-scale', }), width: 200, }} > <Interactive.Div name="Subscribe button" style={{ alignItems: 'center', backgroundColor: '#ff1744', borderRadius: 14, color: '#ffffff', display: 'flex', fontFamily: 'Inter', fontSize: 26, fontWeight: 700, height: 62, justifyContent: 'center', left: 0, letterSpacing: -0.25, opacity: interpolate(frame, [63, 64], [1, 0], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp', }), pointerEvents: frame < 64 ? 'auto' : 'none', position: 'absolute', top: 0, width: 200, }} > Subscribe </Interactive.Div> <Interactive.Div name="Subscribed button" style={{ alignItems: 'center', backgroundColor: '#2a2b31', border: '1px solid rgba(255, 255, 255, 0.1)', borderRadius: 14, boxSizing: 'border-box', color: '#ffffff', display: 'flex', fontFamily: 'Inter', fontSize: 26, fontWeight: 700, gap: 10, height: 62, justifyContent: 'center', left: 0, letterSpacing: -0.25, opacity: interpolate(frame, [63, 64], [0, 1], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp', }), pointerEvents: frame < 64 ? 'none' : 'auto', position: 'absolute', top: 0, width: 200, }} > <Interactive.Svg aria-hidden="true" height="26" name="Subscribed checkmark" style={{ color: '#ffffff', flex: '0 0 auto', translate: '0px 1px', }} viewBox="0 0 24 24" width="26" > <path d="m4 12.5 5 4.8L20 7" fill="none" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" strokeWidth="3" /> </Interactive.Svg> Subscribed </Interactive.Div> </div> <Interactive.Div name="Notification bell" style={{ alignItems: 'center', backgroundColor: frame >= 81 ? '#303137' : '#24252b', border: '1px solid rgba(255, 255, 255, 0.12)', borderRadius: 999, boxSizing: 'border-box', display: 'flex', flex: '0 0 auto', height: 64, justifyContent: 'center', position: 'relative', scale: interpolate(frame, [78, 81, 93], [1, 0.9, 1], { easing: [ Easing.bezier(0.4, 0, 1, 1), Easing.spring({ damping: 7, mass: 0.55, overshootClamping: false, stiffness: 160, }), ], extrapolateLeft: 'clamp', extrapolateRight: 'clamp', output: 'perceptual-scale', }), transformOrigin: '50% 50%', width: 64, }} > <Interactive.Svg aria-hidden="true" height="36" name="Bell icon" style={{ color: '#f4f4f5', overflow: 'visible', rotate: interpolate( frame, [81, 85, 89, 93, 97, 101], ['0deg', '-18deg', '16deg', '-11deg', '7deg', '0deg'], { easing: Easing.inOut(Easing.cubic), extrapolateLeft: 'clamp', extrapolateRight: 'clamp', }, ), transformOrigin: '50% 20%', }} viewBox="0 0 24 24" width="36" > {frame < 84 ? ( <> <path d="M5.5 17h13c-1.7-1.9-2.2-3.9-2.2-7 0-2.8-1.7-5-4.3-5s-4.3 2.2-4.3 5c0 3.1-.5 5.1-2.2 7Z" fill="none" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" strokeWidth="1.8" /> <path d="M9.8 20c.5 1 1.2 1.5 2.2 1.5s1.7-.5 2.2-1.5" fill="none" stroke="currentColor" strokeLinecap="round" strokeWidth="1.8" /> </> ) : ( <path d="M19.395 1.196a1 1 0 0 0-.199 1.4A9 9 0 0 1 21 8a1 1 0 0 0 2 0 11 11 0 0 0-2.205-6.605 1 1 0 0 0-1.4-.199Zm-16.192.2A11 11 0 0 0 1 8a1 1 0 0 0 2 0 9 9 0 0 1 1.803-5.404 1 1 0 0 0-1.6-1.2ZM12 1a7 7 0 0 0-7 7v4.446a1 1 0 0 1-.144.515L3.05 15.972C2.25 17.305 3.21 19 4.766 19H8a4 4 0 1 0 8 0h3.233c1.555 0 2.515-1.695 1.715-3.029l-1.805-3.01a1 1 0 0 1-.143-.515V8a7 7 0 0 0-7-7Zm0 2a5 5 0 0 1 5 5v4.445a3 3 0 0 0 .428 1.545L19.233 17H4.766l1.806-3.01c.28-.466.428-1 .428-1.544V8a5 5 0 0 1 5-5Zm-2 16h4a2 2 0 0 1-4 0Z" fill="currentColor" /> )} </Interactive.Svg> </Interactive.Div> </Interactive.Div> <Interactive.Svg aria-hidden="true" height="54" name="Cursor" style={{ filter: 'drop-shadow(0 4px 5px rgba(0, 0, 0, 0.26))', left: interpolate(frame, [40, 60, 66, 78], [690, 620, 620, 699], { easing: [ Easing.bezier(0.16, 1, 0.3, 1), Easing.linear, Easing.bezier(0.65, 0, 0.35, 1), ], extrapolateLeft: 'clamp', extrapolateRight: 'clamp', }), opacity: interpolate(frame, [38, 43, 84, 91], [0, 1, 1, 0], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp', }), position: 'absolute', top: interpolate(frame, [40, 60, 66, 78], [202, 124, 124, 117], { easing: [ Easing.bezier(0.16, 1, 0.3, 1), Easing.linear, Easing.bezier(0.65, 0, 0.35, 1), ], extrapolateLeft: 'clamp', extrapolateRight: 'clamp', }), }} viewBox="0 0 48 54" width="48" > <path d="M5 3 39 35l-16 2 9 13-8 5-9-14L5 50Z" fill="#ffffff" stroke="#111217" strokeLinejoin="round" strokeWidth="3" /> </Interactive.Svg> </div> </div> ); };