How to Make Faceless YouTube Videos with Remotion (Automated Pipeline)
How to Make Faceless YouTube Videos with Remotion (Automated Pipeline)
A faceless YouTube video is a video with no on-camera presenter: a written script becomes a voiceover, the voiceover drives on-screen captions, and b-roll footage plus images carry the visuals. With Remotion, every one of those pieces is code. That means the entire pipeline — script to finished MP4 — can be assembled once and re-run for every new video, with no camera and no dragging clips on a timeline.
The payoff is straightforward. Because the composition is a React component, you feed it text and audio as props, and Remotion renders the frames. You get a repeatable production line: write the script, generate the narration, transcribe it into captions, drop in visuals, sequence the scenes, and render. Change the script and re-render, and you have the next episode built on the exact same layout.
This guide walks the pipeline end to end and shows the Remotion APIs at each stage. It is a developer’s approach to a content workflow — calm, reproducible, and version-controlled — not a shortcut to views. Remotion handles production; distribution and performance are still up to your content.
What Is a Faceless YouTube Video?
A faceless (or “no-face”) video communicates entirely through narration, captions, and supporting visuals. Common formats include explainers, list videos, summaries, tutorials, and narrated stock-footage montages. The defining trait is that the creator never appears — so the whole video can be produced from text and assets rather than filmed.
That structure maps almost perfectly onto Remotion. Remotion is an open-source framework for building videos with React and TypeScript: each frame is a rendered React component, and a headless Chromium runtime captures those frames and encodes them into MP4. Since a faceless video is essentially “narration + timed captions + timed visuals,” and all three are just data and components, the format is a natural fit for a code-driven pipeline.
The Pipeline at a Glance
Here is the full flow, in order. Each step produces an artifact the next step consumes:
- Script — a plain-text (or JSON) script, split into scenes.
- Voiceover — text-to-speech turns each scene’s text into an audio file, played in the composition with the
<Audio>component from@remotion/media. - Captions — the voiceover audio is transcribed (Whisper) into timed caption data using
@remotion/captions. - Visuals — b-roll clips via
<Video>(@remotion/media) or<OffthreadVideo>, plus images via<Img>andstaticFile(). - Assembly — scenes are placed on the timeline with
<Sequence>. - Render — the finished video is produced locally with
@remotion/rendereror at scale with@remotion/lambda.
The sections below cover each stage with the relevant API. Everything is documented on remotion.dev.
Step 1 — Start With a Script
The script is the source of truth for the whole pipeline. Keep it as structured data so downstream steps can iterate over it. A simple shape is one entry per scene, each with an id and its narration text:
export const script = [
{ id: "intro", text: "Three quiet habits that change how you plan your week." },
{ id: "point-1", text: "First, write the outcome before you write the task." },
{ id: "point-2", text: "Second, protect one block of deep-focus time each day." },
{ id: "outro", text: "Small, repeatable systems beat bursts of motivation." },
];
Because this is code, the same array can generate the audio, seed the captions, and define scene boundaries. When you write your next video, you edit this array — the rest of the pipeline is unchanged.
Step 2 — Turn the Script Into Voiceover
Faceless videos live or die on their narration. A text-to-speech (TTS) service turns each scene’s text into an audio file. ElevenLabs is one option that produces natural-sounding speech; any TTS that returns an audio file works the same way in the pipeline. A small Node script loops over the script and writes one MP3 per scene into the public/ folder so Remotion can reach it via staticFile():
import { writeFileSync } from "fs";
import { script } from "./script";
const VOICE_ID = "your-voice-id";
for (const line of script) {
const res = await fetch(
`https://api.elevenlabs.io/v1/text-to-speech/${VOICE_ID}`,
{
method: "POST",
headers: {
"xi-api-key": process.env.ELEVENLABS_API_KEY!,
"Content-Type": "application/json",
Accept: "audio/mpeg",
},
body: JSON.stringify({
text: line.text,
model_id: "eleven_multilingual_v2",
}),
},
);
const audio = Buffer.from(await res.arrayBuffer());
writeFileSync(`public/voiceover/${line.id}.mp3`, audio);
}
Inside the composition, you play each file with the <Audio> component from @remotion/media, referencing it with staticFile():
import { Audio } from "@remotion/media";
import { staticFile } from "remotion";
export const Narration: React.FC<{ file: string }> = ({ file }) => {
return <Audio src={staticFile(file)} />;
};
Because each scene should last exactly as long as its narration, you can size the composition to the audio automatically. Remotion’s calculateMetadata function lets you measure the audio durations before render and set durationInFrames accordingly, so the timeline always matches the voiceover instead of being hard-coded. That keeps captions and visuals in sync without manual trimming. See the calculateMetadata docs for the full pattern.
Step 3 — Generate Captions Automatically
On-screen captions are close to mandatory for faceless content, since much of it is watched with the sound off. Instead of typing them by hand, transcribe the voiceover you just generated. Remotion provides @remotion/install-whisper-cpp, which runs Whisper locally and returns timed transcription data. A Node script installs Whisper, downloads a model, transcribes a clip, and converts the output to the standard Caption shape with toCaptions():
import {
installWhisperCpp,
downloadWhisperModel,
transcribe,
toCaptions,
} from "@remotion/install-whisper-cpp";
import { writeFileSync } from "fs";
import path from "path";
const whisperPath = path.join(process.cwd(), "whisper.cpp");
await installWhisperCpp({ to: whisperPath, version: "1.5.5" });
await downloadWhisperModel({ model: "medium.en", folder: whisperPath });
// Whisper expects a 16 kHz WAV. Convert the MP3 first with ffmpeg if needed:
// ffmpeg -i public/voiceover/intro.mp3 -ar 16000 public/voiceover/intro.wav -y
const output = await transcribe({
model: "medium.en",
whisperPath,
whisperCppVersion: "1.5.5",
inputPath: "public/voiceover/intro.wav",
tokenLevelTimestamps: true,
});
const { captions } = toCaptions({ whisperCppOutput: output });
writeFileSync("public/captions/intro.json", JSON.stringify(captions, null, 2));
Captions are ordinary JSON — a list of Caption objects with text, startMs, and endMs — so you can review and lightly edit them like any other data file. To display them, the @remotion/captions package includes helpers such as createTikTokStyleCaptions(), which groups words into readable pages you can render inside <Sequence> blocks and highlight word-by-word as they are spoken. The captions docs cover the display helpers in detail.
Step 4 — Add B-roll and Images
Visuals are where a faceless video stops being a slideshow. Remotion treats footage and images as components you place on the frame.
For video b-roll, use the <Video> component from @remotion/media (or <OffthreadVideo> from remotion), referencing local files with staticFile() or a remote URL. Add the muted prop so the clip’s own audio does not fight your narration:
import { AbsoluteFill, Img, interpolate, staticFile, useCurrentFrame } from "remotion";
import { Video } from "@remotion/media";
export const BrollScene: React.FC = () => {
const frame = useCurrentFrame();
// A slow push-in, recomputed on every frame.
const scale = interpolate(frame, [0, 90], [1, 1.08], {
extrapolateRight: "clamp",
});
return (
<AbsoluteFill style={{ backgroundColor: "#0B0B0F" }}>
<Video src={staticFile("broll/city.mp4")} muted style={{ scale }} />
<Img
src={staticFile("logo.png")}
style={{ position: "absolute", bottom: 60, right: 60, width: 120 }}
/>
</AbsoluteFill>
);
};
Two things to note. First, still images use <Img> with staticFile() — the same pattern as video. Second, the push-in above is animated with useCurrentFrame() and interpolate(), which recalculate the value for each frame. This is the important detail for anyone coming from CSS: CSS transitions and CSS animations do not render in Remotion. Motion must be driven by the frame number so the renderer can reproduce it deterministically on every frame. Use only b-roll and images you have the rights to use.
Step 5 — Assemble the Scenes
With audio, captions, and visuals defined, <Sequence> places each scene on the timeline. A sequence’s from prop sets when a scene starts, and durationInFrames sets how long it lasts. Walking the script and advancing an offset gives you back-to-back scenes:
import { AbsoluteFill, Sequence, staticFile } from "remotion";
import { Audio } from "@remotion/media";
import { BrollScene } from "./BrollScene";
type Scene = { id: string; durationInFrames: number };
export const FacelessVideo: React.FC<{ scenes: Scene[] }> = ({ scenes }) => {
let from = 0;
return (
<AbsoluteFill>
{scenes.map((scene) => {
const el = (
<Sequence key={scene.id} from={from} durationInFrames={scene.durationInFrames}>
<Audio src={staticFile(`voiceover/${scene.id}.mp3`)} />
<BrollScene />
{/* caption pages for this scene render here */}
</Sequence>
);
from += scene.durationInFrames;
return el;
})}
</AbsoluteFill>
);
};
Each scene carries its own narration, its b-roll, and its captions, and they play together because they share the same sequence. Your intro, lower thirds, and end card are just more sequences layered on top.
Step 6 — Render the Finished Video
Once the preview looks right in Remotion Studio (npx remotion studio), render to a file. For a single video, the CLI or the @remotion/renderer package runs locally:
npx remotion render FacelessVideo out/episode-01.mp4 --props=./episode-01.json
Passing data through --props (or inputProps) is what makes the pipeline repeatable: the same composition renders a different episode for each props file. To produce many videos, loop over your scripts and render one file per entry. When volume grows, @remotion/lambda distributes a render across AWS Lambda functions in parallel — useful for batches — while local rendering remains fine for a handful of videos at a time.
Why Code Makes Faceless Channels Repeatable
The advantage of doing this in Remotion is not any single feature — it is that the whole video is code. A faceless channel usually reuses one visual identity across every episode: the same intro, the same caption style, the same lower thirds, the same end card. When those live as React components accepting props, producing the next video is a matter of new inputs, not new design work. The layout is version-controlled, reviewable, and diff-able like any other part of your codebase.
This is also where a template library saves the most time. The pieces every faceless video needs — an intro, animated captions, lower thirds, and an end card — are exactly the kind of components that are tedious to build from scratch but easy to reuse once built. Starting from ready-made Remotion templates for those elements lets you spend your time on the script and the visuals rather than re-deriving entrance animations for the tenth time. That is the gap RenderComp fills.
Pipeline Summary Table
| Stage | Input | Remotion API / tool | Output |
|---|---|---|---|
| Script | Idea | Plain TS/JSON array | Scenes with text |
| Voiceover | Script text | TTS (e.g. ElevenLabs) → <Audio> from @remotion/media | MP3 per scene |
| Captions | Voiceover audio | @remotion/install-whisper-cpp → @remotion/captions | Timed caption JSON |
| Visuals | Assets you own | <Video> / <OffthreadVideo> (muted), <Img> + staticFile() | On-screen b-roll & images |
| Motion | Frame number | useCurrentFrame() + interpolate() | Frame-accurate animation |
| Assembly | All of the above | <Sequence> | Complete composition |
| Render | Composition + props | @remotion/renderer (local) or @remotion/lambda (scale) | Finished MP4 |
FAQ
Q: Do I need to appear on camera or record a webcam? No. The entire video is built from narration, captions, and visual assets. Nothing in the pipeline requires a camera.
Q: Which text-to-speech service should I use?
ElevenLabs is one well-known option that produces natural narration, and it returns a standard audio file. Any TTS that outputs an audio file fits the pipeline — the composition only needs a file to play with <Audio>.
Q: How does the video stay in sync with the voiceover?
Measure each scene’s audio duration and set the composition length from it using calculateMetadata. Because the timeline is derived from the audio, captions and visuals scale to match the narration instead of being hard-coded.
Q: Do the auto-generated captions have to be perfect?
Whisper transcription is a strong starting point, not a final proof. The captions are plain JSON (text, startMs, endMs), so you can open the file and correct wording or timing before rendering.
Q: Where do the b-roll clips and images come from?
From assets you have the rights to use — your own footage, licensed stock, or generated visuals. Reference local files with staticFile() or point <Video>/<Img> at a remote URL. Add muted to b-roll so its own audio does not overlap the narration.
Q: Can I batch-produce many videos from one composition?
Yes. Pass a different props object per video (via --props or inputProps) and loop over your scripts. Render locally with @remotion/renderer for small batches, or use @remotion/lambda to render many in parallel.
Q: Will this guarantee views or monetization? No. Remotion automates production — turning a script into a finished, consistent video. Whether a video performs or qualifies for monetization depends on your content and YouTube’s own policies, which this pipeline does not influence.
Build Faster with RenderComp
The faceless pipeline is repeatable by design, and the pieces you repeat most — intros, animated captions, lower thirds, and end cards — are the ones worth not rebuilding each time. RenderComp provides a library of production-ready Remotion templates for exactly those components, shipped as TypeScript source you customize directly and drive with inputProps from your render pipeline.
Browse the collection at rendercomp.com and start your faceless channel on a solid foundation instead of a blank canvas.
Now available
Get 1,000+ Remotion Templates
Pay once — no subscription. Lifetime updates. TypeScript-first.
View pricing →