// @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 (
{children}
);
};
/* ============================================================================
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 = (
{/* the lock ring that snaps tight on the launch beat */}
{/* header row — title + GENERATING…/READY status pill */}
render · queue
{genDone ? "READY" : `GENERATING ${genPct}%`}
{/* the GRID of asset tiles — fills one-by-one */}
{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 (
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 && (
<>
>
)}
{kind === 1 && (
{[0.4, 0.8, 0.55, 0.95, 0.65, 0.85].map((h, b) => (
))}
)}
{kind === 2 && (
)}
{/* ignition flash as the tile lands */}
);
})}
{/* STUDIO PREVIEW overlay — slides up over the grid on launch */}
{/* studio header — Motion Studio + 13s preview clock */}
Motion Studio
{`00:${String(Math.floor(scrub * 13)).padStart(2, "0")} / 00:13`}
{/* big preview viewport — the current scene under the play head */}
{/* thumbnail strip + scrubbing play head */}
{Array.from({ length: STUDIO_TILES }, (_, i) => {
const active =
Math.floor(scrub * STUDIO_TILES) === i ? 1 : 0;
return (
);
})}
{/* the play head line that scrubs across the strip */}
{/* progress bar + LAUNCH MOTION STUDIO button (hidden once studio opens) */}
{/* progress track */}
{/* launch button — lights up once the grid is full, then is clicked */}
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
);
return (
Generate · Preview
{/* riser glow swelling behind the hero into the launch */}
{/* impact flash + shockwave on the launch payoff */}
{HeroFace}
{/* billboarded spec callouts firing at orbit waypoints */}
{/* flat masked mirror sibling — re-renders the hero, simplified & matte */}
{HeroFace}
);
};
export default MgOrbitScene15Generate;