Moeeldouma's picture
Deploy Eldouma Graphics — Docker Space, bring-your-own-key demo
39d98a0 verified
Raw
History Blame Contribute Delete
21.8 kB
// @ts-nocheck
// ============================================================================
// MOTION GRAPHICS · OBSIDIAN ORBIT — Scene 15 / GENERATE → PREVIEW (13s, 390f)
//
// "Keynote object-worship". ONE hero artifact: the GENERATION slab. A thick
// glass slab whose face is a render farm — a GRID of asset tiles that fill in
// one-by-one as a progress bar climbs ("GENERATING…"). When the grid completes,
// a "Launch Motion Studio" button lights up and is clicked; the face transitions
// into a STUDIO PREVIEW where a play head scrubs across a row of scene
// thumbnails. The WORLD orbits (never the camera) in slow hold→ease arcs; spec
// Callouts billboard on drawn leader lines ("GENERATING…", "LAUNCH STUDIO",
// "13s PREVIEW"); the angle-tracked sheen rakes the turn. A riser builds into
// the launch where the slab LOCKS straight-on with an impact + shockwave — the
// payoff — then settles into the scrubbing studio, 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";
/* ----------------------------------------------------------------------------
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>
);
};
/* ============================================================================
GENERATE → PREVIEW. Hero = the generation slab. A 4x3 grid of asset tiles
fills in one-by-one while a progress bar climbs; at completion a "Launch
Motion Studio" button is clicked and the face flips to a studio preview where
a play head scrubs across scene thumbnails. World orbits in, callouts fire,
slab LOCKS front-on at the launch, then settles into the scrubbing studio.
========================================================================== */
export const MgOrbitScene15Generate: React.FC = () => {
const frame = useCurrentFrame();
const { fps } = useVideoConfig();
const t = frame / fps;
const LOCK = 8.4; // the launch lock-in beat (seconds) — the payoff
// --- generation grid: 12 tiles fill one-by-one ------------------------
const COLS = 4;
const ROWS = 3;
const TILES = COLS * ROWS;
const GEN_START = 1.0; // first tile begins
const GEN_STEP = 0.42; // seconds between tiles igniting
const GEN_FILL = 0.5; // seconds for a tile to pop in
// overall generation progress 0→1 (drives the bar + the "GENERATING…" %).
const genLast = GEN_START + (TILES - 1) * GEN_STEP + GEN_FILL;
const genProg = clamp01((t - GEN_START) / (genLast - GEN_START));
const genPct = Math.round(100 * genProg);
const genDone = genProg >= 1;
// --- launch button: lights once grid is full, clicked at the lock ------
const BTN_LIVE = genLast; // button becomes live when grid completes
const btnIn = beat(frame, fps, BTN_LIVE, BTN_LIVE + 0.5, EASE.backOut);
const btnGlow = 0.5 + 0.5 * osc(t, 1.4);
const press = beat(frame, fps, LOCK - 0.18, LOCK, EASE.out); // click depress
const pressScale = 1 - 0.06 * (press - clamp01((t - LOCK) / 0.18));
// --- studio preview: opens at the launch, play head scrubs -------------
const studioOpen = beat(frame, fps, LOCK, LOCK + 0.8, EASE.expoOut);
// play head sweeps left→right across the thumbnail strip, looping gently.
const scrubRaw = clamp01((t - (LOCK + 0.7)) / 3.4);
const scrub = scrubRaw; // 0→1 across the strip
const STUDIO_TILES = 5;
// --- the lock-in: a weighty spring settle on the launch ----------------
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 ringPulse = 0.5 + 0.5 * osc(t, 3);
const lockRing = clamp01(stamp);
// hold→ease orbit so the slab reads the grid filling, then a final arc that
// settles to centre (0°) right at the launch — the snap-to-camera payoff.
const legs = [
{ arc: -18, hold: 0.6, travel: 2.6 },
{ arc: 26, hold: 1.2, travel: 2.8 },
{ arc: -8, hold: 0.6, travel: 1.8 }, // lands ~0° near LOCK
];
// deterministic per-tile "kind" so the grid reads like varied assets.
const tileKind = (i: number) => i % 3; // 0 thumb, 1 wave, 2 chip
const HeroFace = (
<div
style={{
position: "absolute",
inset: 0,
padding: 30,
display: "flex",
flexDirection: "column",
gap: 16,
transform: `scale(${stampScale})`,
}}
>
{/* the lock ring that snaps tight on the launch beat */}
<div
style={{
position: "absolute",
inset: 16,
borderRadius: 24,
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",
zIndex: 5,
}}
/>
{/* header row — title + GENERATING…/READY status pill */}
<div
style={{
display: "flex",
alignItems: "center",
justifyContent: "space-between",
}}
>
<div
style={{
fontFamily: MONO,
fontSize: 12,
letterSpacing: 3,
color: C.textDim,
textTransform: "uppercase",
}}
>
render · queue
</div>
<div
style={{
display: "flex",
alignItems: "center",
gap: 8,
padding: "5px 12px",
borderRadius: 999,
background: withAlpha(genDone ? C.accent2 : C.accent, 0.16),
border: `1px solid ${withAlpha(
genDone ? C.accent2 : C.accent,
0.5,
)}`,
fontFamily: MONO,
fontSize: 12,
letterSpacing: 2,
color: genDone ? C.accent2 : C.accent,
}}
>
<span
style={{
width: 8,
height: 8,
borderRadius: "50%",
background: genDone ? C.accent2 : C.accent,
boxShadow: `0 0 10px ${C.accentGlow}`,
opacity: genDone ? 1 : 0.5 + 0.5 * osc(t, 0.9),
}}
/>
{genDone ? "READY" : `GENERATING ${genPct}%`}
</div>
</div>
{/* the GRID of asset tiles — fills one-by-one */}
<div
style={{
flex: 1,
display: "grid",
gridTemplateColumns: `repeat(${COLS}, 1fr)`,
gridTemplateRows: `repeat(${ROWS}, 1fr)`,
gap: 12,
position: "relative",
}}
>
{Array.from({ length: TILES }, (_, i) => {
const tAt = GEN_START + i * GEN_STEP;
const fp = beat(frame, fps, tAt, tAt + GEN_FILL, EASE.backOut);
const justLit = clamp01(1 - (t - (tAt + GEN_FILL)) / 0.4);
const kind = tileKind(i);
return (
<div
key={i}
style={{
borderRadius: 12,
position: "relative",
overflow: "hidden",
background:
fp > 0.02
? `linear-gradient(135deg, ${withAlpha(
C.accent2,
0.22,
)}, ${withAlpha("#0a0e16", 0.9)})`
: withAlpha("#0a0e16", 0.5),
border: `1px solid ${withAlpha(
C.accent,
0.18 + 0.4 * fp + 0.4 * justLit,
)}`,
opacity: mix(0.35, 1, fp),
transform: `scale(${mix(0.84, 1, fp)})`,
boxShadow:
fp > 0.5
? `0 6px 20px ${withAlpha("#000", 0.5)}, 0 0 ${(
18 * justLit
).toFixed(0)}px ${withAlpha(C.accent, 0.4 * justLit)}`
: "none",
}}
>
{/* tile content varies by kind to read as varied assets */}
{kind === 0 && (
<>
<div
style={{
position: "absolute",
left: 8,
top: 8,
width: 18,
height: 18,
borderRadius: 5,
background: withAlpha(C.accent, 0.55 * fp),
}}
/>
<div
style={{
position: "absolute",
left: 8,
right: 8,
bottom: 8,
height: 5,
borderRadius: 3,
background: withAlpha(C.accent2, 0.7 * fp),
}}
/>
</>
)}
{kind === 1 && (
<div
style={{
position: "absolute",
inset: 0,
display: "flex",
alignItems: "flex-end",
gap: 4,
padding: 10,
}}
>
{[0.4, 0.8, 0.55, 0.95, 0.65, 0.85].map((h, b) => (
<div
key={b}
style={{
flex: 1,
height: `${h * 100 * fp}%`,
borderRadius: 2,
background: withAlpha(C.accent, 0.5 * fp),
}}
/>
))}
</div>
)}
{kind === 2 && (
<div
style={{
position: "absolute",
inset: 0,
display: "flex",
flexDirection: "column",
justifyContent: "center",
gap: 6,
padding: 12,
}}
>
<div
style={{
height: 6,
width: `${70 * fp}%`,
borderRadius: 3,
background: withAlpha(C.accent2, 0.7 * fp),
}}
/>
<div
style={{
height: 6,
width: `${45 * fp}%`,
borderRadius: 3,
background: withAlpha("#ffffff", 0.22 * fp),
}}
/>
</div>
)}
{/* ignition flash as the tile lands */}
<div
style={{
position: "absolute",
inset: 0,
background: withAlpha("#ffffff", 0.5 * justLit),
mixBlendMode: "screen",
pointerEvents: "none",
}}
/>
</div>
);
})}
{/* STUDIO PREVIEW overlay — slides up over the grid on launch */}
<div
style={{
position: "absolute",
inset: 0,
borderRadius: 14,
background: `linear-gradient(160deg, ${withAlpha(
"#0b1018",
0.96,
)}, ${withAlpha("#05070c", 0.98)})`,
border: `1px solid ${withAlpha(C.accent, 0.4)}`,
opacity: studioOpen,
transform: `translateY(${mix(34, 0, studioOpen)}px)`,
display: "flex",
flexDirection: "column",
padding: 16,
gap: 12,
pointerEvents: "none",
}}
>
{/* studio header — Motion Studio + 13s preview clock */}
<div
style={{
display: "flex",
alignItems: "center",
justifyContent: "space-between",
}}
>
<div
style={{
fontFamily: FONT,
fontWeight: 800,
fontSize: 18,
letterSpacing: "-0.01em",
color: C.text,
}}
>
Motion Studio
</div>
<div
style={{
fontFamily: MONO,
fontSize: 12,
letterSpacing: 2,
color: C.accent2,
}}
>
{`00:${String(Math.floor(scrub * 13)).padStart(2, "0")} / 00:13`}
</div>
</div>
{/* big preview viewport — the current scene under the play head */}
<div
style={{
flex: 1,
borderRadius: 10,
position: "relative",
overflow: "hidden",
background: `radial-gradient(120% 120% at 50% 20%, ${withAlpha(
C.accent,
0.18,
)}, ${withAlpha("#06080d", 0.95)})`,
border: `1px solid ${withAlpha(C.accent, 0.3)}`,
}}
>
<div
style={{
position: "absolute",
left: "50%",
top: "46%",
width: 64,
height: 64,
transform: "translate(-50%,-50%)",
borderRadius: "50%",
background: ACCENT_GRAD,
boxShadow: `0 0 30px ${C.accentGlow}`,
display: "flex",
alignItems: "center",
justifyContent: "center",
opacity: studioOpen,
}}
>
{/* play triangle */}
<div
style={{
width: 0,
height: 0,
borderTop: "13px solid transparent",
borderBottom: "13px solid transparent",
borderLeft: `20px solid #1a0d03`,
marginLeft: 5,
}}
/>
</div>
</div>
{/* thumbnail strip + scrubbing play head */}
<div style={{ position: "relative", height: 46 }}>
<div
style={{
position: "absolute",
inset: 0,
display: "grid",
gridTemplateColumns: `repeat(${STUDIO_TILES}, 1fr)`,
gap: 6,
}}
>
{Array.from({ length: STUDIO_TILES }, (_, i) => {
const active =
Math.floor(scrub * STUDIO_TILES) === i ? 1 : 0;
return (
<div
key={i}
style={{
borderRadius: 7,
background: `linear-gradient(135deg, ${withAlpha(
C.accent2,
0.18 + 0.25 * active,
)}, ${withAlpha("#0a0e16", 0.92)})`,
border: `1px solid ${withAlpha(
C.accent,
0.25 + 0.5 * active,
)}`,
boxShadow: active
? `0 0 16px ${withAlpha(C.accent, 0.4)}`
: "none",
}}
/>
);
})}
</div>
{/* the play head line that scrubs across the strip */}
<div
style={{
position: "absolute",
top: -6,
bottom: -6,
left: `${scrub * 100}%`,
width: 2,
background: C.text,
boxShadow: `0 0 12px ${withAlpha("#ffffff", 0.8)}`,
opacity: studioOpen,
}}
>
<div
style={{
position: "absolute",
top: -7,
left: "50%",
transform: "translateX(-50%) rotate(45deg)",
width: 9,
height: 9,
background: C.text,
}}
/>
</div>
</div>
</div>
</div>
{/* progress bar + LAUNCH MOTION STUDIO button (hidden once studio opens) */}
<div
style={{
display: "flex",
alignItems: "center",
gap: 14,
opacity: 1 - studioOpen,
}}
>
{/* progress track */}
<div
style={{
flex: 1,
height: 10,
borderRadius: 6,
background: withAlpha("#ffffff", 0.08),
overflow: "hidden",
position: "relative",
}}
>
<div
style={{
position: "absolute",
inset: 0,
width: `${genProg * 100}%`,
borderRadius: 6,
background: ACCENT_GRAD,
boxShadow: `0 0 16px ${C.accentGlow}`,
}}
/>
</div>
{/* launch button — lights up once the grid is full, then is clicked */}
<div
style={{
padding: "11px 22px",
borderRadius: 12,
fontFamily: FONT,
fontWeight: 800,
fontSize: 17,
letterSpacing: "0.01em",
whiteSpace: "nowrap",
color: btnIn > 0.4 ? "#1a0d03" : C.textDim,
background:
btnIn > 0.02
? ACCENT_GRAD
: withAlpha("#ffffff", 0.06),
border: `1px solid ${withAlpha(C.accent, 0.4 + 0.4 * btnIn)}`,
boxShadow:
btnIn > 0.4
? `0 0 ${(22 + 14 * btnGlow).toFixed(
0,
)}px ${withAlpha(C.accent, 0.45 * btnGlow)}`
: "none",
opacity: mix(0.5, 1, btnIn),
transform: `scale(${mix(0.94, 1, btnIn) * pressScale})`,
}}
>
Launch Motion Studio
</div>
</div>
</div>
);
return (
<Plinth>
<Eyebrow at={0.3}>Generate · Preview</Eyebrow>
{/* riser glow swelling behind the hero into the launch */}
<AbsoluteFill style={{ pointerEvents: "none", zIndex: 1 }}>
<GlowPulse
size={1000}
color={withAlpha(C.accent, 0.1 * riser)}
strength={riser}
/>
</AbsoluteFill>
{/* impact flash + shockwave on the launch 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={820} h={500} depth={22} layers={16} radius={28}>
{HeroFace}
</Extrude>
{/* billboarded spec callouts firing at orbit waypoints */}
<Callout
title="The Render"
value="GENERATING…"
at={fps * 2.2}
x={0}
y={-330}
leader={120}
side="right"
/>
<Callout
title="12 Assets"
value="FILLED IN"
at={fps * 5.0}
x={490}
y={150}
leader={150}
side="right"
/>
<Callout
title="One Click"
value="LAUNCH STUDIO"
at={fps * (LOCK + 0.3)}
x={-500}
y={130}
leader={160}
side="left"
/>
<Callout
title="Live Preview"
value="13s PREVIEW"
at={fps * (LOCK + 1.6)}
x={0}
y={340}
leader={120}
side="right"
/>
</OrbitWorld>
{/* flat masked mirror sibling — re-renders the hero, simplified & matte */}
<Plinth.Reflection>
<Extrude w={820} h={500} depth={22} layers={10} radius={28} sheen={false}>
{HeroFace}
</Extrude>
</Plinth.Reflection>
</Plinth>
);
};
export default MgOrbitScene15Generate;