# Complete Example: 15-Second Product Ad

Full implementation of a vertical product ad for social media.

## Project Setup

```bash
npx create-video@latest my-product-ad --template blank
cd my-product-ad
npm install
```

## File Structure

```
my-product-ad/
├── public/
│   ├── logo.png
│   ├── product.png
│   └── music.mp3 (optional)
├── src/
│   ├── index.ts
│   ├── Root.tsx
│   └── ProductAd/
│       ├── index.tsx
│       ├── HookScene.tsx
│       ├── FeaturesScene.tsx
│       └── CTAScene.tsx
├── .mcp.json
└── package.json
```

## .mcp.json

```json
{
  "mcpServers": {
    "chrome-devtools": {
      "command": "npx",
      "args": ["-y", "@anthropic/mcp-chrome-devtools@latest"]
    }
  }
}
```

## src/index.ts

```ts
import {registerRoot} from 'remotion';
import {RemotionRoot} from './Root';

registerRoot(RemotionRoot);
```

## src/Root.tsx

```tsx
import {Composition} from 'remotion';
import {ProductAd, ProductAdProps} from './ProductAd';

export const RemotionRoot = () => {
  const defaultProps: ProductAdProps = {
    headline: 'Finally, Coffee That Keeps Up With You',
    productName: 'BrewMaster Pro',
    features: [
      '☕ 10-second brew time',
      '🌡️ Perfect temperature, every time',
      '📱 App-controlled',
    ],
    price: '₱2,499',
    ctaText: 'Order Now',
    brandColor: '#1e3a5f',
    accentColor: '#c9a227',
  };

  return (
    <>
      {/* Vertical - Stories/Reels/TikTok */}
      <Composition
        id="ProductAd-Vertical"
        component={ProductAd}
        durationInFrames={450}
        fps={30}
        width={1080}
        height={1920}
        defaultProps={defaultProps}
      />

      {/* Square - Instagram Feed */}
      <Composition
        id="ProductAd-Square"
        component={ProductAd}
        durationInFrames={450}
        fps={30}
        width={1080}
        height={1080}
        defaultProps={defaultProps}
      />

      {/* Horizontal - YouTube */}
      <Composition
        id="ProductAd-Horizontal"
        component={ProductAd}
        durationInFrames={450}
        fps={30}
        width={1920}
        height={1080}
        defaultProps={defaultProps}
      />
    </>
  );
};
```

## src/ProductAd/index.tsx

```tsx
import {AbsoluteFill, Audio, Sequence, staticFile, useVideoConfig} from 'remotion';
import {HookScene} from './HookScene';
import {FeaturesScene} from './FeaturesScene';
import {CTAScene} from './CTAScene';

export interface ProductAdProps {
  headline: string;
  productName: string;
  features: string[];
  price: string;
  ctaText: string;
  brandColor: string;
  accentColor: string;
}

export const ProductAd: React.FC<ProductAdProps> = (props) => {
  const {width, height} = useVideoConfig();
  const isVertical = height > width;

  return (
    <AbsoluteFill style={{
      backgroundColor: props.brandColor,
      fontFamily: 'Inter, system-ui, sans-serif',
    }}>
      {/* Optional background music */}
      {/* <Audio src={staticFile('music.mp3')} volume={0.2} /> */}

      {/* Scene 1: Hook (0-3s) */}
      <Sequence from={0} durationInFrames={90}>
        <HookScene 
          headline={props.headline}
          accentColor={props.accentColor}
          isVertical={isVertical}
        />
      </Sequence>

      {/* Scene 2: Features (3-11s) */}
      <Sequence from={90} durationInFrames={240}>
        <FeaturesScene 
          features={props.features}
          accentColor={props.accentColor}
          isVertical={isVertical}
        />
      </Sequence>

      {/* Scene 3: CTA (11-15s) */}
      <Sequence from={330} durationInFrames={120}>
        <CTAScene 
          productName={props.productName}
          price={props.price}
          ctaText={props.ctaText}
          accentColor={props.accentColor}
          isVertical={isVertical}
        />
      </Sequence>
    </AbsoluteFill>
  );
};
```

## src/ProductAd/HookScene.tsx

```tsx
import {AbsoluteFill, interpolate, useCurrentFrame, spring, useVideoConfig} from 'remotion';

interface HookSceneProps {
  headline: string;
  accentColor: string;
  isVertical: boolean;
}

export const HookScene: React.FC<HookSceneProps> = ({headline, accentColor, isVertical}) => {
  const frame = useCurrentFrame();
  const {fps} = useVideoConfig();
  
  const scale = spring({frame, fps, config: {damping: 12, stiffness: 100}});
  const opacity = interpolate(frame, [0, 20], [0, 1], {extrapolateRight: 'clamp'});

  return (
    <AbsoluteFill style={{
      justifyContent: 'center',
      alignItems: 'center',
      padding: isVertical ? 60 : 100,
    }}>
      <h1 style={{
        opacity,
        transform: `scale(${scale})`,
        fontSize: isVertical ? 64 : 72,
        fontWeight: 800,
        color: 'white',
        textAlign: 'center',
        lineHeight: 1.2,
        textShadow: '0 4px 20px rgba(0,0,0,0.3)',
      }}>
        {headline.split(' ').map((word, i) => {
          const delay = i * 3;
          const wordOpacity = interpolate(frame - delay, [0, 10], [0, 1], {
            extrapolateLeft: 'clamp',
            extrapolateRight: 'clamp',
          });
          const isAccent = i === headline.split(' ').length - 1;
          
          return (
            <span
              key={i}
              style={{
                opacity: wordOpacity,
                color: isAccent ? accentColor : 'white',
                display: 'inline-block',
                marginRight: 12,
              }}
            >
              {word}
            </span>
          );
        })}
      </h1>
    </AbsoluteFill>
  );
};
```

