Animated Video Overlays with Remotion

Build animated video overlays with React, Remotion, and Claude Code.

Introduction

30 Second Summary

Every polished video has animated overlays. The lower-third banners, title cards, and progress bars you see on YouTube and live streams are usually built by hand in video editing software.

In this project, you will build animated video overlays entirely with code using Remotion and Claude Code. You will render a finished MP4 and upload it to Google Drive.

What You'll Build

An animated video with text overlays, a color-shifting lower-third banner, and a progress bar built programmatically using React and Remotion.

By the end of this project, you'll have:

  • A working Remotion project with animated overlay components built using Claude Code as your AI co-pilot.
  • A rendered MP4 video with text animations, color transitions, and graphics layered on top of an existing video.
  • A Node.js upload script that sends your finished video to Google Drive using the Drive API.
  • Secret Mission: Add a custom animated intro sequence that plays before the main video starts.

Are there any prerequisites?

You need a Claude Pro or Max subscription with Claude Code access and a Google account. No prior React or Remotion experience is required.

Set Up Your Development Environment

Before you can build animated video overlays, you need a few tools in place.

In this step, you'll install Node.js (the runtime that powers Remotion), scaffold a brand-new Remotion project, and connect Claude Code as your AI co-pilot.

In this step, get ready to:
  • Install Node.js and Claude Code.
  • Scaffold a Remotion project with the Blank template, TailwindCSS, and Agent Skills.
  • Launch Remotion Studio and start Claude Code inside your project.
Install Node.js and Claude Code

Remotion needs Node.js v16 or higher to run. Claude Code is the AI assistant that will help you write animation code throughout the project.

  • Press Cmd+Space (macOS) or the Windows key (Windows) to open your search bar.
  • Type Terminal (macOS) or Command Prompt (Windows) and press Enter to open it.
  • Check if Node.js is already installed by running this command:
node --version

✔️ I see a version number

Node.js is ready to go. Move on to the Claude Code install below.

ⓧ Command not found

You need to install Node.js first.

  • Go to https://nodejs.org in your browser.
  • Download the LTS (Long Term Support) version.
  • Run the installer and follow the on-screen prompts. Accept all defaults.
  • Close and reopen your terminal after the installation finishes.
  • Verify the installation by running:
node --version

You should see a version number like v22.x.x printed in your terminal.

  • Install Claude Code globally by running this command:
npm install -g @anthropic-ai/claude-code

What does this command do?

The -g flag installs Claude Code globally on your machine. This means you can run the claude command from any project folder, not just this one.

  • Verify Claude Code is installed by running:
claude --version

You should see a version number printed in your terminal.

Claude Code install failing?

Make sure you have Node.js installed and your terminal is reopened after installing it. On macOS, you may need to prefix the command with sudo if you see a permissions error.

Claude Code also requires an active Claude Pro or Max subscription. If you don't have one yet, sign up at claude.ai before continuing.

Help me troubleshoot my Claude Code installation

Scaffold a new Remotion project

Remotion provides a scaffolding command that sets up a ready-to-go video project. You'll use the Blank template, which gives you a clean starting point without any demo code.

  • Move to your Desktop by running this command:
cd ~/Desktop
  • Scaffold your Remotion project by running this command:
npx create-video@latest

What happens when you run this?

This command launches an interactive setup wizard. It will ask you a series of questions to configure your project. Follow the prompts below carefully.

  • When prompted for a project name, type a name for your project folder (e.g. my-video).
  • Select the Blank template.
  • Enter yes when asked to use TailwindCSS.
  • Enter yes when asked to install Skills.

Why the Blank template with Skills?

The Blank template gives you a clean slate without demo components cluttering your project. The Agent Skills are curated instructions that teach Claude Code Remotion-specific best practices.

Together, these settings create a project optimized for AI-assisted video development.

Wait for the scaffolding to finish. It will install all dependencies automatically.

  • Move into your new project folder by running this command:
cd [[PROJECT_NAME="your project folder name, e.g. my-video"]]
  • Confirm the project files exist by running:
ls

You should see files like package.json, tsconfig.json, a src/ directory, and a public/ folder in the output.

  • Open the project in VS Code (or your preferred code editor). Press Cmd+Space (macOS) or the Windows key (Windows) to open your search bar.
  • Type VS Code and press Enter to open it.
  • In VS Code, click File in the top menu bar.
  • Click Open Folder.
  • Navigate to your Desktop and select the project folder you just created, then click Open.
  • Launch Remotion Studio by running this command in your terminal:
npm run dev

Your browser should open automatically to http://localhost:3000 showing Remotion Studio. This is where you'll preview your animations as you build them.

Remotion Studio not loading?

Make sure you're inside the project folder (not the Desktop) when you run npm run dev. If the browser doesn't open automatically, visit http://localhost:3000 manually.

If you see a port conflict, another process may already be using port 3000. Close that process or restart your terminal and try again.

Help me fix Remotion Studio not loading in my browser

Start Claude Code inside your project

Claude Code needs to run in a separate terminal so that Remotion Studio stays active in the first one. Claude Code will read the Agent Skills you installed during scaffolding to understand Remotion's patterns.

  • Open a second terminal window. Press Cmd+Space (macOS) or the Windows key (Windows) and search for Terminal or Command Prompt again.
  • Navigate into your project folder by running this command:
cd ~/Desktop/[[PROJECT_NAME="your project folder name, e.g. my-video"]]
  • Start Claude Code by running:
claude

Claude Code will start up and connect to your project. If this is your first time running Claude Code, it will prompt you to sign in with your Anthropic account in your browser.

  • Verify that Claude Code can see your project files and the installed Agent Skills. Check that the .claude/skills/ directory exists by running this in your Claude Code session:
ls .claude/skills

You should see Remotion Agent Skills files listed. These skills give Claude Code deep knowledge about Remotion's API and best practices.

What are Agent Skills?

Agent Skills are curated instruction files maintained by the Remotion team. They live in your project's .claude/skills/ folder and teach Claude Code how to write Remotion code correctly.

When you prompt Claude Code to create animations, it reads these skills automatically and follows Remotion-specific patterns.

Don't see the skills folder?

If the .claude/skills/ directory is missing, you may have skipped the Skills install during scaffolding. You can add them manually by running:

npx skills add remotion-dev/skills

Help me install Remotion Agent Skills for Claude Code

Your environment is fully set up. You have Remotion Studio running in one terminal and Claude Code ready in another. Next up, you'll use Claude Code to create your first animations.

Create Your First Animations with Claude Code

