snapcn
97 components
DocsComponentsTemplatesVideo EditorNewMCPNewRemotion StudioNewRoadmapChangelog
GitHub

Star snapcn

Free, open-source components.

Production-ready Remotion animations, transitions and backgrounds — you own the code, free or paid.

Star on GitHubXFollow

46 MIT · 51 Pro · own your code.

© 2026 snapcn

DocsComponentsTemplatesVideo EditorNewMCPNewRemotion StudioNewRoadmapChangelog

Components

Every component in snapcn, ready to install.
Updated 1h ago
AllText & TitlesCaptionsLogosScreens & DevicesCharts & StatsSocial ProofScenesAI Chat Input
Pro
Pro
Pro
Pro
Pro
Pro
Pro
Pro
Pro
Pro
Pro
Pro
Pro
Pro
Pro
Pro
Pro
Pro
Pro
Pro
Pro
Pro
Pro
Pro
Pro
Pro
Pro
Pro
Pro
Pro
Pro
Pro
Pro
Pro
Pro
Pro
Pro
Pro
Pro
Pro
Pro
Pro
Pro
Pro
Pro
Pro
Pro
Pro
Pro
Pro
Pro

Captions

Karaoke Captions

A caption line over any footage — the YouTube per-line black box by default, with karaoke fill, highlight-bar, and pill presets

Make a video
Type
Animation
Duration
5.0s
Category
Captions
Source
snapcn

Install

$ npx shadcn@latest add @snapcn/karaoke-captions

Installation

pnpm
$ pnpm dlx shadcn@latest add @snapcn/karaoke-captions

Needs an existing Remotion project and a components.json. Two-minute setup if this is your first snapcn component.

How it works

Component code

The exact file shadcn add copies into your project.

Usage

The whole line stays on screen while a color fill sweeps left-to-right across the words to indicate speech progress — muted #667085 ramps to ink #101828 in the light theme, and a translucent-white ramp resolves to #FAFAFA in the dark theme. Emphasized words fill in the accent color and scale up subtly. Everything is driven by the current frame, so scrubbing and rendering are fully deterministic.

// src/Root.tsx
import { Composition } from "remotion";
import { KaraokeCaptions } from "@/components/snap-cn/karaoke-captions";

const CaptionScene = () => (
  <KaraokeCaptions
    lines={[
      {
        text: "Acme reconciles every transaction automatically",
        startFrame: 10,
        endFrame: 120,
        emphasize: [4], // "automatically" in accent blue
      },
    ]}
    mode="light"
    aspect="landscape"
    pill
  />
);

export const RemotionRoot = () => (
  <Composition
    id="KaraokeCaptions"
    component={CaptionScene}
    durationInFrames={150}
    fps={30}
    width={1280}
    height={720}
  />
);

Without lines, the component builds a single demo line from text and emphasize. For word-accurate karaoke timing (e.g. from a transcription API), pass wordTimings — the absolute frame each word starts filling; a word's fill ends when the next word begins, and the last word runs to endFrame:

<KaraokeCaptions
  lines={[
    {
      text: "Acme reconciles every transaction automatically",
      startFrame: 10,
      endFrame: 120,
      wordTimings: [10, 28, 52, 68, 92],
      emphasize: [4],
    },
  ]}
/>

The component renders with a transparent canvas (plus the optional pill surface), so place it over any footage or backdrop. Unlike word-by-word caption styles, the full line is always visible — the fill is what indicates progress.

Safe-area presets

The aspect prop positions the caption block clear of platform UI:

  • landscape — 16:9 videos; sits 8% from the bottom with 10% side margins.
  • portrait — 9:16 (TikTok / Reels / Shorts); raised to 18% from the bottom to clear platform chrome, 7% side margins.
  • square — 1:1 feeds; 10% from the bottom, 8% side margins.

Using a real transcript

Nobody has frame numbers. Your transcript is in milliseconds — the shape Whisper, CapCut and Remotion's own @remotion/captions all speak:

<KaraokeCaptions
  captions={[
    { text: "Acme",        startMs: 1000, endMs: 1400 },
    { text: "reconciles",  startMs: 1400, endMs: 2000 },
    { text: "every",       startMs: 2000, endMs: 2300 },
    { text: "transaction", startMs: 2300, endMs: 3000 },
  ]}
  preset="karaoke"
