← Catalog
Edit Props (live)
Source
387 linesimport React from "react";
import {
AbsoluteFill,
Sequence,
Img,
useCurrentFrame,
useVideoConfig,
interpolate,
Easing,
staticFile,
} from "remotion";
import { loadFont as loadInter } from "@remotion/google-fonts/Inter";
import { ShaderTransition } from "./ShaderTransition";
import { PageBurn } from "./PageBurn";
import { FrostedGlassCard } from "./FrostedGlassCard";
import { AnimatedChart } from "./AnimatedChart";
loadInter("normal", { weights: ["400", "700", "900"] });
loadInter("italic", { weights: ["400", "900"] });
const FONT = "'Inter', sans-serif";
// ---- Demo timeline (30 fps) ----
// 0.0 - 3.0s (0-90) : Intro + GLITCH shader
// 3.0 - 5.5s (90-165) : DOMAIN-WARP shader
// 5.5 - 7.5s (165-225) : WHIP-PAN shader
// 7.5 - 11.0s (225-330) : PAGE BURN transition
// 11.0 - 16.0s (330-480): Frosted glass card
// 16.0 - 21.0s (480-630): Animated bar chart
// 21.0 - 26.0s (630-780): Animated line chart
export const TIER3_DEMO_DURATION_FRAMES = 780;
// Big section label that slides up then out
const SectionLabel: React.FC<{ text: string; sub?: string }> = ({ text, sub }) => {
const frame = useCurrentFrame();
const { durationInFrames } = useVideoConfig();
const inOp = interpolate(frame, [0, 8], [0, 1], { extrapolateRight: "clamp" });
const outOp = interpolate(
frame,
[durationInFrames - 12, durationInFrames - 2],
[1, 0],
{ extrapolateLeft: "clamp", extrapolateRight: "clamp" }
);
const op = Math.min(inOp, outOp);
const yIn = interpolate(frame, [0, 14], [24, 0], {
extrapolateRight: "clamp",
easing: Easing.out(Easing.cubic),
});
return (
<div
style={{
position: "absolute",
top: 130,
left: 0,
right: 0,
textAlign: "center",
opacity: op,
transform: `translateY(${yIn}px)`,
zIndex: 100,
pointerEvents: "none",
}}
>
<div
style={{
fontFamily: FONT,
fontSize: 28,
fontWeight: 700,
letterSpacing: 10,
color: "rgba(255,255,255,0.72)",
marginBottom: 8,
textShadow: "0 2px 16px rgba(0,0,0,0.85)",
}}
>
TIER 3
</div>
<div
style={{
fontFamily: FONT,
fontSize: 86,
fontWeight: 900,
letterSpacing: -3,
color: "#fff",
textShadow: "0 4px 30px rgba(0,0,0,0.9)",
lineHeight: 1,
}}
>
{text}
</div>
{sub && (
<div
style={{
fontFamily: FONT,
fontSize: 26,
fontWeight: 400,
fontStyle: "italic",
color: "rgba(255,255,255,0.7)",
marginTop: 10,
textShadow: "0 2px 12px rgba(0,0,0,0.85)",
}}
>
{sub}
</div>
)}
</div>
);
};
// Static image with subtle zoom — used between transitions
const StillImage: React.FC<{ src: string; scaleFrom?: number; scaleTo?: number }> = ({
src,
scaleFrom = 1.02,
scaleTo = 1.08,
}) => {
const frame = useCurrentFrame();
const { durationInFrames } = useVideoConfig();
const t = frame / Math.max(1, durationInFrames - 1);
const scale = interpolate(t, [0, 1], [scaleFrom, scaleTo]);
return (
<AbsoluteFill style={{ backgroundColor: "#000", overflow: "hidden" }}>
<Img
src={staticFile(src)}
style={{
width: "100%",
height: "100%",
objectFit: "cover",
transform: `scale(${scale})`,
}}
/>
</AbsoluteFill>
);
};
// Opening title card
const OpeningTitle: React.FC = () => {
const frame = useCurrentFrame();
const op = interpolate(frame, [0, 10, 50, 60], [0, 1, 1, 0], {
extrapolateRight: "clamp",
});
const y = interpolate(frame, [0, 14], [30, 0], {
extrapolateRight: "clamp",
easing: Easing.out(Easing.cubic),
});
// Bar draws in under title
const barW = interpolate(frame, [8, 28], [0, 360], {
extrapolateRight: "clamp",
easing: Easing.out(Easing.cubic),
});
return (
<AbsoluteFill
style={{
backgroundColor: "#0a0612",
display: "flex",
alignItems: "center",
justifyContent: "center",
flexDirection: "column",
}}
>
{/* Soft radial glow */}
<div
style={{
position: "absolute",
inset: 0,
background:
"radial-gradient(circle at 50% 55%, rgba(120,60,180,0.35), transparent 60%)",
}}
/>
<div
style={{
fontFamily: FONT,
fontSize: 30,
fontWeight: 700,
letterSpacing: 12,
color: "rgba(255,255,255,0.75)",
marginBottom: 20,
opacity: op,
transform: `translateY(${y}px)`,
}}
>
HYPERFRAMES × REMOTION
</div>
<div
style={{
fontFamily: FONT,
fontSize: 120,
fontWeight: 900,
letterSpacing: -5,
color: "#fff",
opacity: op,
transform: `translateY(${y}px)`,
lineHeight: 1,
}}
>
TIER 3
</div>
<div
style={{
width: barW,
height: 5,
background: "#c94f3d",
marginTop: 28,
opacity: op,
}}
/>
<div
style={{
fontFamily: FONT,
fontSize: 34,
fontWeight: 400,
fontStyle: "italic",
color: "rgba(255,255,255,0.82)",
marginTop: 32,
opacity: op,
transform: `translateY(${y}px)`,
}}
>
WebGL · Burn · Glass · Charts
</div>
</AbsoluteFill>
);
};
export const Tier3Demo: React.FC = () => {
// Timeline (all in frames @ 30fps)
const INTRO = 60;
const SHADER_DUR = 50; // Each shader ~1.67s
const PAGEBURN_DUR = 75; // 2.5s
const SCENE_DUR = 150; // 5s for scenes
let t = 0;
const introStart = t; t += INTRO;
const glitchStart = t; t += SHADER_DUR;
const domainStart = t; t += SHADER_DUR;
const whipStart = t; t += SHADER_DUR;
const burnStart = t; t += PAGEBURN_DUR;
const glassStart = t; t += SCENE_DUR;
const barStart = t; t += SCENE_DUR;
const lineStart = t; t += SCENE_DUR;
return (
<AbsoluteFill style={{ backgroundColor: "#000" }}>
{/* 0. Opening title */}
<Sequence from={introStart} durationInFrames={INTRO}>
<OpeningTitle />
</Sequence>
{/* 1. GLITCH shader transition */}
<Sequence from={glitchStart} durationInFrames={SHADER_DUR}>
<ShaderTransition
fromSrc="visuals/asset_001.jpg"
toSrc="visuals/asset_002.jpg"
shader="glitch"
accent={[0.8, 0.2, 0.9]}
/>
<Sequence durationInFrames={SHADER_DUR}>
<SectionLabel text="GLITCH" sub="shader · VHS corruption" />
</Sequence>
</Sequence>
{/* 2. DOMAIN-WARP shader */}
<Sequence from={domainStart} durationInFrames={SHADER_DUR}>
<ShaderTransition
fromSrc="visuals/asset_002.jpg"
toSrc="visuals/asset_003.jpg"
shader="domain-warp"
accent={[0.31, 0.6, 0.86]}
/>
<Sequence durationInFrames={SHADER_DUR}>
<SectionLabel text="DOMAIN WARP" sub="shader · ink dissolve" />
</Sequence>
</Sequence>
{/* 3. WHIP-PAN shader */}
<Sequence from={whipStart} durationInFrames={SHADER_DUR}>
<ShaderTransition
fromSrc="visuals/asset_003.jpg"
toSrc="visuals/asset_004.jpg"
shader="whip-pan"
accent={[1.0, 0.7, 0.2]}
/>
<Sequence durationInFrames={SHADER_DUR}>
<SectionLabel text="WHIP PAN" sub="shader · motion blur" />
</Sequence>
</Sequence>
{/* 4. PAGE BURN transition */}
<Sequence from={burnStart} durationInFrames={PAGEBURN_DUR}>
<PageBurn
fromSrc="visuals/asset_004.jpg"
toSrc="visuals/asset_005.jpg"
accent={[255, 120, 30]}
/>
<Sequence durationInFrames={PAGEBURN_DUR}>
<SectionLabel text="PAGE BURN" sub="canvas · fire + embers" />
</Sequence>
</Sequence>
{/* 5. FROSTED GLASS card */}
<Sequence from={glassStart} durationInFrames={SCENE_DUR}>
<FrostedGlassCard
backgroundSrc="visuals/asset_005.jpg"
artworkSrc="visuals/asset_006.jpg"
title="Late Night Drive"
subtitle="Midnight Tapes · Neon Nights"
trackLabel="NOW PLAYING"
tintColor="rgba(20, 10, 40, 0.4)"
/>
<Sequence durationInFrames={SCENE_DUR}>
<SectionLabel text="FROSTED GLASS" sub="scene · backdrop-filter" />
</Sequence>
</Sequence>
{/* 6. ANIMATED BAR CHART */}
<Sequence from={barStart} durationInFrames={SCENE_DUR}>
<AnimatedChart
mode="bar"
title={"Engagement\nby category"}
subtitle="Share of total views, Q1 2026"
bars={[
{ label: "Hooks", value: 42 },
{ label: "Demos", value: 58 },
{ label: "Stories", value: 71 },
{ label: "CTAs", value: 88 },
]}
counterValue={88}
counterSuffix="%"
accentColor="#c94f3d"
/>
</Sequence>
{/* 7. ANIMATED LINE CHART */}
<Sequence from={lineStart} durationInFrames={SCENE_DUR}>
<AnimatedChart
mode="line"
title={"Reel growth\nin ninety days"}
subtitle="Weekly unique views (thousands)"
linePoints={[12, 18, 15, 22, 28, 35, 31, 42, 58, 71, 85, 112]}
lineLabels={["W1", "W2", "W3", "W4", "W5", "W6", "W7", "W8", "W9", "W10", "W11", "W12"]}
counterValue={112000}
counterSuffix=""
accentColor="#1e6091"
backgroundColor="#f6f4ed"
/>
</Sequence>
{/* Persistent grain overlay across everything */}
<GrainOverlay />
</AbsoluteFill>
);
};
// Subtle film grain overlay - ported from HyperFrames grain-overlay component
const GrainOverlay: React.FC = () => {
const frame = useCurrentFrame();
// 10 keyframe positions, jumping every 0.5s (15 frames)
const pos = frame % 150;
const step = Math.floor(pos / 15);
const offsets = [
[0, 0], [-4, -2], [3, 1], [-2, 4], [1, -3],
[-3, 2], [4, 0], [0, -4], [-1, 3], [2, 1],
];
const [ox, oy] = offsets[step % offsets.length];
return (
<AbsoluteFill
style={{
opacity: 0.08,
pointerEvents: "none",
mixBlendMode: "overlay",
zIndex: 1000,
}}
>
<svg
width="200%"
height="200%"
style={{ position: "absolute", transform: `translate(${ox}%, ${oy}%)` }}
>
<filter id="grain-noise">
<feTurbulence type="fractalNoise" baseFrequency="0.85" numOctaves="3" stitchTiles="stitch" />
<feColorMatrix values="0 0 0 0 0.5 0 0 0 0 0.5 0 0 0 0 0.5 0 0 0 1 0" />
</filter>
<rect width="100%" height="100%" filter="url(#grain-noise)" />
</svg>
</AbsoluteFill>
);
};
Schema (Zod)
// Auto-extracted from Props interface — see source for authoritative types.
// Component: Tier3Demo
// 0 props detected · 0 port(s)
Tier3Demo
tier3demoTier3Demo — Andy's hand-crafted Remotion scene. 0 props. no ports detected — source-only preview.
Workflow Inputs (0)
This block has no workflow-connected inputs.
Meta
- Updated
- 4/21/2026