We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/chrishayuk/chuk-mcp-remotion'
If you have feedback or need assistance with the MCP directory API, please join our Discord server
{/* chuk-motion/src/chuk_motion/components/overlays/TitleScene/template.tsx.j2 */}
import React from 'react';
import { AbsoluteFill, interpolate, spring, useCurrentFrame, useVideoConfig } from 'remotion';
interface TitleSceneProps {
title: string;
subtitle?: string;
startFrame: number;
durationInFrames: number;
variant?: string;
animation?: string;
}
export const TitleScene: React.FC<TitleSceneProps> = ({
title,
subtitle,
startFrame,
durationInFrames,
variant = 'bold',
animation = 'fade'
}) => {
const frame = useCurrentFrame();
const { fps } = useVideoConfig();
const relativeFrame = frame - startFrame;
// Don't render if outside the time range
if (frame < startFrame || frame >= startFrame + durationInFrames) {
return null;
}
// Animation logic
let opacity = 1;
let transform = 'none';
let filter = 'none';
let displayText = title;
if (animation === 'fade_zoom') {
// Fade + Zoom animation
const progress = spring({
frame: relativeFrame,
fps,
config: {
damping: [[ motion.default_spring.config.damping ]],
mass: [[ motion.default_spring.config.mass ]],
stiffness: [[ motion.default_spring.config.stiffness ]]
}
});
opacity = interpolate(progress, [0, 1], [0, 1]);
const scale = interpolate(progress, [0, 1], [0.8, 1]);
transform = `scale(${scale})`;
} else if (animation === 'slide_up') {
// Slide up animation
const progress = spring({
frame: relativeFrame,
fps,
config: {
damping: [[ motion.default_spring.config.damping ]],
mass: [[ motion.default_spring.config.mass ]],
stiffness: [[ motion.default_spring.config.stiffness ]]
}
});
opacity = interpolate(progress, [0, 1], [0, 1]);
const translateY = interpolate(progress, [0, 1], [100, 0]);
transform = `translateY(${translateY}px)`;
} else if (animation === 'typewriter') {
// Typewriter animation
const charsToShow = Math.floor(interpolate(relativeFrame, [0, 60], [0, title.length], {
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp'
}));
displayText = title.slice(0, charsToShow);
opacity = 1;
} else if (animation === 'blur_in') {
// Blur to focus animation
const progress = spring({
frame: relativeFrame,
fps,
config: { damping: 150, mass: 0.5, stiffness: 150 }
});
opacity = interpolate(progress, [0, 1], [0, 1]);
const blur = interpolate(progress, [0, 1], [20, 0]);
filter = `blur(${blur}px)`;
} else if (animation === 'fade_slide') {
// Fade + Slide animation
const progress = spring({
frame: relativeFrame,
fps,
config: {
damping: [[ motion.default_spring.config.damping ]],
mass: [[ motion.default_spring.config.mass ]],
stiffness: [[ motion.default_spring.config.stiffness ]]
}
});
opacity = interpolate(progress, [0, 1], [0, 1]);
const translateX = interpolate(progress, [0, 1], [-50, 0]);
transform = `translateX(${translateX}px)`;
} else if (animation === 'zoom') {
// Zoom animation
const progress = spring({
frame: relativeFrame,
fps,
config: {
damping: [[ motion.default_spring.config.damping ]],
mass: [[ motion.default_spring.config.mass ]],
stiffness: [[ motion.default_spring.config.stiffness ]]
}
});
opacity = interpolate(progress, [0, 1], [0, 1]);
const scale = interpolate(progress, [0, 1], [1.2, 1]);
transform = `scale(${scale})`;
} else {
// Default: fade in
opacity = interpolate(relativeFrame, [0, 20], [0, 1], {
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp'
});
}
// Fade out at the end
const fadeOutStart = durationInFrames - 20;
const fadeOut = interpolate(
relativeFrame,
[fadeOutStart, durationInFrames],
[1, 0],
{
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp'
}
);
const finalOpacity = opacity * fadeOut;
// Variant styles using theme tokens
const variants: Record<string, any> = {
minimal: {
background: '[[ colors.background.light ]]',
textColor: '[[ colors.text.on_light ]]',
accentColor: '[[ colors.primary[0] ]]'
},
standard: {
background: '[[ colors.gradient.primary_to_secondary ]]',
textColor: '[[ colors.text.on_dark ]]',
accentColor: '[[ colors.accent[0] ]]'
},
bold: {
background: '[[ colors.gradient.bold ]]',
textColor: '[[ colors.text.on_dark ]]',
accentColor: '[[ colors.text.on_dark ]]'
},
kinetic: {
background: '[[ colors.background.dark ]]',
textColor: '[[ colors.text.on_dark ]]',
accentColor: '[[ colors.accent[0] ]]'
},
glass: {
background: '[[ colors.background.glass ]]',
textColor: '[[ colors.text.on_dark ]]',
accentColor: '[[ colors.accent[0] ]]'
}
};
const variantStyle = variants[variant] || variants.bold;
return (
<AbsoluteFill
style={{
background: variantStyle.background,
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
fontFamily: "'[[ "', '".join(typography.primary_font.fonts) ]]'",
padding: parseInt('[[ spacing.spacing['4xl'] ]]')
}}
>
<div
style={{
opacity: finalOpacity,
transform,
filter,
textAlign: 'center'
}}
>
<h1
style={{
fontSize: variant === 'bold' ? parseInt('[[ typography.font_sizes[typography.default_resolution]['4xl'] ]]') : parseInt('[[ typography.font_sizes[typography.default_resolution]['2xl'] ]]'),
fontWeight: variant === 'kinetic' ? parseInt('[[ typography.font_weights.black ]]') : parseInt('[[ typography.font_weights.bold ]]'),
color: variantStyle.textColor,
margin: 0,
lineHeight: parseFloat('[[ typography.line_heights.tight ]]'),
letterSpacing: '[[ typography.letter_spacing.tight ]]',
textTransform: variant === 'kinetic' ? 'uppercase' : 'none',
maxWidth: 1200
}}
>
{displayText}
</h1>
{subtitle && (
<p
style={{
fontSize: parseInt('[[ typography.font_sizes[typography.default_resolution].lg ]]'),
fontWeight: parseInt('[[ typography.font_weights.medium ]]'),
color: variantStyle.accentColor,
margin: '[[ spacing.spacing.xl ]] 0 0 0',
lineHeight: parseFloat('[[ typography.line_heights.snug ]]'),
maxWidth: 900
}}
>
{subtitle}
</p>
)}
</div>
</AbsoluteFill>
);
};