import { useCurrentFrame, useVideoConfig, interpolate } from 'remotion'; import { C } from '../lib/colors'; import { rand, clampLerp, easeInOut, softSpring } from '../lib/easings'; import { BEAT } from '../HeroVideo'; // Beat 2 — the wow moment. // // Prompt words detonate into ~60 chunky glowing particles that drift, then // magnetically snap into target slots along a SERVER SCHEMATIC. The // schematic strokes on IN PARALLEL with the convergence so the eye always // has something to anchor to — earlier versions had a dead frame ~3s in // where particles were too small and the box hadn't drawn yet. const PARTICLE_COUNT = 60; const SERVER_W = 720; const SERVER_H = 420; const CX = 960; const CY = 540; function targetSlot(i: number) { const N = PARTICLE_COUNT; if (i < N / 2) { // perimeter walk const t = i / (N / 2); const perim = 2 * (SERVER_W + SERVER_H); const d = t * perim; const left = CX - SERVER_W / 2; const top = CY - SERVER_H / 2; let px = left; let py = top; if (d < SERVER_W) { px = left + d; py = top; } else if (d < SERVER_W + SERVER_H) { px = left + SERVER_W; py = top + (d - SERVER_W); } else if (d < 2 * SERVER_W + SERVER_H) { px = left + SERVER_W - (d - SERVER_W - SERVER_H); py = top + SERVER_H; } else { px = left; py = top + SERVER_H - (d - 2 * SERVER_W - SERVER_H); } return { x: px, y: py }; } // Inside the box: three tool rows const j = i - N / 2; const perRow = Math.ceil(N / 2 / 3); const row = Math.floor(j / perRow); const col = (j % perRow) / Math.max(1, perRow - 1); const rowY = CY - 90 + row * 90; const rowX = CX - SERVER_W / 2 + 50 + col * (SERVER_W - 100); return { x: rowX, y: rowY }; } export function TransformScene() { const frame = useCurrentFrame(); const { fps } = useVideoConfig(); const local = frame - BEAT.transform.in; const sceneIn = clampLerp(frame, BEAT.transform.in, BEAT.transform.in + 6); const sceneOut = 1 - clampLerp(frame, BEAT.transform.out - 8, BEAT.transform.out); const sceneAlpha = Math.min(sceneIn, sceneOut); // Schematic strokes on IN PARALLEL with the convergence — starts at // local 8 instead of 30 so the box is visible before particles arrive. const strokeT = easeInOut(clampLerp(local, 8, 55)); // Ports + rows show up as schematic completes const innerT = clampLerp(local, 35, 65); const portPulse = clampLerp(local, 55, 90); // Scan-line — diagonal pass once the schematic is fully drawn const scanT = clampLerp(local, 55, 90); // Central core glow — visible throughout Beat 2 so the eye has an // anchor even when particles are mid-flight. Pulses softly. const coreAlpha = clampLerp(local, 0, 12) * (1 - clampLerp(local, 95, 110) * 0.4); const corePulse = 1 + 0.25 * Math.sin(local * 0.18); return (