← Catalog
Edit Props (live)
TEXT
TEXT
config
config
config
config
Source
257 linesimport React from "react";
import {
AbsoluteFill,
Img,
useCurrentFrame,
useVideoConfig,
interpolate,
spring,
staticFile,
Easing,
} from "remotion";
import { loadFont as loadInter } from "@remotion/google-fonts/Inter";
loadInter("normal", { weights: ["400", "600", "700", "900"] });
interface FrostedGlassCardProps {
backgroundSrc: string;
artworkSrc: string;
title: string;
subtitle: string;
trackLabel?: string; // e.g. "NOW PLAYING"
// tint colour overlay on background (helps readability)
tintColor?: string;
}
export const FrostedGlassCard: React.FC<FrostedGlassCardProps> = ({
backgroundSrc,
artworkSrc,
title,
subtitle,
trackLabel = "NOW PLAYING",
tintColor = "rgba(15, 10, 30, 0.35)",
}) => {
const frame = useCurrentFrame();
const { fps, durationInFrames, width, height } = useVideoConfig();
// Card entrance spring
const cardSpring = spring({
frame,
fps,
config: { mass: 0.9, damping: 14, stiffness: 120 },
});
const cardY = interpolate(cardSpring, [0, 1], [160, 0]);
const cardOpacity = interpolate(frame, [0, 15], [0, 1], {
extrapolateRight: "clamp",
});
const cardScale = interpolate(cardSpring, [0, 1], [0.94, 1]);
// Album art breathing
const breathT = (frame / fps) * 0.7;
const artScale = 1 + Math.sin(breathT * Math.PI * 2) * 0.012;
// Background zoom (Ken Burns)
const bgProgress = frame / Math.max(durationInFrames, 1);
const bgScale = interpolate(bgProgress, [0, 1], [1.08, 1.15]);
// Specular sweep highlight — moves across the card
const sweepCycle = (frame / fps) % 3.5;
const sweepT = Math.min(1, sweepCycle / 1.2);
const sweepX = interpolate(sweepT, [0, 1], [-40, 140]);
const sweepOpacity = sweepCycle < 1.2 ? interpolate(sweepT, [0, 0.5, 1], [0, 0.6, 0]) : 0;
// Progress bar on the card
const barProgress = (frame / fps) % 8;
const barFill = interpolate(barProgress, [0, 8], [0.15, 0.85]);
// Card layout
const cardW = 900;
const cardH = 280;
const cardX = (width - cardW) / 2;
const cardTop = height - cardH - 260;
return (
<AbsoluteFill style={{ backgroundColor: "#0a0612" }}>
{/* Background image with Ken Burns + dark tint */}
<div style={{ position: "absolute", inset: 0, overflow: "hidden" }}>
<Img
src={staticFile(backgroundSrc)}
style={{
width: "100%",
height: "100%",
objectFit: "cover",
transform: `scale(${bgScale})`,
// Heavier blur + desaturation to mimic a frosted-glass FEEL over the
// whole background — backdrop-filter is unreliable in headless
// Chrome, so we bake the blur into the background itself.
filter: "saturate(1.2) brightness(0.55) blur(28px)",
}}
/>
<div style={{ position: "absolute", inset: 0, background: tintColor }} />
{/* Subtle vignette */}
<div
style={{
position: "absolute",
inset: 0,
background:
"radial-gradient(ellipse at center, transparent 35%, rgba(0,0,0,0.65) 100%)",
}}
/>
</div>
{/* Floating label above card */}
<div
style={{
position: "absolute",
top: cardTop - 80,
left: 0,
right: 0,
textAlign: "center",
fontFamily: "'Inter', sans-serif",
fontSize: 28,
fontWeight: 700,
letterSpacing: 8,
color: "rgba(255,255,255,0.85)",
textShadow: "0 2px 20px rgba(0,0,0,0.6)",
opacity: interpolate(frame, [5, 20], [0, 1], { extrapolateRight: "clamp" }),
transform: `translateY(${interpolate(
frame,
[5, 25],
[20, 0],
{ extrapolateRight: "clamp", easing: Easing.out(Easing.cubic) }
)}px)`,
}}
>
{trackLabel}
</div>
{/* Frosted glass card */}
<div
style={{
position: "absolute",
left: cardX,
top: cardTop,
width: cardW,
height: cardH,
borderRadius: 32,
overflow: "hidden",
opacity: cardOpacity,
transform: `translateY(${cardY}px) scale(${cardScale})`,
background: "rgba(255, 255, 255, 0.12)",
backdropFilter: "blur(40px) saturate(180%)",
WebkitBackdropFilter: "blur(40px) saturate(180%)",
border: "1px solid rgba(255, 255, 255, 0.25)",
boxShadow:
"0 30px 80px rgba(0, 0, 0, 0.45), inset 0 1px 0 rgba(255,255,255,0.3), inset 0 -1px 0 rgba(0,0,0,0.1)",
display: "flex",
alignItems: "center",
padding: 28,
gap: 28,
}}
>
{/* Specular edge highlight along top */}
<div
style={{
position: "absolute",
top: 0,
left: 0,
right: 0,
height: 3,
background:
"linear-gradient(90deg, transparent, rgba(255,255,255,0.6), transparent)",
pointerEvents: "none",
}}
/>
{/* Sweep highlight */}
<div
style={{
position: "absolute",
inset: 0,
background: `linear-gradient(110deg, transparent ${sweepX - 15}%, rgba(255,255,255,0.35) ${sweepX}%, transparent ${sweepX + 15}%)`,
opacity: sweepOpacity,
pointerEvents: "none",
mixBlendMode: "overlay",
}}
/>
{/* Album art */}
<div
style={{
width: 224,
height: 224,
borderRadius: 20,
overflow: "hidden",
flexShrink: 0,
boxShadow: "0 12px 40px rgba(0,0,0,0.5)",
transform: `scale(${artScale})`,
}}
>
<Img
src={staticFile(artworkSrc)}
style={{ width: "100%", height: "100%", objectFit: "cover" }}
/>
</div>
{/* Text + progress */}
<div style={{ flex: 1, color: "#fff", fontFamily: "'Inter', sans-serif" }}>
<div
style={{
fontSize: 46,
fontWeight: 900,
letterSpacing: -1,
lineHeight: 1.05,
marginBottom: 10,
textShadow: "0 2px 20px rgba(0,0,0,0.4)",
}}
>
{title}
</div>
<div
style={{
fontSize: 28,
fontWeight: 500,
opacity: 0.82,
marginBottom: 24,
textShadow: "0 1px 10px rgba(0,0,0,0.3)",
}}
>
{subtitle}
</div>
{/* Progress bar */}
<div
style={{
width: "100%",
height: 6,
background: "rgba(255,255,255,0.22)",
borderRadius: 4,
overflow: "hidden",
}}
>
<div
style={{
width: `${barFill * 100}%`,
height: "100%",
background: "#fff",
borderRadius: 4,
boxShadow: "0 0 10px rgba(255,255,255,0.5)",
}}
/>
</div>
<div
style={{
display: "flex",
justifyContent: "space-between",
marginTop: 10,
fontSize: 18,
opacity: 0.7,
}}
>
<span>1:42</span>
<span>3:18</span>
</div>
</div>
</div>
</AbsoluteFill>
);
};
Schema (Zod)
// Auto-extracted from Props interface — see source for authoritative types.
// Component: FrostedGlassCard
// 6 props detected · 2 port(s)
Frosted Glass Card
frosted_glass_cardFrosted Glass Card — Andy's hand-crafted Remotion scene. 6 props. title:TEXT, subtitle:TEXT.
Workflow Inputs (2)
- Title
titleTEXTRequired - Subtitle
subtitleTEXTRequired
Config Fields (4)
tintColorartworkSrctrackLabelbackgroundSrc
Meta
- Updated
- 4/21/2026