Spaces:
Running
Running
| // @ts-nocheck | |
| // ============================================================================ | |
| // MOTION GRAPHICS · OBSIDIAN ORBIT — Scene 06 / PROJECT CREATED (11s, 330f) | |
| // | |
| // "Keynote object-worship". ONE hero artifact: a FILE-TREE slab. A thick glass | |
| // slab whose face is a software file-tree — a root folder "Motion Graphics" | |
| // that UNFOLDS open, then core files (remotion / director / episodes) CASCADE | |
| // in one by one, each stamped with a green tick. Order-from-nothing: the empty | |
| // slab fills with structure as the WORLD orbits (never the camera) in slow | |
| // hold→ease arcs. Billboarded spec Callouts fire on drawn leader lines at | |
| // waypoints ("PROJECT READY", "CORE FILES"); the angle-tracked sheen rakes the | |
| // turn; a settle/lock-in on the apex where the tree completes — the payoff — | |
| // then it rests, sheen tracking the final turn. | |
| // | |
| // Built ENTIRELY on the Plinth module (mirrors ObsidianOrbit.tsx's contract) + | |
| // the shared Chronixel engine. CSS-3D only, deterministic, no WebGL. | |
| // ============================================================================ | |
| import React from "react"; | |
| import { AbsoluteFill, useCurrentFrame, useVideoConfig } from "remotion"; | |
| import { | |
| Plinth, | |
| OrbitWorld, | |
| Extrude, | |
| Callout, | |
| DustMotes, | |
| } from "../../plinth/plinth"; | |
| import { C, withAlpha, ACCENT_GRAD } from "../../chronixel/theme"; | |
| import { FONT, MONO } from "../../chronixel/fonts"; | |
| import { | |
| beat, | |
| clamp01, | |
| mix, | |
| osc, | |
| enter, | |
| springAt, | |
| EASE, | |
| SPRING, | |
| } from "../../chronixel/motion"; | |
| import { Shockwave, GlowPulse, LightSweep } from "../../chronixel/fx"; | |
| const GREEN = "#54a06f"; // SEMANTIC.success — desaturated tick green | |
| /* ---------------------------------------------------------------------------- | |
| A small mono eyebrow tag pinned top-left over the void — frames the beat. | |
| -------------------------------------------------------------------------- */ | |
| const Eyebrow: React.FC<{ at: number; children: React.ReactNode }> = ({ | |
| at, | |
| children, | |
| }) => { | |
| const frame = useCurrentFrame(); | |
| const { fps } = useVideoConfig(); | |
| return ( | |
| <div | |
| style={{ | |
| position: "absolute", | |
| left: 120, | |
| top: 120, | |
| display: "flex", | |
| alignItems: "center", | |
| gap: 14, | |
| fontFamily: MONO, | |
| fontSize: 15, | |
| letterSpacing: 5, | |
| textTransform: "uppercase", | |
| color: C.accent2, | |
| ...enter(frame, fps, at, { from: "left", dist: 32, blur: 0 }), | |
| }} | |
| > | |
| <span | |
| style={{ | |
| width: 30, | |
| height: 2, | |
| background: ACCENT_GRAD, | |
| boxShadow: `0 0 12px ${C.accentGlow}`, | |
| }} | |
| /> | |
| {children} | |
| </div> | |
| ); | |
| }; | |
| /* ---------------------------------------------------------------------------- | |
| A tiny folder glyph that swings OPEN as `open` goes 0→1 (the lid hinges up). | |
| -------------------------------------------------------------------------- */ | |
| const FolderGlyph: React.FC<{ open: number; color: string }> = ({ | |
| open, | |
| color, | |
| }) => ( | |
| <div style={{ position: "relative", width: 30, height: 24, flexShrink: 0 }}> | |
| {/* back wall of the folder */} | |
| <div | |
| style={{ | |
| position: "absolute", | |
| inset: 0, | |
| top: 5, | |
| borderRadius: 4, | |
| background: withAlpha(color, 0.22), | |
| border: `1.5px solid ${withAlpha(color, 0.7)}`, | |
| }} | |
| /> | |
| {/* tab */} | |
| <div | |
| style={{ | |
| position: "absolute", | |
| left: 0, | |
| top: 1, | |
| width: 13, | |
| height: 7, | |
| borderRadius: "3px 3px 0 0", | |
| background: withAlpha(color, 0.7), | |
| }} | |
| /> | |
| {/* hinging front flap — swings open on `open` */} | |
| <div | |
| style={{ | |
| position: "absolute", | |
| left: 0, | |
| right: 0, | |
| bottom: 0, | |
| height: 17, | |
| borderRadius: 4, | |
| background: `linear-gradient(180deg, ${withAlpha(color, 0.55)}, ${withAlpha( | |
| color, | |
| 0.28, | |
| )})`, | |
| border: `1.5px solid ${withAlpha(color, 0.85)}`, | |
| transformOrigin: "50% 100%", | |
| transform: `perspective(140px) rotateX(${(open * 62).toFixed(1)}deg)`, | |
| boxShadow: `0 0 12px ${withAlpha(color, 0.4 * open)}`, | |
| }} | |
| /> | |
| </div> | |
| ); | |
| /* ---------------------------------------------------------------------------- | |
| A small green tick that draws in with a scale/pop. | |
| -------------------------------------------------------------------------- */ | |
| const Tick: React.FC<{ p: number }> = ({ p }) => ( | |
| <div | |
| style={{ | |
| position: "relative", | |
| width: 22, | |
| height: 22, | |
| borderRadius: "50%", | |
| flexShrink: 0, | |
| background: withAlpha(GREEN, 0.18), | |
| border: `1.5px solid ${withAlpha(GREEN, 0.6 + 0.4 * p)}`, | |
| transform: `scale(${mix(0.3, 1, p)})`, | |
| opacity: p, | |
| boxShadow: `0 0 12px ${withAlpha(GREEN, 0.5 * p)}`, | |
| }} | |
| > | |
| <svg | |
| width={22} | |
| height={22} | |
| viewBox="0 0 22 22" | |
| style={{ position: "absolute", inset: 0 }} | |
| > | |
| <path | |
| d="M6 11.5 L9.5 15 L16 7.5" | |
| fill="none" | |
| stroke={GREEN} | |
| strokeWidth={2.4} | |
| strokeLinecap="round" | |
| strokeLinejoin="round" | |
| strokeDasharray={18} | |
| strokeDashoffset={18 * (1 - p)} | |
| style={{ filter: `drop-shadow(0 0 4px ${withAlpha(GREEN, 0.8)})` }} | |
| /> | |
| </svg> | |
| </div> | |
| ); | |
| /* ============================================================================ | |
| PROJECT CREATED. Hero = the file-tree artifact. An extruded glass slab whose | |
| face is a software file-tree: root folder "Motion Graphics" unfolds, then the | |
| core files cascade in (remotion / director / episodes), each green-ticked. | |
| The world orbits in, callouts fire ("PROJECT READY", "CORE FILES"), and the | |
| tree LOCKS complete on the apex with an impact — then settles. | |
| ========================================================================== */ | |
| export const MgOrbitScene06ProjectCreated: React.FC = () => { | |
| const frame = useCurrentFrame(); | |
| const { fps } = useVideoConfig(); | |
| const t = frame / fps; | |
| const LOCK = 8.2; // the apex lock-in beat (seconds) — the payoff | |
| // --- face choreography ------------------------------------------------- | |
| const rootIn = beat(frame, fps, 1.0, 1.9, EASE.backOut); // root row drops in | |
| const rootOpen = beat(frame, fps, 1.8, 2.7, EASE.out); // root folder unfolds | |
| const connP = beat(frame, fps, 2.4, 3.2, EASE.out); // the tree spine draws | |
| // core files cascade in, staggered, each with a green tick after it lands. | |
| const files = [ | |
| { name: "remotion", ext: "/", at: 2.9 }, | |
| { name: "director", ext: "/", at: 3.6 }, | |
| { name: "episodes", ext: "/", at: 4.3 }, | |
| ]; | |
| // --- the lock-in: a weighty spring settle on the apex ------------------- | |
| const stamp = springAt(frame, fps, LOCK, SPRING.HEAVY); // 0→1 weighty settle | |
| const stampScale = mix(1.12, 1, clamp01(stamp)); // slams down to rest | |
| const flash = clamp01(1 - (t - LOCK) / 0.45) * (t >= LOCK ? 1 : 0); | |
| const riser = beat(frame, fps, LOCK - 1.4, LOCK, EASE.out); // build into apex | |
| const lockRing = clamp01(stamp); | |
| const ringPulse = 0.5 + 0.5 * osc(t, 3); | |
| // hold→ease orbit so the slab reads its face on the way in, then a final arc | |
| // that settles to centre (0°) right at the lock — the snap-to-camera payoff. | |
| const legs = [ | |
| { arc: -22, hold: 0.5, travel: 2.6 }, | |
| { arc: 30, hold: 1.2, travel: 2.8 }, | |
| { arc: -8, hold: 0.6, travel: 1.7 }, // lands ~0° near LOCK | |
| ]; | |
| const HeroFace = ( | |
| <div | |
| style={{ | |
| position: "absolute", | |
| inset: 0, | |
| padding: "40px 46px", | |
| display: "flex", | |
| flexDirection: "column", | |
| justifyContent: "center", | |
| gap: 0, | |
| transform: `scale(${stampScale})`, | |
| }} | |
| > | |
| {/* window chrome bar — reads as a file explorer panel */} | |
| <div | |
| style={{ | |
| display: "flex", | |
| alignItems: "center", | |
| gap: 8, | |
| marginBottom: 22, | |
| opacity: rootIn, | |
| }} | |
| > | |
| <span | |
| style={{ | |
| width: 10, | |
| height: 10, | |
| borderRadius: "50%", | |
| background: withAlpha(C.accent, 0.7), | |
| }} | |
| /> | |
| <span | |
| style={{ | |
| width: 10, | |
| height: 10, | |
| borderRadius: "50%", | |
| background: withAlpha("#ffffff", 0.22), | |
| }} | |
| /> | |
| <span | |
| style={{ | |
| width: 10, | |
| height: 10, | |
| borderRadius: "50%", | |
| background: withAlpha("#ffffff", 0.22), | |
| }} | |
| /> | |
| <div | |
| style={{ | |
| marginLeft: 12, | |
| fontFamily: MONO, | |
| fontSize: 12, | |
| letterSpacing: 2.5, | |
| textTransform: "uppercase", | |
| color: C.textDim, | |
| }} | |
| > | |
| explorer | |
| </div> | |
| </div> | |
| {/* ROOT ROW — the project folder that unfolds open */} | |
| <div | |
| style={{ | |
| display: "flex", | |
| alignItems: "center", | |
| gap: 14, | |
| ...enter(frame, fps, 1.0, { from: "up", dist: 22, blur: 0 }), | |
| opacity: rootIn, | |
| }} | |
| > | |
| <FolderGlyph open={rootOpen} color={C.accent2} /> | |
| <div | |
| style={{ | |
| fontFamily: FONT, | |
| fontWeight: 800, | |
| fontSize: 38, | |
| letterSpacing: "-0.02em", | |
| color: C.text, | |
| textShadow: `0 0 24px ${withAlpha(C.accent, 0.35)}`, | |
| }} | |
| > | |
| Motion Graphics | |
| </div> | |
| <div | |
| style={{ | |
| marginLeft: 6, | |
| fontFamily: MONO, | |
| fontSize: 13, | |
| letterSpacing: 2, | |
| color: C.accent2, | |
| opacity: rootOpen, | |
| }} | |
| > | |
| / | |
| </div> | |
| </div> | |
| {/* the tree spine + the cascading core files */} | |
| <div style={{ position: "relative", paddingLeft: 16, marginTop: 18 }}> | |
| {/* vertical spine of the tree, drawing down from the root */} | |
| <div | |
| style={{ | |
| position: "absolute", | |
| left: 16, | |
| top: -6, | |
| width: 2, | |
| height: `${connP * 88}%`, | |
| background: `linear-gradient(180deg, ${withAlpha( | |
| C.accent2, | |
| 0.8, | |
| )}, ${withAlpha(C.accent, 0.25)})`, | |
| boxShadow: `0 0 8px ${withAlpha(C.accent, 0.4)}`, | |
| }} | |
| /> | |
| <div | |
| style={{ | |
| display: "flex", | |
| flexDirection: "column", | |
| gap: 14, | |
| paddingLeft: 30, | |
| }} | |
| > | |
| {files.map((f) => { | |
| const rowP = beat(frame, fps, f.at, f.at + 0.6, EASE.backOut); | |
| const tickP = beat(frame, fps, f.at + 0.5, f.at + 1.0, EASE.out); | |
| const folderOpen = beat(frame, fps, f.at + 0.3, f.at + 0.9, EASE.out); | |
| return ( | |
| <div | |
| key={f.name} | |
| style={{ | |
| position: "relative", | |
| display: "flex", | |
| alignItems: "center", | |
| gap: 14, | |
| opacity: rowP, | |
| transform: `translateX(${((1 - rowP) * 26).toFixed(1)}px)`, | |
| }} | |
| > | |
| {/* elbow connector from the spine to this row */} | |
| <div | |
| style={{ | |
| position: "absolute", | |
| left: -30, | |
| top: "50%", | |
| width: 24, | |
| height: 2, | |
| transform: "translateY(-50%)", | |
| background: withAlpha(C.accent2, 0.7 * rowP), | |
| boxShadow: `0 0 6px ${withAlpha(C.accent, 0.4 * rowP)}`, | |
| }} | |
| /> | |
| <FolderGlyph open={folderOpen} color={C.accent} /> | |
| <div | |
| style={{ | |
| fontFamily: MONO, | |
| fontSize: 26, | |
| fontWeight: 600, | |
| letterSpacing: "0.01em", | |
| color: C.text, | |
| }} | |
| > | |
| {f.name} | |
| <span style={{ color: C.accent2 }}>{f.ext}</span> | |
| </div> | |
| <div style={{ flex: 1 }} /> | |
| <Tick p={tickP} /> | |
| </div> | |
| ); | |
| })} | |
| </div> | |
| </div> | |
| {/* the lock ring that snaps tight on the apex beat */} | |
| <div | |
| style={{ | |
| position: "absolute", | |
| inset: 18, | |
| borderRadius: 26, | |
| border: `2px solid ${withAlpha(C.accent, 0.3 + 0.45 * ringPulse)}`, | |
| opacity: lockRing, | |
| boxShadow: `0 0 30px ${withAlpha(C.accent, 0.3 * ringPulse)}`, | |
| pointerEvents: "none", | |
| }} | |
| /> | |
| </div> | |
| ); | |
| return ( | |
| <Plinth> | |
| <Eyebrow at={0.3}>Project Created</Eyebrow> | |
| {/* riser glow swelling behind the hero into the apex */} | |
| <AbsoluteFill style={{ pointerEvents: "none", zIndex: 1 }}> | |
| <GlowPulse | |
| size={1000} | |
| color={withAlpha(C.accent, 0.1 * riser)} | |
| strength={riser} | |
| /> | |
| </AbsoluteFill> | |
| {/* impact flash + shockwave on the lock-in payoff */} | |
| <AbsoluteFill style={{ pointerEvents: "none", zIndex: 6 }}> | |
| <GlowPulse | |
| size={1200} | |
| color={withAlpha(C.accent, 0.2 * flash)} | |
| strength={flash} | |
| /> | |
| <Shockwave at={LOCK} size={820} /> | |
| <Shockwave at={LOCK + 0.08} size={1120} /> | |
| <LightSweep at={LOCK} dur={1.0} strength={0.16} /> | |
| </AbsoluteFill> | |
| <OrbitWorld legs={legs} perspective={2300} bobAmp={12}> | |
| <DustMotes layers={3} opacity={0.42} /> | |
| <Extrude w={760} h={460} depth={22} layers={16} radius={28}> | |
| {HeroFace} | |
| </Extrude> | |
| {/* billboarded spec callouts firing at orbit waypoints */} | |
| <Callout | |
| title="Status" | |
| value="PROJECT READY" | |
| at={fps * 2.6} | |
| x={0} | |
| y={-320} | |
| leader={120} | |
| side="right" | |
| /> | |
| <Callout | |
| title="Scaffolded" | |
| value="CORE FILES" | |
| at={fps * 4.6} | |
| x={470} | |
| y={150} | |
| leader={150} | |
| side="right" | |
| /> | |
| <Callout | |
| title="Build Time" | |
| value="11s" | |
| at={fps * (LOCK + 0.3)} | |
| x={-470} | |
| y={140} | |
| leader={160} | |
| side="left" | |
| /> | |
| </OrbitWorld> | |
| {/* flat masked mirror sibling — re-renders the hero, simplified & matte */} | |
| <Plinth.Reflection> | |
| <Extrude w={760} h={460} depth={22} layers={10} radius={28} sheen={false}> | |
| {HeroFace} | |
| </Extrude> | |
| </Plinth.Reflection> | |
| </Plinth> | |
| ); | |
| }; | |
| export default MgOrbitScene06ProjectCreated; |