## src/ProductAd/FeaturesScene.tsx

```tsx
import {AbsoluteFill, interpolate, useCurrentFrame} from 'remotion';

interface FeaturesSceneProps {
  features: string[];
  accentColor: string;
  isVertical: boolean;
}

export const FeaturesScene: React.FC<FeaturesSceneProps> = ({features, accentColor, isVertical}) => {
  const frame = useCurrentFrame();

  return (
    <AbsoluteFill style={{
      justifyContent: 'center',
      alignItems: isVertical ? 'flex-start' : 'center',
      padding: isVertical ? 80 : 120,
    }}>
      <div style={{
        display: 'flex',
        flexDirection: 'column',
        gap: isVertical ? 50 : 40,
      }}>
        {features.map((feature, i) => {
          const delay = i * 50;
          const opacity = interpolate(frame - delay, [0, 20], [0, 1], {
            extrapolateLeft: 'clamp',
            extrapolateRight: 'clamp',
          });
          const x = interpolate(frame - delay, [0, 20], [-60, 0], {
            extrapolateLeft: 'clamp',
            extrapolateRight: 'clamp',
          });

          return (
            <div
              key={i}
              style={{
                opacity,
                transform: `translateX(${x}px)`,
                fontSize: isVertical ? 42 : 48,
                fontWeight: 600,
                color: 'white',
                padding: '20px 30px',
                backgroundColor: 'rgba(255,255,255,0.1)',
                borderRadius: 16,
                borderLeft: `4px solid ${accentColor}`,
              }}
            >
              {feature}
            </div>
          );
        })}
      </div>
    </AbsoluteFill>
  );
};
```

## src/ProductAd/CTAScene.tsx

```tsx
import {AbsoluteFill, interpolate, useCurrentFrame, spring, useVideoConfig} from 'remotion';

interface CTASceneProps {
  productName: string;
  price: string;
  ctaText: string;
  accentColor: string;
  isVertical: boolean;
}

export const CTAScene: React.FC<CTASceneProps> = ({
  productName,
  price,
  ctaText,
  accentColor,
  isVertical,
}) => {
  const frame = useCurrentFrame();
  const {fps} = useVideoConfig();
  
  const scale = spring({frame, fps, config: {damping: 10, stiffness: 120}});
  
  // Pulsing effect on button
  const buttonScale = interpolate(
    frame % 30,
    [0, 15, 30],
    [1, 1.05, 1],
  );

  return (
    <AbsoluteFill style={{
      justifyContent: 'center',
      alignItems: 'center',
      backgroundColor: accentColor,
    }}>
      <div style={{
        transform: `scale(${scale})`,
        textAlign: 'center',
      }}>
        <div style={{
          fontSize: isVertical ? 36 : 32,
          fontWeight: 500,
          color: 'rgba(0,0,0,0.6)',
          marginBottom: 10,
          textTransform: 'uppercase',
          letterSpacing: 2,
        }}>
          {productName}
        </div>
        
        <div style={{
          fontSize: isVertical ? 96 : 80,
          fontWeight: 900,
          color: 'white',
          marginBottom: 40,
          textShadow: '0 4px 20px rgba(0,0,0,0.2)',
        }}>
          {price}
        </div>
        
        <div style={{
          transform: `scale(${buttonScale})`,
          display: 'inline-block',
          padding: '24px 60px',
          backgroundColor: 'white',
          borderRadius: 50,
          fontSize: isVertical ? 36 : 32,
          fontWeight: 700,
          color: '#1a1a1a',
          boxShadow: '0 10px 40px rgba(0,0,0,0.2)',
        }}>
          {ctaText} →
        </div>
      </div>
    </AbsoluteFill>
  );
};
```

## Running the Project

```bash
# Start preview
npx remotion studio

# Render vertical version
npx remotion render src/index.ts ProductAd-Vertical out/ad-vertical.mp4

# Render all versions
npx remotion render src/index.ts ProductAd-Vertical out/ad-vertical.mp4
npx remotion render src/index.ts ProductAd-Square out/ad-square.mp4
npx remotion render src/index.ts ProductAd-Horizontal out/ad-horizontal.mp4
```

## Output

- `out/ad-vertical.mp4` — 1080×1920, 15 seconds, for Stories/Reels/TikTok
- `out/ad-square.mp4` — 1080×1080, 15 seconds, for Instagram Feed
- `out/ad-horizontal.mp4` — 1920×1080, 15 seconds, for YouTube/Web
