import React from 'react'; import { useCurrentFrame, useVideoConfig, interpolate, Easing, AbsoluteFill } from 'remotion'; // ============================================================================= // COMPOSITION CONFIG (Required for auto-discovery) // ============================================================================= export const compositionConfig = { id: 'SineWaveVisualization', durationInSeconds: 5, fps: 60, width: 1080, height: 1920, }; // ============================================================================= // PRE-GENERATED DATA // ============================================================================= const seededRandom = (seed: number): number => { const x = Math.sin(seed * 9999) * 10000; return x - Math.floor(x); }; // Generate grid lines const horizontalGridLines = Array.from({ length: 20 }, (_, i) => ({ y: (i + 1) * 96, opacity: 0.3 + seededRandom(i) * 0.2, })); const verticalGridLines = Array.from({ length: 12 }, (_, i) => ({ x: (i + 1) * 90, opacity: 0.3 + seededRandom(i + 100) * 0.2, })); // ============================================================================= // MAIN COMPONENT // ============================================================================= const SineWaveVisualization: React.FC = () => { const frame = useCurrentFrame(); const { fps } = useVideoConfig(); // Easing functions const easeOut = Easing.bezier(0.33, 1, 0.68, 1); const easeInOut = Easing.bezier(0.37, 0, 0.63, 1); // Layout constants const circleCenter = { x: 1080 * 0.28, y: 1920 * 0.42 }; const circleRadius = 140; const waveStartX = circleCenter.x + circleRadius + 60; const waveEndX = 1080 - 60; const waveWidth = waveEndX - waveStartX; const waveAmplitude = circleRadius; // Animation phases const headingOpacity = interpolate(frame, [0, 30], [0, 1], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp', easing: easeOut, }); const gridOpacity = interpolate(frame, [0, 30], [0, 0.6], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp', easing: easeOut, }); // Circle draw animation (stroke-dashoffset technique) const circleCircumference = 2 * Math.PI * circleRadius; const circleDrawProgress = interpolate(frame, [30, 50], [0, 1], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp', easing: easeOut, }); const circleStrokeDashoffset = circleCircumference * (1 - circleDrawProgress); // Rotation animation (frames 50-300) const rotationAngle = interpolate(frame, [50, 295], [0, Math.PI * 2], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp', easing: Easing.linear, }); // Point position on circle const pointX = circleCenter.x + circleRadius * Math.cos(rotationAngle - Math.PI / 2); const pointY = circleCenter.y + circleRadius * Math.sin(rotationAngle - Math.PI / 2); // Wave progress (how much of the wave to show) const waveProgress = interpolate(frame, [50, 295], [0, 1], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp', easing: Easing.linear, }); // Generate sine wave path const generateWavePath = (progress: number): string => { if (progress <= 0) return ''; const points: string[] = []; const numPoints = Math.floor(progress * 200); for (let i = 0; i <= numPoints; i++) { const t = i / 200; const x = waveStartX + t * waveWidth; const angle = t * Math.PI * 2 - Math.PI / 2; const y = circleCenter.y + waveAmplitude * Math.sin(angle); if (i === 0) { points.push(`M ${x} ${y}`); } else { points.push(`L ${x} ${y}`); } } return points.join(' '); }; const wavePath = generateWavePath(waveProgress); // Current wave endpoint for tracer line const currentWaveX = waveStartX + waveProgress * waveWidth; // Formula fade in const formulaOpacity = interpolate(frame, [250, 280], [0, 1], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp', easing: easeOut, }); const formulaY = interpolate(frame, [250, 280], [30, 0], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp', easing: easeOut, }); // Watermark fade in const watermarkOpacity = interpolate(frame, [280, 300], [0, 0.5], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp', easing: easeOut, }); // Point visibility (starts after circle is drawn) const pointOpacity = interpolate(frame, [48, 55], [0, 1], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp', }); // Tracer line visibility const tracerOpacity = interpolate(frame, [50, 60], [0, 0.5], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp', }); return ( {/* Background Grid */} {horizontalGridLines.map((line, i) => ( ))} {verticalGridLines.map((line, i) => ( ))} {/* Heading */}

The Beauty of Sine

{/* Main SVG for circle and wave */} {/* Unit Circle */} {/* Center dot */} {/* Rotating radius line */} {frame >= 50 && ( )} {/* Point on circumference */} {/* Horizontal tracer line (dashed) */} {frame >= 50 && waveProgress > 0 && ( )} {/* Y-axis reference line */} {/* Sine Wave */} {wavePath && ( <> {/* Glow layer */} {/* Main wave */} )} {/* Current point on wave */} {frame >= 50 && waveProgress > 0 && ( )} {/* Axis labels */}
1
-1
{/* Formula */}

y = sin(θ)

{/* Angle indicator */} {frame >= 50 && (
θ = {Math.round((rotationAngle * 180) / Math.PI)}°
)} {/* Watermark */}

Built with VidTSX

); }; export default SineWaveVisualization;