import React from 'react'; import { useCurrentFrame, useVideoConfig, interpolate, Easing, AbsoluteFill, Img, } from 'remotion'; // ============================================================================= // COMPOSITION CONFIG // ============================================================================= export const compositionConfig = { id: 'TheProcess', durationInSeconds: 10, fps: 30, width: 2160, height: 2160, }; // ============================================================================= // STYLE CONSTANTS // ============================================================================= const COLORS = { primary: '#7C3AED', secondary: '#A78BFA', accent: '#C084FC', background: '#0A0A12', text: '#FFFFFF', } as const; const EASINGS = { easeOut: Easing.bezier(0.33, 1, 0.68, 1), easeInOut: Easing.bezier(0.37, 0, 0.63, 1), }; const CAT_IMAGE_URL = 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQWRoqiCsMF9IV50udIf1AVIV2cvHf7DK2l4Q&s'; // ============================================================================= // NOISE CONFIG — matching Slide 3 // ============================================================================= const GRID_SIZE = 120; const PIXEL_SIZE = 2160 / GRID_SIZE; const NUM_NOISE_FRAMES = 6; // ============================================================================= // TIMING // ============================================================================= const DENOISE_START = 15; // frame 0.5s — start denoising const DENOISE_END = 195; // frame 6.5s — fully clean const HOLD_START = 195; const HOLD_END = 240; // hold clean for ~1.5s const RESET_START = 240; const RESET_END = 270; // fade back to noise for loop const TOTAL_STEPS = 50; // ============================================================================= // PRE-GENERATED NOISE DATA // ============================================================================= const seededRandom = (seed: number): number => { const x = Math.sin(seed * 9999) * 10000; return x - Math.floor(x); }; const getNoiseColor = (seed: number): string => { const r = seededRandom(seed); const base = Math.floor(r * 140 + 30); const colorChance = seededRandom(seed + 997); if (colorChance < 0.06) { const b = Math.min(base + 40, 200); return `rgb(${base - 15},${base - 5},${b})`; } else if (colorChance < 0.10) { const rb = Math.min(base + 25, 190); return `rgb(${rb},${base - 10},${Math.min(base + 35, 200)})`; } else if (colorChance < 0.13) { return `rgb(${base - 15},${Math.min(base + 25, 180)},${Math.min(base + 20, 185)})`; } const g = base + Math.floor(seededRandom(seed + 3) * 20 - 10); const b = base + Math.floor(seededRandom(seed + 7) * 20 - 10); return `rgb(${base},${Math.max(20, g)},${Math.max(20, b)})`; }; // Pre-generate noise frames const NOISE_FRAMES: string[][][] = Array.from({ length: NUM_NOISE_FRAMES }, (_, nf) => Array.from({ length: GRID_SIZE }, (_, row) => Array.from({ length: GRID_SIZE }, (_, col) => { const seed = nf * 1000000 + row * GRID_SIZE + col; return getNoiseColor(seed); }) ) ); const FLICKER_MAP: boolean[][] = Array.from({ length: GRID_SIZE }, (_, row) => Array.from({ length: GRID_SIZE }, (_, col) => { return seededRandom(row * GRID_SIZE + col + 777) < 0.08; }) ); // ============================================================================= // NOISE ROW COMPONENT // ============================================================================= const NoiseRow: React.FC<{ rowIndex: number; baseNoiseFrame: number; flickerNoiseFrame: number; }> = React.memo(({ rowIndex, baseNoiseFrame, flickerNoiseFrame }) => { const baseColors = NOISE_FRAMES[baseNoiseFrame][rowIndex]; const flickerColors = NOISE_FRAMES[flickerNoiseFrame][rowIndex]; const flickers = FLICKER_MAP[rowIndex]; return (
{baseColors.map((color, col) => (
))}
); }); // ============================================================================= // MAIN COMPONENT // ============================================================================= const TheProcess: React.FC = () => { const frame = useCurrentFrame(); const { fps } = useVideoConfig(); // === DENOISE PROGRESS === // Forward pass: 0 → 1 const denoiseForward = interpolate(frame, [DENOISE_START, DENOISE_END], [0, 1], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp', }); // Reverse for loop reset const denoiseReverse = interpolate(frame, [RESET_START, RESET_END], [0, 1], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp', easing: EASINGS.easeInOut, }); // Combined progress: goes 0→1 during denoise, then 1→0 during reset const progress = frame < RESET_START ? denoiseForward : 1 - denoiseReverse; // === STEP COUNTER === const currentStep = Math.min( Math.floor(progress * TOTAL_STEPS) + 1, TOTAL_STEPS ); const displayStep = progress <= 0 ? 0 : currentStep; // === IMAGE REVEAL === // Image blur: starts very blurry, ends sharp // Use an exponential curve so early steps barely change, later steps sharpen fast const blurAmount = interpolate(progress, [0, 0.3, 0.6, 0.85, 1], [80, 55, 30, 10, 0], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp', }); // Image opacity: fades in gradually const imageOpacity = interpolate(progress, [0, 0.15, 0.5, 1], [0, 0.3, 0.7, 1], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp', }); // Image saturation: starts desaturated, fully colored at end const imageSaturation = interpolate(progress, [0, 0.5, 1], [0, 0.5, 1], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp', }); // Pixelation: scale down then up for blocky look (decreases over time) // At progress 0: scale to 1/16 then back up → very pixelated // At progress 1: no pixelation const pixelScale = interpolate(progress, [0, 0.4, 0.75, 1], [0.04, 0.1, 0.35, 1], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp', }); // === NOISE OVERLAY === // Noise fades out as image comes in const noiseOpacity = interpolate(progress, [0, 0.4, 0.8, 1], [1, 0.7, 0.25, 0], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp', }); // Noise flicker const baseNoiseFrame = Math.floor(frame / 20) % NUM_NOISE_FRAMES; const flickerNoiseFrame = Math.floor(frame / 3) % NUM_NOISE_FRAMES; const flickerBrightness = interpolate( seededRandom(frame * 7), [0, 1], [0.95, 1.05] ); // === STEP COUNTER UI === const counterOpacity = interpolate(frame, [8, 18], [0, 1], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp', }); // Counter pulse on step change const stepFraction = progress * TOTAL_STEPS; const stepProgress = stepFraction - Math.floor(stepFraction); const counterScale = progress > 0 && progress < 1 ? interpolate(stepProgress, [0, 0.1, 1], [1.08, 1, 1], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp', }) : 1; // === GLOW on completion === const completionGlow = interpolate(frame, [DENOISE_END, DENOISE_END + 15], [0, 0.25], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp', }); const completionGlowFade = interpolate(frame, [RESET_START, RESET_END], [1, 0], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp', }); return ( {/* === LAYER 1: Cat image (pixelated + blurred) === */}
{/* Pixelation container: render image small, scale up with pixelated rendering */}
{/* === LAYER 2: Noise overlay (fades out) === */} {noiseOpacity > 0.01 && (
{Array.from({ length: GRID_SIZE }, (_, rowIndex) => ( ))}
)} {/* Scanlines */} {noiseOpacity > 0.1 && (
)} {/* === LAYER 3: Completion glow === */}
{/* Vignette */}
{/* === STEP COUNTER === */}
{/* Step label */} Step {/* Step number */} {displayStep} {/* Divider */} / {/* Total */} {TOTAL_STEPS} {/* Progress bar */}
); }; export default TheProcess;