Your environment is set up with Remotion Studio running and Claude Code connected to your project.

Now it is time to create your first animation. In Remotion, every animation is driven by frame numbers, and you will use Claude Code to generate a text animation component that fades in, bounces, and fades out.

In this step, get ready to:
  • Create a text animation component with fade-in and slide-up effects.
  • Add a spring-based bounce animation for extra polish.
  • Register and preview the component in Remotion Studio.
Create a fade-in and slide-up text animation

Remotion maps frame numbers to CSS values. A 30fps video has 30 frames per second, and functions like interpolate() translate the current frame into values like opacity or position.

  • Switch to your Claude Code terminal and enter this prompt:
Create a new file src/TitleAnimation.tsx with a React component called TitleAnimation. It should accept a text string prop and display it as large centered white text (72px, bold, sans-serif font with a text shadow) inside an AbsoluteFill. Use useCurrentFrame() and interpolate() to fade in from opacity 0 to 1 over frames 0-20, and slide up from 40px to 0px over the same frame range. Use extrapolateRight: 'clamp' on both interpolations.

What does this prompt do?

Claude Code reads your prompt along with the Remotion Agent Skills installed in your project. It uses these skills to generate a properly structured React component with the animation logic built in.

Claude Code will ask for permission to create the file. Accept the prompt and let it finish writing the component.

  • After Claude Code finishes, open src/TitleAnimation.tsx in your editor.
  • Look for these two key animation lines inside the component:
// Fade in from invisible to fully visible over the first 20 frames
const opacity = interpolate(frame, [0, 20], [0, 1], {
  extrapolateRight: 'clamp',
});

// Slide up from 40px below to the final position over 20 frames
const slideUp = interpolate(frame, [0, 20], [40, 0], {
  extrapolateRight: 'clamp',
});

How does interpolate() work?

The interpolate() function takes the current frame number and maps it to an output value. It is a translator between time and visual properties.

  • interpolate(frame, [0, 20], [0, 1]) maps frame 0 to opacity 0 and frame 20 to opacity 1. Frames in between get proportional values.
  • [40, 0] in the slideUp means the text starts 40 pixels below its final position and moves up to 0.
  • extrapolateRight: 'clamp' prevents the value from going beyond 1 after frame 20. Without it, the value would keep increasing.

Experiment with animation values

Try changing [0, 20] to [0, 40] to make the animation slower. Change [40, 0] to [100, 0] for a bigger slide distance. Remember to revert your changes before moving to the next substep.

Add a spring-based bounce animation

Linear animations from interpolate() move at a constant rate. Spring animations from spring() simulate real physics, so the text overshoots its target and bounces back for a more natural feel.

  • In your Claude Code terminal, enter this prompt to enhance the component:
Update src/TitleAnimation.tsx to add two more animations. First, a spring-based scale animation using spring() that starts at frame 10 with config damping: 12 and stiffness: 200. Apply it as a CSS transform scale. Second, an exit fade using interpolate() that goes from opacity 1 to 0 between frames 80-100 with both extrapolateLeft and extrapolateRight clamped. Multiply the entry opacity with the exit opacity for the final opacity value.

What does this prompt do?

Claude Code updates the existing file to add a spring bounce effect and an exit fade. The component now has three phases: enter, sustain, and exit.

Accept the permission prompt when Claude Code asks to edit the file.

  • Check src/TitleAnimation.tsx for these new animation lines:
// Spring physics creates a natural bounce as the text scales in
const scale = spring({
  frame: frame - 10,
  fps,
  config: {damping: 12, stiffness: 200},
});

// Fade out between frames 80-100 for a clean exit
const exitOpacity = interpolate(frame, [80, 100], [1, 0], {
  extrapolateLeft: 'clamp',
  extrapolateRight: 'clamp',
});

How does spring() differ from interpolate()?

  • spring() uses physics simulation. It automatically calculates values based on mass, damping, and stiffness. You do not pick exact start and end values like you do with interpolate().
  • frame: frame - 10 delays the spring by 10 frames, so the bounce starts just after the fade-in begins.
  • damping controls how quickly the bounce settles. Lower values create more bouncing.
  • stiffness controls how snappy the motion feels. Higher values create faster initial movement.

Fine-tune the bounce feel

Try damping: 8 for a bouncier effect or stiffness: 300 for a snappier start. Preview changes live in Remotion Studio. Revert to the original values when you are done experimenting.

Register the component and preview it

Before you can preview an animation, Remotion needs it registered as a Composition. A Composition defines the video's dimensions, frame rate, and duration.

  • In your Claude Code terminal, enter this prompt to update the root file:
Update src/Root.tsx to import TitleAnimation from ./TitleAnimation and register it as a Composition with id TitleAnimation, durationInFrames 120, fps 30, width 1920, height 1080, and defaultProps with text set to Hello World. Remove any existing sample compositions.

What do these Composition props mean?

  • durationInFrames: 120 at fps: 30 means the animation plays for 4 seconds (120 frames divided by 30 frames per second).
  • width: 1920 and height: 1080 set a standard 1080p video resolution.
  • defaultProps passes text: 'Hello World' to the component so it has something to display in the preview.
  • After Claude Code finishes, verify src/Root.tsx looks like this:
import {Composition} from 'remotion';
import {TitleAnimation} from './TitleAnimation';

export const RemotionRoot: React.FC = () => {
  return (
    <>
      <Composition
        id="TitleAnimation"
        component={TitleAnimation}
        durationInFrames={120}
        fps={30}
        width={1920}
        height={1080}
        defaultProps={{text: 'Hello World'}}
      />
    </>
  );
};
  • Switch to the browser tab running Remotion Studio at http://localhost:3000.
  • Click the Compositions panel in the left sidebar.
  • Click TitleAnimation to select it.
  • Press Space to play the animation, or drag the timeline scrubber to step through frame by frame.

You should see white text reading "Hello World" fade in, slide up, bounce with a spring effect, then fade out.

Don't see TitleAnimation in the sidebar?

  • Make sure the id in your <Composition> matches exactly. Composition IDs can only contain letters, numbers, and -.
  • Check that src/Root.tsx imports TitleAnimation from the correct path (./TitleAnimation).
  • Remotion Studio hot-reloads on save. If it does not update, try refreshing the browser.
  • Why isn't my TitleAnimation showing up in Remotion Studio?

Double check that your two files match the expected code.

✔️ Awesome, I've got everything!

Great. Make sure both files are saved before moving on.

ⓧ I'd like to double check the full code

Here is the complete src/TitleAnimation.tsx:

