Loading...
Loading...
Compare original and translation side by side
https://snapcn.dev/llms-components.txt?ref=skill-explainerhttps://snapcn.dev/docs/captions/word-captions.md?ref=skill-explainernpx skills add snapcndev/snapcn --skill snapcnhttps://snapcn.dev/llms-components.txt?ref=skill-explainerhttps://snapcn.dev/docs/captions/word-captions.md?ref=skill-explainernpx skills add snapcndev/snapcn --skill snapcn| Beat | What it says | snapcn scene | Frames |
|---|---|---|---|
| Hook | The problem, in the viewer's words | | 144 / 94 |
| How it works | 2–3 steps or states | | 198 / 180 / 108 |
| Show it | The product doing it | | 240 / 240 / 139 |
| Proof | One real number | | 47 |
| Close | The name | | 66 / 150 |
| 节拍 | 表达内容 | snapcn 场景 | 帧数 |
|---|---|---|---|
| 钩子 | 用观众自己的语言描述问题 | | 144 / 94 |
| 工作原理 | 2–3 个步骤或状态 | | 198 / 180 / 108 |
| 实际展示 | 产品执行该操作 | | 240 / 240 / 139 |
| 证据 | 一个真实数字 | | 47 |
| 收尾 | 名称 | | 66 / 150 |
public/voiceover.mp3{ text, startMs, endMs }[]@remotion/captions@remotion/install-whisper-cpppublic/voiceover.mp3{ text, startMs, endMs }[]@remotion/captions@remotion/install-whisper-cppword-captionscaptionspresetaspect="9:16"karaoke-captionsword-captionscaptionspresetaspect="9:16"karaoke-captionsnpx shadcn@latest add @snapcn/punch-lines @snapcn/status-cycle @snapcn/count-grid @snapcn/wordmark-cut @snapcn/word-captionspublic/voiceover.mp3import { AbsoluteFill, Audio, Series, staticFile } from "remotion";
import { CountGrid } from "@/components/snap-cn/count-grid";
import { PunchLines } from "@/components/snap-cn/punch-lines";
import { StatusCycle } from "@/components/snap-cn/status-cycle";
import { WordCaptions } from "@/components/snap-cn/word-captions";
import { WordmarkCut } from "@/components/snap-cn/wordmark-cut";
// Word-level timings from Whisper (or @remotion/captions): { text, startMs, endMs }[].
const captions = [
{ text: "Closing", startMs: 0, endMs: 400 },
{ text: "the", startMs: 400, endMs: 550 },
{ text: "books", startMs: 550, endMs: 1000 },
{ text: "takes", startMs: 1000, endMs: 1350 },
{ text: "a", startMs: 1350, endMs: 1450 },
{ text: "week.", startMs: 1450, endMs: 2100 },
];
// Real product screenshots in public/. Scenes fall back to snapcn sample images without them.
const shots = ["shot-1.jpg", "shot-2.jpg", "shot-3.jpg", "shot-4.jpg"].map((f) => staticFile(f));
// ~15s explainer: problem → how it works → result → name, narrated, captions burned in.
export const Explainer: React.FC = () => (
<AbsoluteFill>
<Series>
<Series.Sequence durationInFrames={144}>
<PunchLines script="Closing the books / takes a week. | Acme / does it overnight." />
</Series.Sequence>
<Series.Sequence durationInFrames={198}>
<StatusCycle
prefix="Every invoice gets"
statuses={["imported", "matched", "reconciled"]}
chips={["Stripe", "QuickBooks", "Xero"]}
/>
</Series.Sequence>
<Series.Sequence durationInFrames={60}>
<CountGrid from="5" to="500" noun="teams" cards={shots} />
</Series.Sequence>
<Series.Sequence durationInFrames={66}>
<WordmarkCut word="acme." />
</Series.Sequence>
</Series>
<WordCaptions captions={captions} />
<Audio src={staticFile("voiceover.mp3")} />
</AbsoluteFill>
);durationInFrames={468}fps={30}width={1280}height={720}npx shadcn@latest add @snapcn/punch-lines @snapcn/status-cycle @snapcn/count-grid @snapcn/wordmark-cut @snapcn/word-captionspublic/voiceover.mp3import { AbsoluteFill, Audio, Series, staticFile } from "remotion";
import { CountGrid } from "@/components/snap-cn/count-grid";
import { PunchLines } from "@/components/snap-cn/punch-lines";
import { StatusCycle } from "@/components/snap-cn/status-cycle";
import { WordCaptions } from "@/components/snap-cn/word-captions";
import { WordmarkCut } from "@/components/snap-cn/wordmark-cut";
// Word-level timings from Whisper (or @remotion/captions): { text, startMs, endMs }[].
const captions = [
{ text: "Closing", startMs: 0, endMs: 400 },
{ text: "the", startMs: 400, endMs: 550 },
{ text: "books", startMs: 550, endMs: 1000 },
{ text: "takes", startMs: 1000, endMs: 1350 },
{ text: "a", startMs: 1350, endMs: 1450 },
{ text: "week.", startMs: 1450, endMs: 2100 },
];
// Real product screenshots in public/. Scenes fall back to snapcn sample images without them.
const shots = ["shot-1.jpg", "shot-2.jpg", "shot-3.jpg", "shot-4.jpg"].map((f) => staticFile(f));
// ~15s explainer: problem → how it works → result → name, narrated, captions burned in.
export const Explainer: React.FC = () => (
<AbsoluteFill>
<Series>
<Series.Sequence durationInFrames={144}>
<PunchLines script="Closing the books / takes a week. | Acme / does it overnight." />
</Series.Sequence>
<Series.Sequence durationInFrames={198}>
<StatusCycle
prefix="Every invoice gets"
statuses={["imported", "matched", "reconciled"]}
chips={["Stripe", "QuickBooks", "Xero"]}
/>
</Series.Sequence>
<Series.Sequence durationInFrames={60}>
<CountGrid from="5" to="500" noun="teams" cards={shots} />
</Series.Sequence>
<Series.Sequence durationInFrames={66}>
<WordmarkCut word="acme." />
</Series.Sequence>
</Series>
<WordCaptions captions={captions} />
<Audio src={staticFile("voiceover.mp3")} />
</AbsoluteFill>
);durationInFrames={468}fps={30}width={1280}height={720}npx remotion still Explainer out/beat.png --frame=250
npx remotion render Explainer out/explainer.mp4 --codec=h264 --crf=18maxWordsnpx remotion still Explainer out/beat.png --frame=250
npx remotion render Explainer out/explainer.mp4 --codec=h264 --crf=18maxWordsorbit-flowproof-linehex-tallymetric-morphread-throughclaude mcp add snapcn -- npx -y @snapcn/mcp@latest SNAPCN_API_KEY=YOUR_KEYcomponents.jsonorbit-flowproof-linehex-tallymetric-morphread-throughclaude mcp add snapcn -- npx -y @snapcn/mcp@latest SNAPCN_API_KEY=YOUR_KEYcomponents.jsonTHIRD_PARTY_NOTICES.mdTHIRD_PARTY_NOTICES.md