Create professional walkthrough videos from app screenshots or live sites using Remotion. Produces smooth, polished MP4 videos with transitions, zoom effects, and text overlays.
Overview
This skill takes a set of screenshots (or captures them from a running app) and orchestrates them into a Remotion video composition with:
- Smooth transitions between screens (fade, slide, wipe)
- Zoom effects to highlight specific UI areas
- Text overlays with titles, descriptions, and callouts
- Progress indicators showing position in the walkthrough
- Optional voiceover narration track
Prerequisites
- Node.js 18+ installed
- Screenshots of the app (or a running app to screenshot)
- No Remotion experience needed — the skill generates all code
Workflow
Step 1: Gather Screenshots
Choose one approach:
Option A: From Existing Screenshots
If the user already has screenshots (e.g. from design-loop or product-showcase):
Read screenshots from:
- .design/screenshots/
- .jez/screenshots/
- User-specified directory
Sort them in walkthrough order (alphabetically by filename, or as user specifies).
Option B: Capture from Running App
If the app is running locally:
- Start Playwright CLI session
- Navigate through each screen in sequence
- Screenshot at consistent dimensions (1280x720 recommended for video)
- Save to
video/public/screens/
playwright-cli -s=walkthrough open http://localhost:3000
playwright-cli -s=walkthrough resize 1280 720
playwright-cli -s=walkthrough screenshot --filename=video/public/screens/01-home.png
# Navigate to next page...
playwright-cli -s=walkthrough screenshot --filename=video/public/screens/02-dashboard.png
Option C: From Live URL
Same as Option B but with a public URL. Screenshot each key page.
Step 2: Create Screen Manifest
Build a screens.json describing the walkthrough:
{
"projectName": "My App Walkthrough",
"fps": 30,
"width": 1280,
"height": 720,
"screens": [
{
"id": "home",
"title": "Welcome to MyApp",
"description": "The landing page introduces the core value proposition",
"imagePath": "screens/01-home.png",
"durationSeconds": 4,
"transition": "fade",
"zoomTarget": null
},
{
"id": "dashboard",
"title": "Your Dashboard",
"description": "See all your projects at a glance",
"imagePath": "screens/02-dashboard.png",
"durationSeconds": 5,
"transition": "slide-left",
"zoomTarget": { "x": 100, "y": 200, "width": 400, "height": 300, "delay": 2 }
}
]
}
| Field | Type | Description |
|---|---|---|
id |
string | Unique screen identifier |
title |
string | Text overlay title |
description |
string | Subtitle or narration text |
imagePath |
string | Path relative to video/public/ |
durationSeconds |
number | How long to show this screen |
transition |
string | fade, slide-left, slide-right, slide-up, wipe, none |
zoomTarget |
object/null | If set, zoom into this region after delay seconds |
Step 3: Scaffold Remotion Project
If no Remotion project exists:
mkdir -p video
cd video
npm init -y
npm install remotion @remotion/cli @remotion/transitions react react-dom
npm install -D typescript @types/react
Create the project structure:
video/
├── src/
│ ├── Root.tsx # Remotion entry point
│ ├── WalkthroughComposition.tsx # Main composition
│ ├── components/
│ │ ├── ScreenSlide.tsx # Individual screen display
│ │ ├── TextOverlay.tsx # Title/description overlay
│ │ ├── ProgressBar.tsx # Walkthrough progress indicator
│ │ └── ZoomEffect.tsx # Zoom into regions
│ └── config.ts # Load screens.json, calculate durations
├── public/
│ └── screens/ # Screenshot assets
│ ├── 01-home.png
│ └── 02-dashboard.png
├── screens.json # Screen manifest
├── remotion.config.ts
├── tsconfig.json
└── package.json
Step 4: Generate Remotion Components
Generate each component file. Key patterns:
Root.tsx
import { Composition } from "remotion";
import { WalkthroughComposition } from "./WalkthroughComposition";
import { screens, totalDurationInFrames, FPS, WIDTH, HEIGHT } from "./config";
export const RemotionRoot = () => (
<Composition
id="Walkthrough"
component={WalkthroughComposition}
durationInFrames={totalDurationInFrames}
fps={FPS}
width={WIDTH}
height={HEIGHT}
defaultProps={{ screens }}
/>
);
ScreenSlide.tsx Pattern
import { AbsoluteFill, Img, spring, useCurrentFrame, useVideoConfig } from "remotion";
interface ScreenSlideProps {
imageSrc: string;
title: string;
description: string;
}
export const ScreenSlide: React.FC<ScreenSlideProps> = ({ imageSrc, title, description }) => {
const frame = useCurrentFrame();
const { fps } = useVideoConfig();
// Fade in
const opacity = spring({ frame, fps, config: { damping: 20 } });
// Subtle zoom (Ken Burns effect)
const scale = 1 + frame * 0.0002;
return (
<AbsoluteFill style={{ backgroundColor: "#000" }}>
<Img
src={imageSrc}
style={{
width: "100%",
height: "100%",
objectFit: "contain",
opacity,
transform: `scale(${scale})`,
}}
/>
{/* Text overlay at bottom */}
<div style={{
position: "absolute",
bottom: 40,
left: 40,
right: 40,
opacity: spring({ frame: frame - 15, fps, config: { damping: 20 } }),
}}>
<h2 style={{ color: "#fff", fontSize: 32, fontWeight: 700, textShadow: "0 2px 8px rgba(0,0,0,0.8)" }}>
{title}
</h2>
<p style={{ color: "#ccc", fontSize: 18, textShadow: "0 1px 4px rgba(0,0,0,0.8)" }}>
{description}
</p>
</div>
</AbsoluteFill>
);
};
Transitions Between Screens
Use @remotion/transitions for transitions:
import { TransitionSeries } from "@remotion/transitions";
import { fade } from "@remotion/transitions/fade";
import { slide } from "@remotion/transitions/slide";
// In WalkthroughComposition:
<TransitionSeries>
{screens.map((screen, i) => (
<TransitionSeries.Sequence
key={screen.id}
durationInFrames={screen.durationSeconds * FPS}
>
<ScreenSlide {...screen} />
</TransitionSeries.Sequence>
// Add transition between screens (not after last)
{i < screens.length - 1 && (
<TransitionSeries.Transition
presentation={getTransition(screens[i + 1].transition)}
timing={springTiming({ config: { damping: 20 }, durationInFrames: 15 })}
/>
)}
))}
</TransitionSeries>
Step 5: Preview and Refine
cd video
npx remotion studio
This opens a browser-based preview. Check:
- Timing feels right for each screen
- Transitions are smooth
- Text overlays are readable
- Zoom targets hit the right area
- Progress bar (if included) is accurate
Step 6: Render the Video
cd video
npx remotion render Walkthrough output.mp4 --codec h264
For higher quality:
npx remotion render Walkthrough output.mp4 --codec h264 --quality 90
For web-optimised (smaller file):
npx remotion render Walkthrough output.webm --codec vp8
Advanced Features
Zoom to Region
Zoom into a specific area of the screen to highlight a feature:
// In ZoomEffect.tsx — interpolate scale and translate
const zoomScale = interpolate(frame, [delayFrames, delayFrames + 30], [1, 2.5], {
extrapolateRight: "clamp",
});
const translateX = interpolate(frame, [delayFrames, delayFrames + 30], [0, -targetX], {
extrapolateRight: "clamp",
});
Animated Callout Circles
Draw attention to UI elements:
// Pulsing circle that appears at a specific point
const scale = spring({ frame: frame - delay, fps, config: { damping: 8, stiffness: 80 } });
<div style={{
position: "absolute",
left: x - 20, top: y - 20,
width: 40, height: 40,
borderRadius: "50%",
border: "3px solid #3B82F6",
transform: `scale(${scale})`,
opacity: Math.min(1, scale),
}} />
Background Music
Add a subtle background track:
import { Audio } from "remotion";
<Audio src={staticFile("music/background.mp3")} volume={0.15} />
Intro and Outro Slides
Add title card at start and CTA at end:
// First sequence: Title card (3 seconds)
<TransitionSeries.Sequence durationInFrames={90}>
<TitleCard projectName="MyApp" tagline="The future of project management" />
</TransitionSeries.Sequence>
// ... screen sequences ...
// Last sequence: CTA card (4 seconds)
<TransitionSeries.Sequence durationInFrames={120}>
<CtaCard url="myapp.com" text="Try it free" />
</TransitionSeries.Sequence>
Transition Reference
| Name | Effect | Best for |
|---|---|---|
fade |
Cross-fade dissolve | Default, works everywhere |
slide-left |
New screen slides in from right | Sequential flow (next page) |
slide-right |
New screen slides in from left | Going back |
slide-up |
New screen slides in from bottom | Drill-down into detail |
wipe |
Wipe transition | Dramatic reveal |
none |
Hard cut | Quick comparison |
Output Options
| Format | Command | Use case |
|---|---|---|
| MP4 (H.264) | --codec h264 |
Universal compatibility |
| WebM (VP8) | --codec vp8 |
Web embedding, smaller files |
| GIF | --image-format png then ffmpeg |
Short loops, social media |
| PNG sequence | --image-format png --sequence |
Post-production editing |
Tips and Pitfalls
- 1280×720, 3-5 seconds per screen, under 90 seconds total — beyond these, attention drops
- Fade is the safest transition — pick 1-2 transition types and stay consistent
- Text overlays need contrast — text-shadow or semi-transparent background; too small or low-contrast reads as "AI made this"
- Ken Burns (subtle zoom) prevents static screenshots from feeling dead. Add intro/outro cards so the video doesn't feel abrupt
- Preview before rendering —
npx remotion studiosaves time vs full renders - Match screenshot dimensions (mismatched causes scaling issues). Use
staticFile()forpublic/assets