/>

or paste an .srt straight in with srt={...}. Word-level output is collapsed into LINES (consecutive words become one line, a pause starts a new one) — which is exactly how a caption tool builds a line. Milliseconds are converted against useVideoConfig().fps, so one transcript is correct at any frame rate.

What actually makes a caption look premium

Three things, and the first one is most of it.

1. An OUTSIDE outline

A caption has to stay legible over footage it has never seen — a face, a sky, a white desk. A heavy black outline is the only thing that does that, and it has to sit outside the letterform.

-webkit-text-stroke centres the stroke on the glyph outline, so half of it eats inwards and the letters go thin and mushy. Measured on Montserrat 900 at 160px with a 14px stroke:

white stem
no stroke38px
-webkit-text-stroke alone22px — the stroke eats 42% of the letterform
+ paint-order: stroke fill38px — identical to no stroke
WebkitTextStrokeWidth: `${stroke}px`,
WebkitTextStrokeColor: "#000",
paintOrder: "stroke fill",   // draw the stroke, THEN the fill over it

That one line is most of the difference between a caption and a cheap caption.

2. Weight and size

Montserrat 800–900, at 11–13% of the frame's short side. Not Inter at 700, and not 2.8% of the height — that is a subtitle. Load the face through @remotion/google-fonts so the Player, the mp4 and your own project all get the same one.

3. The spoken word lands

A spring with overshoot, not an ease. And that scale is a scale on text, which is where captions usually fall apart: a browser gives glyph origins no vertical sub-pixel precision, so a scale that moves the baseline makes the word climb the pixel grid in whole-pixel jumps. The pop pivots on the measured baseline — verified on rendered frames, the baseline holds to 0px while the word scales 1.16×, so it grows upward off its baseline exactly like real caption type.

Props

PropTypeDefaultDescription
preset
"boxed" | "karaoke" | "highlight" | "clean""boxed"boxed (default): the YouTube auto-caption — white Roboto on a solid per-line black box, no outline. karaoke: heavy outlined line, each word snapping from dimmed to full white as it is spoken. highlight: the spoken word rides a rounded accent bar. clean: the quiet pill, no outline
strokeRatio
number—Outline width as a fraction of the font size. Defaults per preset. 0 turns it off
strokeColor
string"#000000"Outline colour
uppercase
boolean—Force upper case. Defaults per preset
lines
CaptionLine[]—Timed lines: { text, startFrame, endFrame, wordTimings?: number[], emphasize?: number[] }. Omit to build a single line from text
text
string"Acme reconciles every transaction automatically"Convenience single line used when lines is not provided
emphasize
string"automatically"Comma-separated words of text to emphasize (case-insensitive, punctuation ignored). Only used with the text fallback
mode
"light" | "dark""dark"Which end of the design system to resolve tokens against
theme
Partial<SnapCnTheme>—Design-system token overrides. Anything you leave out falls back to the shadcn defaults
accentColor
stringtheme.primaryFill color of emphasized words
fontSize
number48Font size in pixels
fontWeight
number600CSS font-weight
aspect
"landscape" | "portrait" | "square""landscape"Safe-area preset positioning the caption block
pill
booleantrueRounded surface behind the line with a hairline border
emphasisScale
number1.04Scale emphasized words grow to as their fill completes (ease-out, no bounce)
baseColor
string—Unfilled (not-yet-spoken) word color override
fillColor
string—Filled (spoken) word color override
speed
number1Global playback multiplier
className
string—Optional className passed to the outer wrapper

Frequently asked questions

How do I add Karaoke Captions to a Remotion project?

Run npx shadcn@latest add @snapcn/karaoke-captions. The source is copied into components/snap-cn/ (or wherever your components.json points) with anything it depends on, and you own the code from then on — there is no runtime package to keep installed.

What is Karaoke Captions?

Karaoke Captions is a Remotion karaoke captions for React. A caption line over any footage — the YouTube per-line black box by default, with karaoke fill, highlight-bar, and pill presets.

What do I need before using Karaoke Captions?

An existing Remotion project (npx create-video@latest) with a components.json. shadcn init does not recognise Remotion, so the installation guide has you write that file by hand — it takes two minutes. The component is MIT licensed and free.