import React from 'react';
import {
  AbsoluteFill,
  interpolate,
  spring,
  useCurrentFrame,
  useVideoConfig,
} from 'remotion';

type TitleAnimationProps = {
  text: string;
};

export const TitleAnimation: React.FC<TitleAnimationProps> = ({text}) => {
  const frame = useCurrentFrame();
  const {fps} = useVideoConfig();

  const opacity = interpolate(frame, [0, 20], [0, 1], {
    extrapolateRight: 'clamp',
  });

  const slideUp = interpolate(frame, [0, 20], [40, 0], {
    extrapolateRight: 'clamp',
  });

  const scale = spring({
    frame: frame - 10,
    fps,
    config: {damping: 12, stiffness: 200},
  });

  const exitOpacity = interpolate(frame, [80, 100], [1, 0], {
    extrapolateLeft: 'clamp',
    extrapolateRight: 'clamp',
  });

  return (
    <AbsoluteFill
      style={{
        justifyContent: 'center',
        alignItems: 'center',
      }}
    >
      <div
        style={{
          color: 'white',
          fontSize: 72,
          fontWeight: 'bold',
          fontFamily: 'sans-serif',
          textShadow: '0 4px 20px rgba(0, 0, 0, 0.5)',
          opacity: opacity * exitOpacity,
          transform: `translateY(${slideUp}px) scale(${scale})`,
        }}
      >
        {text}
      </div>
    </AbsoluteFill>
  );
};

Here is the complete src/Root.tsx:

import {Composition} from 'remotion';
import {TitleAnimation} from './TitleAnimation';

export const RemotionRoot: React.FC = () => {
  return (
    <>
      <Composition
        id="TitleAnimation"
        component={TitleAnimation}
        durationInFrames={120}
        fps={30}
        width={1920}
        height={1080}
        defaultProps={{text: 'Hello World'}}
      />
    </>
  );
};

You have a working text animation with fade-in, slide-up, spring bounce, and exit effects. Next up, you will add color animations and timeline sequencing to control when different elements appear and disappear.

Add Color Animations and Sequencing

Your TitleAnimation component already fades in, bounces, and fades out. Those are all movement-based animations.

In this step, you will add a new dimension to your animations: color. You will also learn how to control when elements appear and disappear in your video timeline using Remotion's <Sequence> component.

In this step, get ready to:
  • Create a color-shifting banner component using interpolateColors() and Easing.bezier().
  • Add timed subtitle text with <Sequence> components for precise timeline control.
  • Register and preview the new composition in Remotion Studio.
Create a color-shifting background with Claude Code

So far, your animations have changed position and opacity. interpolateColors() is Remotion's tool for smoothly transitioning between colors over time. It works just like interpolate(), but instead of outputting numbers, it outputs color values.

  • Switch to your Claude Code terminal and type this prompt:
