import React from 'react'; import { useCurrentFrame, useVideoConfig, interpolate, Easing, AbsoluteFill, spring, } from 'remotion'; // ============================================================================= // COMPOSITION CONFIG // ============================================================================= export const compositionConfig = { id: 'TenKCelebration', durationInSeconds: 6, fps: 30, width: 1080, height: 1920, }; // ============================================================================= // PROPS INTERFACE // ============================================================================= interface TenKCelebrationProps { primaryColor?: string; secondaryColor?: string; accentColor?: string; backgroundColor?: string; mainText?: string; subText?: string; } // ============================================================================= // STYLE CONSTANTS // ============================================================================= const EASINGS = { easeOut: Easing.bezier(0.33, 1, 0.68, 1), easeInOut: Easing.bezier(0.37, 0, 0.63, 1), overshoot: Easing.bezier(0.34, 1.56, 0.64, 1), elastic: Easing.bezier(0.68, -0.6, 0.32, 1.6), }; // ============================================================================= // PRE-GENERATED DATA // ============================================================================= const seededRandom = (seed: number): number => { const x = Math.sin(seed * 9999) * 10000; return x - Math.floor(x); }; // Pixel grid definitions for "10K" const DIGIT_1 = [ [0, 1], [1, 0], [1, 1], [2, 1], [3, 1], [4, 1], [5, 1], [5, 0], [5, 2], ]; const DIGIT_0 = [ [0, 0], [0, 1], [0, 2], [0, 3], [1, 0], [1, 3], [2, 0], [2, 3], [3, 0], [3, 3], [4, 0], [4, 3], [5, 0], [5, 1], [5, 2], [5, 3], ]; const LETTER_K = [ [0, 0], [1, 0], [2, 0], [3, 0], [4, 0], [5, 0], [2, 1], [1, 2], [3, 2], [0, 3], [4, 3], [5, 4], ]; // Generate confetti colors dynamically based on props const getConfettiColors = (primary: string, secondary: string, accent: string) => [ primary, secondary, accent, '#FFD700', '#FF6B6B', '#A855F7', '#F472B6' ]; // Generate confetti particles const CONFETTI_COUNT = 80; const generateConfetti = (colors: string[]) => Array.from({ length: CONFETTI_COUNT }, (_, i) => ({ x: seededRandom(i * 7) * 1080, delay: seededRandom(i * 13) * 40, speed: 0.5 + seededRandom(i * 19) * 1.5, rotation: seededRandom(i * 23) * 360, rotationSpeed: (seededRandom(i * 29) - 0.5) * 15, size: 8 + seededRandom(i * 31) * 16, colorIndex: Math.floor(seededRandom(i * 37) * colors.length), shape: Math.floor(seededRandom(i * 41) * 3), wobbleSpeed: 2 + seededRandom(i * 43) * 4, wobbleAmount: 20 + seededRandom(i * 47) * 40, })); const confettiParticles = generateConfetti([]); // Floating particles background const FLOATING_COUNT = 40; const floatingParticles = Array.from({ length: FLOATING_COUNT }, (_, i) => ({ x: seededRandom(i * 101) * 1080, y: seededRandom(i * 103) * 1920, size: 2 + seededRandom(i * 107) * 4, speed: 0.3 + seededRandom(i * 109) * 0.7, opacity: 0.2 + seededRandom(i * 113) * 0.4, })); // ============================================================================= // COMPONENTS // ============================================================================= const PixelBlock: React.FC<{ x: number; y: number; index: number; frame: number; fps: number; blockSize: number; gap: number; offsetX: number; offsetY: number; color: string; glowColor: string; }> = ({ x, y, index, frame, fps, blockSize, gap, offsetX, offsetY, color, glowColor }) => { const delay = index * 1.5; const startFrame = 15 + delay; const scale = spring({ frame: frame - startFrame, fps, config: { damping: 12, stiffness: 150, mass: 0.8 }, }); const finalX = offsetX + x * (blockSize + gap); const finalY = offsetY + y * (blockSize + gap); const initialX = finalX + (seededRandom(index * 53) - 0.5) * 400; const initialY = finalY - 300 - seededRandom(index * 59) * 200; const posX = interpolate(scale, [0, 1], [initialX, finalX]); const posY = interpolate(scale, [0, 1], [initialY, finalY]); const rotation = interpolate(scale, [0, 1], [seededRandom(index * 61) * 360, 0]); // Glow pulse const glowIntensity = interpolate( Math.sin(frame * 0.15 + index * 0.3), [-1, 1], [0.5, 1] ); return (
); }; const PixelNumber: React.FC<{ pixels: number[][]; frame: number; fps: number; offsetX: number; offsetY: number; startIndex: number; color: string; glowColor: string; }> = ({ pixels, frame, fps, offsetX, offsetY, startIndex, color, glowColor }) => { const blockSize = 38; const gap = 6; return ( <> {pixels.map(([row, col], i) => ( ))} ); }; const Confetti: React.FC<{ frame: number; colors: string[] }> = ({ frame, colors }) => { const burstStart = 60; return ( <> {confettiParticles.map((particle, i) => { const adjustedFrame = frame - burstStart - particle.delay; if (adjustedFrame < 0) return null; const y = -50 + adjustedFrame * particle.speed * 12; const wobble = Math.sin(adjustedFrame * 0.1 * particle.wobbleSpeed) * particle.wobbleAmount; const rotation = particle.rotation + adjustedFrame * particle.rotationSpeed; const color = colors[particle.colorIndex % colors.length]; const opacity = interpolate( y, [1600, 1920], [1, 0], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp' } ); if (y > 2000) return null; const shapes = [
,
,
, ]; return shapes[particle.shape]; })} ); }; const FloatingParticles: React.FC<{ frame: number; color: string }> = ({ frame, color }) => { return ( <> {floatingParticles.map((particle, i) => { const y = (particle.y + frame * particle.speed * 2) % 2000 - 40; const pulse = Math.sin(frame * 0.05 + i) * 0.3 + 0.7; return (
); })} ); }; const Heart: React.FC<{ frame: number; fps: number }> = ({ frame, fps }) => { const enterStart = 90; const scale = spring({ frame: frame - enterStart, fps, config: { damping: 10, stiffness: 100, mass: 1 }, }); const pulse = interpolate( Math.sin((frame - enterStart) * 0.2), [-1, 1], [0.95, 1.05] ); const finalScale = scale * pulse; return (
💙
); }; const GlowRing: React.FC<{ frame: number; color: string }> = ({ frame, color }) => { const burstFrame = 70; const progress = interpolate( frame, [burstFrame, burstFrame + 30], [0, 1], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp', easing: EASINGS.easeOut } ); const scale = interpolate(progress, [0, 1], [0, 3]); const opacity = interpolate(progress, [0, 0.3, 1], [0, 0.8, 0]); return (
); }; // ============================================================================= // MAIN COMPONENT // ============================================================================= const TenKCelebration: React.FC = (props) => { const { primaryColor = '#FF3366', secondaryColor = '#00FF88', accentColor = '#00BFFF', backgroundColor = '#0D1117', mainText = 'FOLLOWERS!', subText = 'Thank you for the incredible support!', } = props; const frame = useCurrentFrame(); const { fps, width, height } = useVideoConfig(); const confettiColors = getConfettiColors(primaryColor, secondaryColor, accentColor); // Text animations const followersOpacity = interpolate( frame, [75, 90], [0, 1], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp' } ); const followersY = interpolate( frame, [75, 95], [40, 0], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp', easing: EASINGS.overshoot } ); const thankYouOpacity = interpolate( frame, [100, 115], [0, 1], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp' } ); const thankYouY = interpolate( frame, [100, 120], [30, 0], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp', easing: EASINGS.easeOut } ); // Calculate pixel number positions const blockSize = 38; const gap = 6; const digit1Width = 3 * (blockSize + gap); const digit0Width = 4 * (blockSize + gap); const letterKWidth = 5 * (blockSize + gap); const totalWidth = digit1Width + digit0Width + letterKWidth + 60; const startX = (width - totalWidth) / 2; const startY = height * 0.22; return ( {/* Gradient overlay */}
{/* Floating particles background */} {/* Glow ring burst */} {/* Pixel numbers "10K" */} {/* Main text */}
{mainText}
{/* Sub text */}
{subText}
{/* Heart */} {/* Confetti */} {/* Vignette */}
); }; export default TenKCelebration;