Create a new component called ColorBanner in src/ColorBanner.tsx. It should:
- Fill the screen with a background color that shifts from blue (#3b82f6) to purple (#8b5cf6) to pink (#ec4899) over the full duration using interpolateColors()
- Use Easing.bezier() with interpolate() to create a smooth eased progress value that drives the color transition, instead of using the raw frame number directly
- Display a large centered title that says "Color Animations" in white
- Also register it as a new Composition called "ColorBanner" in src/Root.tsx with 1920x1080 resolution, 30fps, and 120 frames duration
  • Review Claude Code's proposed changes, then approve them.

What does this code do?

interpolateColors() maps a numeric input (like the current frame) to a color output. You give it an input range like [0, 60, 120] and a matching array of colors like ['#3b82f6', '#8b5cf6', '#ec4899']. It returns an rgba string you can plug straight into a CSS property.

Easing.bezier() customizes how interpolate() progresses over time. Without it, progress is perfectly linear. With a bezier curve, the animation speeds up and slows down naturally, just like a CSS transition-timing-function.

  • Confirm that Claude Code created a new file called src/ColorBanner.tsx.
  • Open src/ColorBanner.tsx in your editor and look for these key patterns in the generated code:

What should I see in the generated code?

  • An import of interpolateColors, interpolate, and Easing from 'remotion'.
  • An interpolate() call with an easing: Easing.bezier(...) option. This creates a smooth progress value from 0 to 1.
  • An interpolateColors() call using that eased progress value (not the raw frame) with three colors in the output range.
  • Switch to your browser and open Remotion Studio at http://localhost:3000.
  • Select ColorBanner from the composition dropdown in the top-left corner.
  • Press the play button or drag the timeline scrubber. You should see the background smoothly shift from blue to purple to pink.

Don't see ColorBanner in the dropdown?

Check that src/Root.tsx contains a <Composition> with id="ColorBanner". Claude Code should have added this, but if not, you will add it yourself in the next substep.

Also verify that Remotion Studio is still running in your first terminal. If you see an error, try saving src/Root.tsx again to trigger a refresh.

Help me troubleshoot my ColorBanner composition not appearing in Remotion Studio

Add timed elements with Sequence

The color shift plays across the entire duration. But what if you want an element to appear only during part of the video? That is exactly what the <Sequence> component does. It controls when child elements mount and unmount based on frame numbers.

  • In your Claude Code terminal, type this prompt:
Update the ColorBanner component in src/ColorBanner.tsx to add a subtitle that says "Built with Remotion" below the title. Wrap the subtitle in a <Sequence from={30} durationInFrames={60}> so it only appears from frame 30 to frame 90. The subtitle should have a fade-in animation using interpolate() on the local frame from useCurrentFrame().
  • Review Claude Code's proposed changes, then approve them.

What does Sequence do?

<Sequence> takes two key props: from (the frame where children start appearing) and durationInFrames (how long they stay visible). Children are completely unmounted outside this window.

An important detail: useCurrentFrame() inside a <Sequence> returns a value relative to the sequence start. So at frame 30 on the main timeline, the subtitle's local frame is 0. This means animations inside a sequence always start from 0, which makes components easy to reuse.

  • Verify that src/Root.tsx includes both the TitleAnimation and ColorBanner compositions. Open the file and confirm it looks like this:
import {Composition} from 'remotion';
import {TitleAnimation} from './TitleAnimation';
import {ColorBanner} from './ColorBanner';

export const RemotionRoot: React.FC = () => {
  return (
    <>
      <Composition
        id="TitleAnimation"
        component={TitleAnimation}
        durationInFrames={120}
        fps={30}
        width={1920}
        height={1080}
        defaultProps={{text: 'Hello World'}}
      />
      <Composition
        id="ColorBanner"
        component={ColorBanner}
        durationInFrames={120}
        fps={30}
        width={1920}
        height={1080}
      />
    </>
  );
};

What does Root.tsx do?

Root.tsx is the registry for all your compositions. Each <Composition> defines a renderable video with its own dimensions, frame rate, and duration. Remotion Studio reads this file to populate the composition dropdown.

  • If Claude Code did not add the ColorBanner composition, copy the code above into src/Root.tsx and save the file.

✔️ Awesome, I've got everything!

Make sure you have saved both src/Root.tsx and src/ColorBanner.tsx.

ⓧ I'd like to double check the full code

Here is the exact src/Root.tsx you should have at this point. Copy it in if yours looks different.

import {Composition} from 'remotion';
import {TitleAnimation} from './TitleAnimation';
import {ColorBanner} from './ColorBanner';

export const RemotionRoot: React.FC = () => {
  return (
    <>
      <Composition
        id="TitleAnimation"
        component={TitleAnimation}
        durationInFrames={120}
        fps={30}
        width={1920}
        height={1080}
        defaultProps={{text: 'Hello World'}}
      />
      <Composition
        id="ColorBanner"
        component={ColorBanner}
        durationInFrames={120}
        fps={30}
        width={1920}
        height={1080}
      />
    </>
  );
};

Your src/ColorBanner.tsx was generated by Claude Code, so the exact code may differ. The key features to check for: it imports interpolateColors, Easing, and Sequence from 'remotion', uses Easing.bezier() inside an interpolate() call, and wraps the subtitle in <Sequence from={30} durationInFrames={60}>.

  • Switch back to Remotion Studio and select the ColorBanner composition from the dropdown.
  • Press play and watch the full timeline. You should see the background colors shifting smoothly across the entire duration.
  • Look for the subtitle text appearing around the 1-second mark (frame 30) and disappearing at the 3-second mark (frame 90).

Subtitle not appearing or disappearing?

Make sure the <Sequence> wrapping the subtitle has from={30} and durationInFrames={60}. If the subtitle stays on screen the whole time, the <Sequence> wrapper might be missing.

If the subtitle never appears, check that its text color contrasts with the background. White or light text should be visible against the blue-to-pink gradient.

Help me fix my Sequence timing in the ColorBanner component

You now have two compositions: a text animation with spring physics and a color-shifting banner with timed elements. Next up, you will build reusable overlay components that can be layered on top of a real video.

Build Reusable Overlay Components

You've got color transitions and timeline sequencing under your belt. Now it's time to build the overlay components that will actually sit on top of a video.

In this step, you'll use Claude Code to build two reusable components: a lower-third banner and an animated progress bar. Both combine the animation techniques you've already learned into polished, production-style overlays.

In this step, get ready to:
  • Build a lower-third banner component with spring-based enter and exit animations.
  • Build an animated progress bar component that fills across the screen over time.
Build the lower-third banner

A lower-third banner is the animated name/title bar you see on TV broadcasts and YouTube videos. It slides in from the edge of the screen, stays visible for a few seconds, then slides back out.

You'll prompt Claude Code to generate this component using spring() for both the enter and exit animations, and interpolateColors() for a gradual color shift in the banner background.

  • In your Claude Code terminal, prompt Claude Code to build the lower-third component by entering this:
Create a LowerThird component in src/LowerThird.tsx. It accepts name and title
string props (define a LowerThirdProps type). The banner should be positioned at
the bottom-left of the screen (bottom: 80px). Use spring() with damping 15 and
stiffness 100 for both enter and exit animations. The enter spring should last 30
frames and the exit spring should last 20 frames, with a delay so it triggers near
the end of the composition. Combine enter and exit by subtracting exitProgress from
enterProgress, then use that combined value to drive translateX (from -400 to 0) and
opacity (0 to 1) with interpolate(). Use interpolateColors() to shift the
background from rgba(20, 80, 200, 0.9) to rgba(120, 40, 180, 0.9) over the full
video duration. Display the name in bold white 32px text and the title in 20px
semi-transparent white text below. Wrap everything in an AbsoluteFill.

What does the generated code do?

Claude Code generates a component with two separate spring animations stored as enterProgress and exitProgress. The exit spring uses a delay so it only fires near the end of the composition.

The key technique here is subtracting exit from enter: enterProgress - exitProgress. This single combined value drives both translateX and opacity. When enter is active, the value rises toward 1 (slides in). When exit kicks in, it drops back toward 0 (slides out).

The interpolateColors() call gradually shifts the banner's background from blue to purple over the full duration. This is a subtle touch that makes the overlay feel alive instead of static.

  • Open src/LowerThird.tsx in your code editor to review the generated code.
  • Confirm you see the enterProgress and exitProgress spring definitions, the subtraction pattern feeding into interpolate(), and the interpolateColors() call for the background.

Claude Code generated different code?

Claude Code might produce slightly different variable names, styling values, or animation configs. That's OK as long as the core structure matches: two spring() calls, the subtraction pattern, interpolate() for motion, and interpolateColors() for the background.

If the output looks very different, use the double-check tab below to compare. You can also ask Claude Code to adjust specific parts by describing what you want changed.

Why doesn't my LowerThird code match the expected output?

✔️ Awesome, I've got everything!

Great. Make sure you've saved src/LowerThird.tsx before moving on.

ⓧ I'd like to double check the full code

import React from 'react';
import {
  AbsoluteFill,
  interpolate,
  interpolateColors,
  spring,
  useCurrentFrame,
  useVideoConfig,
} from 'remotion';

type LowerThirdProps = {
  name: string;
  title: string;
};

export const LowerThird: React.FC<LowerThirdProps> = ({name, title}) => {
  const frame = useCurrentFrame();
  const {fps, durationInFrames} = useVideoConfig();

  const enterProgress = spring({
    frame,
    fps,
    config: {damping: 15, stiffness: 100},
    durationInFrames: 30,
  });

  const exitProgress = spring({
    frame,
    fps,
    config: {damping: 15, stiffness: 100},
    durationInFrames: 20,
    delay: durationInFrames - 20,
  });

  const translateX = interpolate(
    enterProgress - exitProgress,
    [0, 1],
    [-400, 0],
  );

  const opacity = interpolate(
    enterProgress - exitProgress,
    [0, 1],
    [0, 1],
    {extrapolateLeft: 'clamp', extrapolateRight: 'clamp'},
  );

  const backgroundColor = interpolateColors(
    frame,
    [0, durationInFrames],
    ['rgba(20, 80, 200, 0.9)', 'rgba(120, 40, 180, 0.9)'],
  );

  return (
    <AbsoluteFill>
      <div
        style={{
          position: 'absolute',
          bottom: 80,
          left: 0,
          transform: `translateX(${translateX}px)`,
          opacity,
        }}
      >
        <div
          style={{
            backgroundColor,
            padding: '16px 32px',
            borderRadius: '0 8px 8px 0',
          }}
        >
          <div
            style={{
              color: 'white',
              fontSize: 32,
              fontWeight: 'bold',
              fontFamily: 'sans-serif',
            }}
          >
            {name}
          </div>
          <div
            style={{
              color: 'rgba(255, 255, 255, 0.8)',
              fontSize: 20,
              fontFamily: 'sans-serif',
              marginTop: 4,
            }}
          >
            {title}
          </div>
        </div>
      </div>
    </AbsoluteFill>
  );
};
Build an animated progress bar

A progress bar overlay shows the viewer how far through the video they are. It fills from left to right as the video plays, and a color shift keeps it visually interesting.

  • In your Claude Code terminal, prompt Claude Code to build the progress bar component by entering this:
Create a ProgressBar component in src/ProgressBar.tsx. It should render a thin
6px bar at the very bottom of the screen that fills from left to right. Calculate
progress as (frame / durationInFrames) * 100 using useCurrentFrame() and
useVideoConfig(). Use interpolateColors() with three color stops at frame 0,
durationInFrames / 2, and durationInFrames to shift from #3b82f6 (blue) to
#8b5cf6 (purple) to #ec4899 (pink). Add a full-width background track with
rgba(255, 255, 255, 0.2) behind the fill. Wrap everything in an AbsoluteFill.

What does the generated code do?

This component calculates progress as a percentage of how far through the video the current frame is. The formula (frame / durationInFrames) * 100 gives a value from 0 to 100, which becomes the bar's width.

The interpolateColors() call here uses three color stops instead of two. The bar starts blue, shifts to purple at the halfway point, then finishes pink. This multi-stop pattern gives you smooth gradients across more than two colors.

  • Open src/ProgressBar.tsx in your code editor to review the generated code.
  • Confirm you see the progress calculation, the three-stop interpolateColors() call, and the two nested div elements (background track and fill bar).

Claude Code generated different code?

The progress bar is simpler than the lower-third, so there's less room for variation. The key pieces to check are: the progress percentage calculation, the three-stop interpolateColors() call, and the bar positioned at the bottom with a height of 6 pixels.

If Claude Code used a different approach (like interpolate() instead of simple division for progress), you can ask it to simplify. Use the double-check tab below to compare.

Why doesn't my ProgressBar code match the expected output?

✔️ Awesome, I've got everything!

Great. Make sure you've saved src/ProgressBar.tsx before moving on.

ⓧ I'd like to double check the full code

import React from 'react';
import {
  AbsoluteFill,
  interpolateColors,
  useCurrentFrame,
  useVideoConfig,
} from 'remotion';

export const ProgressBar: React.FC = () => {
  const frame = useCurrentFrame();
  const {durationInFrames} = useVideoConfig();

  const progress = (frame / durationInFrames) * 100;

  const barColor = interpolateColors(
    frame,
    [0, durationInFrames / 2, durationInFrames],
    ['#3b82f6', '#8b5cf6', '#ec4899'],
  );

  return (
    <AbsoluteFill>
      <div
        style={{
          position: 'absolute',
          bottom: 0,
          left: 0,
          width: '100%',
          height: 6,
          backgroundColor: 'rgba(255, 255, 255, 0.2)',
        }}
      >
        <div
          style={{
            height: '100%',
            width: `${progress}%`,
            backgroundColor: barColor,
            transition: 'none',
          }}
        />
      </div>
    </AbsoluteFill>
  );
};

Why aren't we previewing these yet?

These components are designed to be layered on top of a video, not viewed on their own. You'll register them as part of a full video composition in the next step, which is when you'll preview everything together in Remotion Studio.

Your overlay components are ready. In the next step, you'll layer them on top of an actual video and see everything come together in Remotion Studio.

Layer Overlays onto a Video

You now have a toolkit of animated components: a title with spring physics, a lower-third banner that slides in and out, and a progress bar that fills over time. Each works great on its own, but the real power of Remotion is stacking them together as layers on top of a real video.

In this step, you'll compose all of your overlay animations on top of a background video file. Remotion treats every component as a layer, so building a full production looks a lot like stacking transparent sheets.

In this step, get ready to:
  • Add a sample video file to your project.
  • Create a VideoOverlay composition that layers all animations on top of the video.
  • Preview and fine-tune the full composition in Remotion Studio.
Add a sample video to your project

Before you can overlay animations, you need a background video to layer them on. Any short MP4 file will work here.

  • Find a short MP4 video file on your computer. This could be a screen recording, a clip from your phone, or a sample video you download from a free stock video site.
  • Rename the file to sample-video.mp4.
  • Move the renamed file into the public/ folder inside your Remotion project.
  • Confirm the file is in the right place by running:
ls public/

You should see sample-video.mp4 listed in the output.

Why the public/ folder?

Remotion's staticFile() function serves files from the public/ folder. Placing your video here makes it available to Remotion at render time without any extra bundler configuration.

Don't have a video handy?

Search for "free sample MP4 video" on sites like Pexels or Pixabay. Download any short clip (5-15 seconds works well), rename it to sample-video.mp4, and place it in public/.

Help me find a sample video for my Remotion project

Build the VideoOverlay composition

Now you'll bring all of your components together into one composition. The background video sits at the bottom, and each animation stacks on top as a transparent layer.

  • In your Claude Code terminal, ask Claude Code to create a new file called src/VideoOverlay.tsx. Tell it to build a composition that uses OffthreadVideo with staticFile('sample-video.mp4') as the background layer. Then stack the TitleAnimation (from frame 0 to 120), LowerThird (from frame 60 to 240), and ProgressBar (full duration) on top using AbsoluteFill and Sequence components.

Claude Code should generate a file that looks like this:

import React from 'react';
import {AbsoluteFill, OffthreadVideo, Sequence, staticFile} from 'remotion';
import {TitleAnimation} from './TitleAnimation';
import {LowerThird} from './LowerThird';
import {ProgressBar} from './ProgressBar';

export const VideoOverlay: React.FC = () => {
  return (
    <AbsoluteFill>
      {/* Background video layer - renders at the bottom of the stack */}
      <AbsoluteFill>
        <OffthreadVideo src={staticFile('sample-video.mp4')} />
      </AbsoluteFill>

      {/* Title appears at frame 0, lasts 120 frames (4 seconds at 30fps) */}
      <Sequence from={0} durationInFrames={120}>
        <TitleAnimation text="My Awesome Video" />
      </Sequence>

      {/* Lower third slides in at frame 60, lasts 180 frames (6 seconds) */}
      <Sequence from={60} durationInFrames={180}>
        <LowerThird name="Your Name" title="Video Creator" />
      </Sequence>

      {/* Progress bar runs for the entire video duration */}
      <AbsoluteFill>
        <ProgressBar />
      </AbsoluteFill>
    </AbsoluteFill>
  );
};

How does layering work in Remotion?

Remotion follows standard HTML stacking rules. Elements lower in the JSX tree render on top of elements higher up. Each <AbsoluteFill> creates a full-frame layer that fills the entire composition.

  • OffthreadVideo renders the background video by extracting frames with FFmpeg. This is the recommended way to embed existing videos in Remotion.
  • <Sequence> controls when a component appears and disappears. The from prop sets the start frame, and durationInFrames sets how long it stays visible.
  • The ProgressBar sits in a plain <AbsoluteFill> instead of a <Sequence> because it runs for the entire video duration.
  • Verify that Claude Code saved the file to src/VideoOverlay.tsx. Open it in your editor and confirm the imports, component structure, and layer order match the code above.

Claude Code generated different code?

Claude Code may produce slightly different variable names or styling. The key things to check are that OffthreadVideo is the first layer, each overlay uses <AbsoluteFill> or <Sequence>, and all three overlay components (TitleAnimation, LowerThird, ProgressBar) are imported and rendered.

If the structure is significantly different, replace the file contents with the code shown above.

Help me fix my VideoOverlay.tsx file

Next, you need to register this new composition so Remotion knows about it. The current src/Root.tsx file still points to the TitleAnimation and ColorBanner compositions from earlier steps.

  • Ask Claude Code to update src/Root.tsx to replace the existing compositions with a single VideoOverlay composition. Set it to 300 frames at 30 fps with a resolution of 1920x1080.

Your updated src/Root.tsx should look like this:

import {Composition} from 'remotion';
import {VideoOverlay} from './VideoOverlay';

export const RemotionRoot: React.FC = () => {
  return (
    <>
      {/* Register VideoOverlay as the main renderable composition */}
      <Composition
        id="VideoOverlay"
        component={VideoOverlay}
        durationInFrames={300}
        fps={30}
        width={1920}
        height={1080}
      />
    </>
  );
};

What changed in Root.tsx?

The old TitleAnimation and ColorBanner compositions are gone. The single VideoOverlay composition replaces them both.

The id prop ("VideoOverlay") is the name you'll use later when rendering from the command line. The durationInFrames of 300 at 30 fps gives you a 10-second video.

  • Save src/Root.tsx if Claude Code did not save it automatically.
  • Switch to your Remotion Studio tab at http://localhost:3000. You should see VideoOverlay listed as a composition in the left sidebar.

Don't see VideoOverlay in the sidebar?

Make sure both src/VideoOverlay.tsx and src/Root.tsx are saved. Remotion Studio hot-reloads, but it needs both files saved to pick up the new composition.

Check the browser console for errors. A common issue is a mismatched import path. The import in Root.tsx must be './VideoOverlay' (matching the filename exactly).

Help me troubleshoot my Remotion Studio not showing VideoOverlay

✔️ Awesome, I've got everything!

Great. Double check you've saved both files before moving on.

ⓧ I'd like to double check the full code

Here is the full src/VideoOverlay.tsx:

import React from 'react';
import {AbsoluteFill, OffthreadVideo, Sequence, staticFile} from 'remotion';
import {TitleAnimation} from './TitleAnimation';
import {LowerThird} from './LowerThird';
import {ProgressBar} from './ProgressBar';

export const VideoOverlay: React.FC = () => {
  return (
    <AbsoluteFill>
      <AbsoluteFill>
        <OffthreadVideo src={staticFile('sample-video.mp4')} />
      </AbsoluteFill>

      <Sequence from={0} durationInFrames={120}>
        <TitleAnimation text="My Awesome Video" />
      </Sequence>

      <Sequence from={60} durationInFrames={180}>
        <LowerThird name="Your Name" title="Video Creator" />
      </Sequence>

      <AbsoluteFill>
        <ProgressBar />
      </AbsoluteFill>
    </AbsoluteFill>
  );
};

And the full src/Root.tsx:

import {Composition} from 'remotion';
import {VideoOverlay} from './VideoOverlay';

export const RemotionRoot: React.FC = () => {
  return (
    <>
      <Composition
        id="VideoOverlay"
        component={VideoOverlay}
        durationInFrames={300}
        fps={30}
        width={1920}
        height={1080}
      />
    </>
  );
};
Preview and fine-tune with Claude Code

Your full composition is registered. Time to see all the layers working together and make adjustments.

  • In Remotion Studio, click on VideoOverlay in the left sidebar to select it.
  • Press the play button or hit Space to play the composition from the beginning.

You should see your sample video playing in the background with the title text fading in and bouncing, the lower-third banner sliding in from the left around the 2-second mark, and the progress bar filling along the bottom of the frame.

What is the timeline scrubber?

The horizontal bar at the bottom of Remotion Studio is the timeline scrubber. Drag it to jump to any frame in the composition. This is useful for checking that animations start and end at the right moments.

  • Scrub through the timeline to check each layer's timing. The title should appear at frame 0 and fade out around frame 100. The lower-third banner should slide in at frame 60.

If any timing, colors, or positioning feel off, use Claude Code to make adjustments. Here are some things you might want to tweak:

  • Ask Claude Code to change the title text from "My Awesome Video" to something personal.
  • Ask Claude Code to adjust the lower-third's name and title props in VideoOverlay.tsx to show your own name.
  • Ask Claude Code to shift the timing of the <Sequence> components if overlays overlap awkwardly with your specific video.

Frame math shortcut

At 30 fps, every 30 frames equals 1 second. So frame 60 is the 2-second mark, and frame 300 is the 10-second mark. Use this to quickly calculate when overlays should appear and disappear.

Seeing a black screen or error in Studio?

A black screen usually means the video file could not be loaded. Check that your file is named exactly sample-video.mp4 (case-sensitive) and sits directly inside the public/ folder.

If you see an import error, make sure all component file names match their imports. For example, import {LowerThird} from './LowerThird' requires a file named src/LowerThird.tsx.

Help me debug why my Remotion video preview is broken

Your video overlays are composed and looking good. Next up, you'll render this composition to a real MP4 file and upload it to Google Drive.

Render to MP4 and Upload to Google Drive

You have a fully composed video with animated overlays playing in Remotion Studio. The preview looks great, but it only lives inside your browser.

In this step, you will turn that preview into a real MP4 file using the Remotion CLI. Then you will set up the Google Drive API and write a Node.js script to upload your finished video straight to Google Drive.

In this step, get ready to:
  • Render your composed video to an MP4 file using the Remotion CLI.
  • Set up a Google Cloud project with Drive API credentials.
  • Upload the rendered video to Google Drive with a Node.js script.
Render the video to MP4

Remotion Studio is a live preview tool. To produce a video file you can share, you need to render it with the Remotion CLI.

  • Switch to the terminal where Remotion Studio is running.
  • Press Ctrl+C to stop the dev server.
  • Render your video to MP4 by running this command:
npx remotion render VideoOverlay out/video.mp4

What does this command do?

This tells Remotion to render the composition with the ID VideoOverlay (the one you registered in src/Root.tsx) and save the output as out/video.mp4.

Remotion uses the h264 codec by default, which produces a standard MP4 file. The render processes all 300 frames (10 seconds at 30fps) and composites every overlay onto the background video.

The render takes a minute or two depending on your machine. You will see a progress bar in the terminal.

  • Confirm the output file exists by running:
ls out/
  • You should see video.mp4 listed in the output.
  • Open the out/ folder in Finder (macOS) or File Explorer (Windows) and double-click video.mp4 to play it.

You should see the full 10-second video with your title animation, lower-third banner, and progress bar layered on top of the background video.

Render failed or video looks wrong?

  • If you see Error: No composition with the ID "VideoOverlay" found, check that the id prop in your <Composition> in src/Root.tsx is exactly VideoOverlay (case-sensitive).
  • If the video is missing overlays, make sure you saved all your component files before rendering.
  • If the background video is missing, confirm sample-video.mp4 is in the public/ folder and the filename matches your staticFile() call.
  • Help me debug my Remotion render
Set up Google Drive API access

To upload files to Google Drive programmatically, you need API credentials from Google Cloud Console. This involves creating a project, enabling the Drive API, and setting up OAuth 2.0 credentials.

Why OAuth 2.0 instead of a service account?

OAuth 2.0 with a Desktop app flow lets the uploaded file appear directly in your personal Google Drive. A service account would own the file instead, and it would not show up in your Drive unless you explicitly shared it.

  • Open Google Cloud Console in your browser by navigating to:
https://console.cloud.google.com
  • Sign in with your Google account.
  • Click the project dropdown at the top of the page.
  • Click New Project.
  • Enter a project name like Remotion Upload in the Project name field.
  • Click Create. Wait a few seconds for the project to be created.
  • Select your new project from the project dropdown if it does not switch automatically.

Now enable the Google Drive API for this project.

  • Open the navigation menu (the hamburger icon in the top left).
  • Navigate to APIs & Services, then Library.
  • Search for Google Drive API in the search bar.
  • Click on Google Drive API in the results.
  • Click Enable.

Next, configure the OAuth consent screen. This controls what users see when authorizing your app.

  • In the navigation menu, go to Google Auth platform, then Branding.
  • If you see a message that says "Google Auth platform not configured yet", click Get Started.
  • Under App Information, enter an app name like Remotion Uploader in the App name field.
  • Select your email address in the User support email dropdown.
  • Click Next.
  • Under Audience, select External.
  • Click Next.
  • Under Contact Information, enter your email address.
  • Click Next.
  • Under Finish, review the Google API Services User Data Policy.
  • Check the box for I agree to the Google API Services: User Data Policy.
  • Click Continue, then click Create.

Since you selected External, you need to add yourself as a test user.

  • Click Audience in the left sidebar.
  • Under Test users, click Add users.
  • Enter your Google email address.
  • Click Save.

Now create the OAuth 2.0 credentials that your upload script will use.

  • In the navigation menu, go to Google Auth platform, then Clients.
  • Click Create Client.
  • For Application type, select Desktop app.
  • Enter a name like Remotion Uploader in the Name field.
  • Click Create.
  • Save the downloaded JSON file as credentials.json.
  • Move credentials.json into the root of your Remotion project folder (the same folder that contains package.json).

What is credentials.json?

This file contains your OAuth 2.0 client ID and client secret. Your Node.js script uses it to start the authorization flow and get permission to upload files to your Google Drive.

Finally, install the Google API packages your upload script needs.

  • In your terminal (still in the project folder), install the dependencies by running:
npm install googleapis@171 @google-cloud/local-auth@3

What do these packages do?

  • googleapis is the official Google API client library for Node.js. It provides methods for interacting with Google Drive and hundreds of other Google APIs.
  • @google-cloud/local-auth handles the OAuth 2.0 flow locally. It opens your browser for authorization and returns the authenticated client back to your script.
  • Verify the install completed without errors by checking for a success message in your terminal output.

npm install failing?

  • Make sure you are running the command from inside your Remotion project folder (the one with package.json).
  • If you see permission errors, try running with sudo on macOS/Linux.
  • Help me fix my npm install for googleapis
Create and run the upload script

With credentials in place and the Google API packages installed, you can write a script that authenticates with Google and uploads your rendered video.

  • Create a new file called upload-to-drive.mjs in the root of your project folder (next to package.json).
  • Add the imports and configuration constants by copying this code into upload-to-drive.mjs:
import path from 'node:path';
import fs from 'node:fs';
import process from 'node:process';
import {authenticate} from '@google-cloud/local-auth';
import {google} from 'googleapis';

// Only request permission to manage files this app creates
const SCOPES = ['https://www.googleapis.com/auth/drive.file'];
// Path to the OAuth credentials downloaded from Google Cloud Console
const CREDENTIALS_PATH = path.join(process.cwd(), 'credentials.json');
// Path to the rendered video file
const VIDEO_PATH = path.join(process.cwd(), 'out', 'video.mp4');

What does this code do?

  • The node: prefix on path, fs, and process is the recommended way to import built-in Node.js modules.
  • SCOPES uses drive.file, a narrow scope that only grants access to files this app creates. Google recommends this over the broader drive scope.
  • CREDENTIALS_PATH points to the credentials.json file you downloaded from Google Cloud Console.
  • Add the upload function below the constants in upload-to-drive.mjs:
async function uploadVideo() {
  // Authenticate using OAuth 2.0 - opens a browser window for consent
  const auth = await authenticate({
    scopes: SCOPES,
    keyfilePath: CREDENTIALS_PATH,
  });

  // Create a Google Drive API client with the authenticated credentials
  const drive = google.drive({version: 'v3', auth});

  // Set the filename that will appear in Google Drive
  const fileMetadata = {
    name: 'remotion-overlay-video.mp4',
  };

  // Stream the video file for upload
  const media = {
    mimeType: 'video/mp4',
    body: fs.createReadStream(VIDEO_PATH),
  };

  console.log('Uploading video to Google Drive...');

  // Upload the file and request back the id, name, and webViewLink fields
  const file = await drive.files.create({
    requestBody: fileMetadata,
    media,
    fields: 'id, name, webViewLink',
  });

  console.log('Upload complete!');
  console.log('File name:', file.data.name);
  console.log('File ID:', file.data.id);
  console.log('View link:', file.data.webViewLink);
}

uploadVideo().catch(console.error);

What does this function do?

  • authenticate() opens your browser so you can sign in and grant the app permission to upload files to your Drive.
  • drive.files.create() streams the MP4 file to Google Drive. It uses fs.createReadStream() to send the file in chunks rather than loading the whole thing into memory.
  • The fields parameter tells Google to return the file ID, name, and a shareable view link after the upload completes.
  • Save upload-to-drive.mjs.

✔️ Awesome, I've got everything!

Make sure you have saved upload-to-drive.mjs before moving on.

ⓧ I'd like to double check the full code

import path from 'node:path';
import fs from 'node:fs';
import process from 'node:process';
import {authenticate} from '@google-cloud/local-auth';
import {google} from 'googleapis';

const SCOPES = ['https://www.googleapis.com/auth/drive.file'];
const CREDENTIALS_PATH = path.join(process.cwd(), 'credentials.json');
const VIDEO_PATH = path.join(process.cwd(), 'out', 'video.mp4');

async function uploadVideo() {
  const auth = await authenticate({
    scopes: SCOPES,
    keyfilePath: CREDENTIALS_PATH,
  });

  const drive = google.drive({version: 'v3', auth});

  const fileMetadata = {
    name: 'remotion-overlay-video.mp4',
  };

  const media = {
    mimeType: 'video/mp4',
    body: fs.createReadStream(VIDEO_PATH),
  };

  console.log('Uploading video to Google Drive...');

  const file = await drive.files.create({
    requestBody: fileMetadata,
    media,
    fields: 'id, name, webViewLink',
  });

  console.log('Upload complete!');
  console.log('File name:', file.data.name);
  console.log('File ID:', file.data.id);
  console.log('View link:', file.data.webViewLink);
}

uploadVideo().catch(console.error);

Time to run the script and upload your video.

  • Run the upload script with this command:
node upload-to-drive.mjs

What will happen next?

The script opens a browser window asking you to sign in with your Google account and grant permission. Since the app is in testing mode, you may see a "Google hasn't verified this app" warning.

Click Advanced, then click Go to Remotion Uploader (unsafe) to continue. This is safe because you created this app yourself.

  • In the browser window that opens, sign in with the same Google account you added as a test user.
  • If you see the "Google hasn't verified this app" screen, click Advanced, then click Go to Remotion Uploader (unsafe).
  • Click Continue or Allow to grant the app permission to manage files it creates in your Drive.

After you authorize, the script uploads the video. You should see output like this in your terminal:

Uploading video to Google Drive...
Upload complete!
File name: remotion-overlay-video.mp4
File ID: 1aBcD_eFgHiJkLmNoPqRsT
View link: https://drive.google.com/file/d/1aBcD_eFgHiJkLmNoPqRsT/view?usp=drivesdk
  • Open Google Drive in your browser at drive.google.com.
  • You should see remotion-overlay-video.mp4 in your recent files.

Upload script failing?

  • If you see Error: Cannot find module credentials.json, make sure credentials.json is in the same folder where you are running the command.
  • If you see an access_denied error, check that you added your Google email as a test user on the Audience page in Google Cloud Console.
  • If you see ENOENT: no such file or directory for out/video.mp4, go back and re-run the render command from the first substep.
  • Help me debug my Google Drive upload script

Secret mission

Add a Custom Animated Intro

Your video jumps straight into the content. What if it opened with a polished animated intro instead? Build a 3-second intro sequence with spring-animated text and a smooth crossfade into the main video.

Before you go

Clean Up Your Resources

This project runs entirely locally with free tools, so there are no ongoing costs. Decide whether to keep your resources running, pause them to come back later, or delete them entirely.

Resources you used:

  • Remotion project folder with all animation source files.
  • Rendered MP4 video in the out/ folder.
  • Google Cloud project with Drive API enabled and OAuth 2.0 credentials.
  • Sample video file in the public/ folder.
  • Node.js and globally installed packages (Claude Code).
  • Video uploaded to your Google Drive.

Keep everything running

No action needed. Choose this if you're still actively building or want to experiment further with Remotion animations.

  • Your Remotion project stays on your Desktop, ready to edit and re-render.
  • Your Google Cloud project and OAuth credentials remain active for future uploads.
  • The rendered video stays in your Google Drive.

Pause - I'll come back to this later

Free up local resources while keeping everything easy to resume later.

  • Stop the Remotion Studio dev server by pressing Ctrl+C in the terminal where it is running.
  • Close Claude Code by typing /exit in its terminal session.
  • Keep the project folder on your Desktop. You can restart anytime with npm run dev.
  • Your Google Cloud project costs nothing while idle, so leave it as is.

Delete - I don't want to use this again

Remove all project resources and start fresh.

  • Stop the Remotion Studio dev server by pressing Ctrl+C in the terminal.
  • Close Claude Code by typing /exit.
  • Delete the entire Remotion project folder from your Desktop.
  • Delete the uploaded video from your Google Drive if you no longer need it.
  • To remove the Google Cloud project, go to the Google Cloud Console, open IAM & Admin, select Manage Resources, select your project, and click Delete.
  • Node.js and Claude Code can stay installed if you use them for other projects.

Mission accomplished

That's a Wrap!

Nice work! You just built a professional animated video overlay system entirely with code and AI.

You've learned how to:

  • Built a Remotion project from scratch using Claude Code as your co-pilot, creating frame-based animations with interpolate(), spring(), and interpolateColors().
  • Layered animated overlays onto an existing video using <OffthreadVideo>, <AbsoluteFill>, and <Sequence> components, then rendered to MP4 with the Remotion CLI.
  • Uploaded your finished video to Google Drive using the Drive API with OAuth 2.0 authentication.
  • Secret Mission: Added a custom animated intro sequence that plays before the main video starts.

Ready to quiz yourself?