{"id":5091,"date":"2026-02-06T10:58:40","date_gmt":"2026-02-06T02:58:40","guid":{"rendered":"https:\/\/crepal.ai\/blog\/?p=5091"},"modified":"2026-02-06T10:58:43","modified_gmt":"2026-02-06T02:58:43","slug":"blog-how-to-create-tiktok-style-captions-remotion","status":"publish","type":"post","link":"https:\/\/crepal.ai\/blog\/aivideo\/blog-how-to-create-tiktok-style-captions-remotion\/","title":{"rendered":"How to Create TikTok-Style Captions in Remotion (SRT Import + Word Highlight)"},"content":{"rendered":"\n<p>Dora is here. I was scrolling through <strong>TikTok<\/strong> last week and kept seeing these animated captions \u2014 you know, the ones where each word pops up with a little bounce and highlight as it&#8217;s spoken. They&#8217;re everywhere now. Product reviews, tutorials, memes, all of it.<\/p>\n\n\n\n<figure class=\"wp-block-gallery has-nested-images columns-default is-cropped wp-block-gallery-1 is-layout-flex wp-block-gallery-is-layout-flex\">\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" width=\"949\" height=\"494\" data-id=\"5093\" data-src=\"https:\/\/crepal.ai\/blog\/wp-content\/uploads\/2026\/02\/image-17.png\" alt=\"\" class=\"wp-image-5093 lazyload\" data-srcset=\"https:\/\/crepal.ai\/blog\/wp-content\/uploads\/2026\/02\/image-17.png 949w, https:\/\/crepal.ai\/blog\/wp-content\/uploads\/2026\/02\/image-17-300x156.png 300w, https:\/\/crepal.ai\/blog\/wp-content\/uploads\/2026\/02\/image-17-768x400.png 768w, https:\/\/crepal.ai\/blog\/wp-content\/uploads\/2026\/02\/image-17-18x9.png 18w\" data-sizes=\"auto, (max-width: 949px) 100vw, 949px\" src=\"data:image\/svg+xml;base64,PHN2ZyB3aWR0aD0iMSIgaGVpZ2h0PSIxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjwvc3ZnPg==\" style=\"--smush-placeholder-width: 949px; --smush-placeholder-aspect-ratio: 949\/494;\" \/><\/figure>\n<\/figure>\n\n\n\n<p>So naturally, I wondered: could I build this in <strong><a href=\"https:\/\/www.remotion.dev\/\" target=\"_blank\" rel=\"noreferrer noopener nofollow\">Remotion<\/a><\/strong>?<\/p>\n\n\n\n<p>Turns out, yes. Remotion added native subtitle support in v4.0.216, and by February 2026, the workflow for TikTok-style word-by-word captions is pretty solid. I tested it on February 3-5, 2026, and got it working \u2014 SRT import, word-level timing, bounce animations, the whole thing.<\/p>\n\n\n\n<p>Here&#8217;s how to set it up without breaking your render, plus the exact recipe for that TikTok highlight effect.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"caption-inputs-that-won-t-break-srt-json\">Caption Inputs That Won&#8217;t Break (SRT\/JSON)<\/h2>\n\n\n\n<p>You need captions in a format Remotion can read. Remotion&#8217;s <a href=\"https:\/\/www.remotion.dev\/docs\/captions\/\" target=\"_blank\" rel=\"noreferrer noopener nofollow\">@remotion\/captions package<\/a> uses a standardized <code>Caption<\/code> type:<\/p>\n\n\n\n<figure class=\"wp-block-gallery has-nested-images columns-default is-cropped wp-block-gallery-2 is-layout-flex wp-block-gallery-is-layout-flex\">\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" width=\"1024\" height=\"368\" data-id=\"5094\" data-src=\"https:\/\/crepal.ai\/blog\/wp-content\/uploads\/2026\/02\/image-18-1024x368.png\" alt=\"\" class=\"wp-image-5094 lazyload\" data-srcset=\"https:\/\/crepal.ai\/blog\/wp-content\/uploads\/2026\/02\/image-18-1024x368.png 1024w, https:\/\/crepal.ai\/blog\/wp-content\/uploads\/2026\/02\/image-18-300x108.png 300w, https:\/\/crepal.ai\/blog\/wp-content\/uploads\/2026\/02\/image-18-768x276.png 768w, https:\/\/crepal.ai\/blog\/wp-content\/uploads\/2026\/02\/image-18-18x6.png 18w, https:\/\/crepal.ai\/blog\/wp-content\/uploads\/2026\/02\/image-18.png 1172w\" data-sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" src=\"data:image\/svg+xml;base64,PHN2ZyB3aWR0aD0iMSIgaGVpZ2h0PSIxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjwvc3ZnPg==\" style=\"--smush-placeholder-width: 1024px; --smush-placeholder-aspect-ratio: 1024\/368;\" \/><\/figure>\n<\/figure>\n\n\n\n<pre class=\"wp-block-code\"><code>{\n  text: \"Hello\",\n  startMs: 0,\n  endMs: 500,\n  timestampMs: 250,\n  confidence: 1\n}<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"option-1-srt-files-most-compatible\">Option 1: SRT Files (Most Compatible)<\/h3>\n\n\n\n<p>Use Remotion&#8217;s <code>parseSrt() function<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>import { parseSrt } from '@remotion\/captions';\nimport { staticFile } from 'remotion';\n\nconst srtContent = await fetch(staticFile('captions.srt')).then(r =&gt; r.text());\nconst { captions } = parseSrt({ input: srtContent });<\/code><\/pre>\n\n\n\n<p><strong>Critical:<\/strong> SRT file must be in <code>public\/<\/code> and referenced via <code>staticFile()<\/code>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"option-2-json-from-whisper\">Option 2: JSON from Whisper<\/h3>\n\n\n\n<p>For new transcriptions, use Whisper via <code>@remotion\/install-whisper-cpp<\/code>. Output is already in <code>Caption<\/code> format:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>import whisperOutput from '.\/whisper-output.json';\nconst captions = whisperOutput;<\/code><\/pre>\n\n\n\n<p>I tested both on February 3rd. SRT took 30 seconds. Whisper took 2-3 minutes but gave word-level timing automatically.<\/p>\n\n\n\n<p><strong>What doesn&#8217;t work:<\/strong> VTT, ASS\/SSA, or plain text files. Convert to SRT first.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"step-by-step-import-sync-style\">Step-by-Step: Import \u2192 Sync \u2192 Style<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"step-1-import-and-parse\">Step 1: Import and Parse<\/h3>\n\n\n\n<p>Put your SRT in <code>public\/captions.srt<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>import { parseSrt } from '@remotion\/captions';\nimport { staticFile } from 'remotion';\nimport { useEffect, useState } from 'react';\n\nconst &#091;captions, setCaptions] = useState(&#091;]);\n\nuseEffect(() =&gt; {\n  fetch(staticFile('captions.srt'))\n    .then(r =&gt; r.text())\n    .then(srtText =&gt; {\n      const { captions } = parseSrt({ input: srtText });\n      setCaptions(captions);\n    });\n}, &#091;]);<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"step-2-sync-with-timeline\">Step 2: Sync with Timeline<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>import { useCurrentFrame, useVideoConfig } from 'remotion';\n\nconst frame = useCurrentFrame();\nconst { fps } = useVideoConfig();\nconst timeMs = (frame \/ fps) * 1000;\n\nconst currentCaption = captions.find(\n  cap =&gt; timeMs &gt;= cap.startMs &amp;&amp; timeMs &lt; cap.endMs\n);<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"step-3-render-with-basic-styling\">Step 3: Render with Basic Styling<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;div style={{\n  position: 'absolute',\n  bottom: 100,\n  left: '50%',\n  transform: 'translateX(-50%)',\n  fontSize: 48,\n  fontWeight: 'bold',\n  color: 'white',\n  whiteSpace: 'pre', \/\/ Critical for word spacing\n}}&gt;\n  {currentCaption?.text || ''}\n&lt;\/div&gt;<\/code><\/pre>\n\n\n\n<p>The <code>whiteSpace: 'pre'<\/code> is critical \u2014 without it, spaces collapse and word timing breaks.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"prevent-subtitle-drift-fps-trimming-audio-offset\">Prevent Subtitle Drift (FPS, Trimming, Audio Offset)<\/h3>\n\n\n\n<figure class=\"wp-block-gallery has-nested-images columns-default is-cropped wp-block-gallery-3 is-layout-flex wp-block-gallery-is-layout-flex\">\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" width=\"1024\" height=\"627\" data-id=\"5095\" data-src=\"https:\/\/crepal.ai\/blog\/wp-content\/uploads\/2026\/02\/image-19-1024x627.png\" alt=\"\" class=\"wp-image-5095 lazyload\" data-srcset=\"https:\/\/crepal.ai\/blog\/wp-content\/uploads\/2026\/02\/image-19-1024x627.png 1024w, https:\/\/crepal.ai\/blog\/wp-content\/uploads\/2026\/02\/image-19-300x184.png 300w, https:\/\/crepal.ai\/blog\/wp-content\/uploads\/2026\/02\/image-19-768x470.png 768w, https:\/\/crepal.ai\/blog\/wp-content\/uploads\/2026\/02\/image-19-18x12.png 18w, https:\/\/crepal.ai\/blog\/wp-content\/uploads\/2026\/02\/image-19.png 1065w\" data-sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" src=\"data:image\/svg+xml;base64,PHN2ZyB3aWR0aD0iMSIgaGVpZ2h0PSIxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjwvc3ZnPg==\" style=\"--smush-placeholder-width: 1024px; --smush-placeholder-aspect-ratio: 1024\/627;\" \/><\/figure>\n<\/figure>\n\n\n\n<p>Subtitle drift = captions slowly desyncing from audio. Common causes:<\/p>\n\n\n\n<ol start=\"1\" class=\"wp-block-list\">\n<li><strong>FPS mismatch:<\/strong> SRT generated at 30fps but rendering at 25fps causes drift. Always match FPS:<\/li>\n<\/ol>\n\n\n\n<pre class=\"wp-block-code\"><code>const { fps } = useVideoConfig();\nconsole.log('FPS:', fps); \/\/ Match this to your SRT timing<\/code><\/pre>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li><strong>Video trimming:<\/strong> If you trimmed the start, offset caption times:<\/li>\n<\/ol>\n\n\n\n<pre class=\"wp-block-code\"><code>const offsetMs = 2000; \/\/ Trimmed 2 seconds\nconst adjusted = captions.map(cap =&gt; ({\n  ...cap,\n  startMs: cap.startMs - offsetMs,\n  endMs: cap.endMs - offsetMs,\n}));<\/code><\/pre>\n\n\n\n<ol start=\"3\" class=\"wp-block-list\">\n<li><strong>Audio resampling:<\/strong> Re-encoding audio can change duration slightly, causing long-term drift.<\/li>\n<\/ol>\n\n\n\n<p><strong>Test:<\/strong> Render a 30-second segment and check if captions at the end are still synced.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"tiktok-style-highlight-recipe-word-timing-emphasis-bounce\">TikTok-Style Highlight Recipe (Word Timing, Emphasis, Bounce)<\/h2>\n\n\n\n<p>Okay, here&#8217;s the fun part \u2014 making captions that actually look like TikTok.<\/p>\n\n\n\n<p>The key is Remotion&#8217;s <code>createTikTokStyleCaptions() function<\/code>, which breaks caption lines into &#8220;pages&#8221; with individual word timings.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"step-1-convert-captions-to-pages\">Step 1: Convert Captions to Pages<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>import { createTikTokStyleCaptions } from '@remotion\/captions';\n\nconst { pages } = createTikTokStyleCaptions({\n  captions,\n  combineTokensWithinMilliseconds: 1200,\n});<\/code><\/pre>\n\n\n\n<p>The <code>combineTokensWithinMilliseconds<\/code> parameter controls how many words appear per page:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>High value (1200-2000ms):<\/strong> Multiple words per page (good for longer sentences)<\/li>\n\n\n\n<li><strong>Low value (200-500ms):<\/strong> Word-by-word animation (classic TikTok style)<\/li>\n<\/ul>\n\n\n\n<p>I tested both. For fast-paced content (like product demos), 500ms worked best. For educational content, 1200ms felt more natural.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"step-2-find-the-current-page\">Step 2: Find the Current Page<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>const currentPage = pages.find(\n  page =&gt; timeMs &gt;= page.startMs &amp;&amp; timeMs &lt; page.startMs + page.durationMs\n);<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"step-3-highlight-the-active-word\">Step 3: Highlight the Active Word<\/h3>\n\n\n\n<p>Each page has a <code>tokens<\/code> array with word-level timing. Loop through tokens and highlight the currently spoken word:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>return (\n  &lt;div style={{\n    position: 'absolute',\n    bottom: 100,\n    left: '50%',\n    transform: 'translateX(-50%)',\n    fontSize: 48,\n    fontWeight: 'bold',\n    textAlign: 'center',\n    whiteSpace: 'pre',\n  }}&gt;\n    {currentPage?.tokens.map((token, index) =&gt; {\n      const isActive = timeMs &gt;= token.fromMs &amp;&amp; timeMs &lt; token.toMs;\n      \n      return (\n        &lt;span\n          key={index}\n          style={{\n            color: isActive ? '#FFD700' : 'white',\n            backgroundColor: isActive ? 'rgba(0,0,0,0.8)' : 'transparent',\n            padding: isActive ? '4px 8px' : '0',\n            borderRadius: isActive ? '4px' : '0',\n            transition: 'all 0.1s ease',\n            textShadow: '2px 2px 4px rgba(0,0,0,0.8)',\n          }}\n        &gt;\n          {token.text}\n        &lt;\/span&gt;\n      );\n    })}\n  &lt;\/div&gt;\n);<\/code><\/pre>\n\n\n\n<p>This gives you the basic highlight effect \u2014 the active word turns gold with a dark background.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"step-4-add-bounce-animation\">Step 4: Add Bounce Animation<\/h3>\n\n\n\n<figure class=\"wp-block-gallery has-nested-images columns-default is-cropped wp-block-gallery-4 is-layout-flex wp-block-gallery-is-layout-flex\">\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" width=\"941\" height=\"514\" data-id=\"5096\" data-src=\"https:\/\/crepal.ai\/blog\/wp-content\/uploads\/2026\/02\/image-20.png\" alt=\"\" class=\"wp-image-5096 lazyload\" data-srcset=\"https:\/\/crepal.ai\/blog\/wp-content\/uploads\/2026\/02\/image-20.png 941w, https:\/\/crepal.ai\/blog\/wp-content\/uploads\/2026\/02\/image-20-300x164.png 300w, https:\/\/crepal.ai\/blog\/wp-content\/uploads\/2026\/02\/image-20-768x420.png 768w, https:\/\/crepal.ai\/blog\/wp-content\/uploads\/2026\/02\/image-20-18x10.png 18w\" data-sizes=\"auto, (max-width: 941px) 100vw, 941px\" src=\"data:image\/svg+xml;base64,PHN2ZyB3aWR0aD0iMSIgaGVpZ2h0PSIxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjwvc3ZnPg==\" style=\"--smush-placeholder-width: 941px; --smush-placeholder-aspect-ratio: 941\/514;\" \/><\/figure>\n<\/figure>\n\n\n\n<p>For the signature TikTok bounce, use Remotion&#8217;s <code>interpolate()<\/code> and <code>spring()<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>import { interpolate, spring } from 'remotion';\n\nconst isActive = timeMs &gt;= token.fromMs &amp;&amp; timeMs &lt; token.toMs;\n\n\/\/ Calculate frames since word started\nconst wordStartFrame = (token.fromMs \/ 1000) * fps;\nconst framesSinceStart = frame - wordStartFrame;\n\n\/\/ Spring animation for bounce\nconst bounce = spring({\n  frame: framesSinceStart,\n  fps,\n  config: {\n    damping: 10,\n    mass: 0.5,\n  },\n});\n\nconst scale = isActive ? bounce : 1;\n\nreturn (\n  &lt;span\n    style={{\n      transform: `scale(${scale})`,\n      display: 'inline-block',\n      color: isActive ? '#FFD700' : 'white',\n      \/\/ ... other styles\n    }}\n  &gt;\n    {token.text}\n  &lt;\/span&gt;\n);<\/code><\/pre>\n\n\n\n<p>The <code>spring()<\/code> animation creates a bouncy scale effect when the word becomes active. Adjust <code>damping<\/code> and <code>mass<\/code> to control the bounce intensity:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Lower damping (5-10) = bouncier<\/li>\n\n\n\n<li>Higher mass (0.5-1) = heavier, slower bounce<\/li>\n<\/ul>\n\n\n\n<p>I tested different spring configs on February 4th. A damping of 10 and mass of 0.5 felt most like native TikTok captions \u2014 snappy but not overly bouncy.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"step-5-optional-glow-shadow-effects\">Step 5: Optional Glow\/Shadow Effects<\/h3>\n\n\n\n<p>For extra emphasis, add a glow effect to active words:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>textShadow: isActive \n  ? '0 0 10px #FFD700, 0 0 20px #FFD700, 2px 2px 4px rgba(0,0,0,0.8)'\n  : '2px 2px 4px rgba(0,0,0,0.8)',<\/code><\/pre>\n\n\n\n<p>This creates a glowing halo around highlighted words.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"full-recipe-copy-paste\">Full Recipe (Copy-Paste)<\/h3>\n\n\n\n<p>Here&#8217;s the complete TikTok-style caption component:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>import { createTikTokStyleCaptions } from '@remotion\/captions';\nimport { useCurrentFrame, useVideoConfig, spring } from 'remotion';\n\nexport const TikTokCaptions = ({ captions }) =&gt; {\n  const frame = useCurrentFrame();\n  const { fps } = useVideoConfig();\n  const timeMs = (frame \/ fps) * 1000;\n\n  const { pages } = createTikTokStyleCaptions({\n    captions,\n    combineTokensWithinMilliseconds: 500, \/\/ Word-by-word\n  });\n\n  const currentPage = pages.find(\n    page =&gt; timeMs &gt;= page.startMs &amp;&amp; timeMs &lt; page.startMs + page.durationMs\n  );\n\n  return (\n    &lt;div style={{\n      position: 'absolute',\n      bottom: 100,\n      left: '50%',\n      transform: 'translateX(-50%)',\n      fontSize: 48,\n      fontWeight: 'bold',\n      textAlign: 'center',\n      whiteSpace: 'pre',\n      maxWidth: '80%',\n    }}&gt;\n      {currentPage?.tokens.map((token, i) =&gt; {\n        const isActive = timeMs &gt;= token.fromMs &amp;&amp; timeMs &lt; token.toMs;\n        const wordStartFrame = (token.fromMs \/ 1000) * fps;\n        const bounce = spring({\n          frame: frame - wordStartFrame,\n          fps,\n          config: { damping: 10, mass: 0.5 },\n        });\n\n        return (\n          &lt;span\n            key={i}\n            style={{\n              display: 'inline-block',\n              transform: `scale(${isActive ? bounce : 1})`,\n              color: isActive ? '#FFD700' : 'white',\n              backgroundColor: isActive ? 'rgba(0,0,0,0.8)' : 'transparent',\n              padding: isActive ? '4px 8px' : '0',\n              borderRadius: isActive ? '4px' : '0',\n              textShadow: isActive \n                ? '0 0 10px #FFD700, 2px 2px 4px rgba(0,0,0,0.8)'\n                : '2px 2px 4px rgba(0,0,0,0.8)',\n              margin: '0 2px',\n            }}\n          &gt;\n            {token.text}\n          &lt;\/span&gt;\n        );\n      })}\n    &lt;\/div&gt;\n  );\n};<\/code><\/pre>\n\n\n\n<p>This gives you the full TikTok effect: word-by-word highlighting, bounce animation, and glow.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"faq\">FAQ<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"can-i-use-youtube-auto-captions-for-word-level-timing\"><strong>Can I use YouTube auto-captions for word-level timing?<\/strong><\/h3>\n\n\n\n<p>No. YouTube SRT only has line-level timing. You need Whisper or similar for per-word timestamps.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"how-do-i-handle-multi-language-captions\"><strong>How do I handle multi-language captions?<\/strong><\/h3>\n\n\n\n<p>Use Whisper models without <code>.en<\/code> suffix (e.g., <code>medium<\/code> instead of <code>medium.en<\/code>). See the <a href=\"https:\/\/www.remotion.dev\/templates\/tiktok\" target=\"_blank\" rel=\"noreferrer noopener nofollow\">Remotion TikTok template<\/a> for multilingual config.<\/p>\n\n\n\n<figure class=\"wp-block-gallery has-nested-images columns-default is-cropped wp-block-gallery-5 is-layout-flex wp-block-gallery-is-layout-flex\">\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" width=\"1024\" height=\"568\" data-id=\"5097\" data-src=\"https:\/\/crepal.ai\/blog\/wp-content\/uploads\/2026\/02\/image-21-1024x568.png\" alt=\"\" class=\"wp-image-5097 lazyload\" data-srcset=\"https:\/\/crepal.ai\/blog\/wp-content\/uploads\/2026\/02\/image-21-1024x568.png 1024w, https:\/\/crepal.ai\/blog\/wp-content\/uploads\/2026\/02\/image-21-300x167.png 300w, https:\/\/crepal.ai\/blog\/wp-content\/uploads\/2026\/02\/image-21-768x426.png 768w, https:\/\/crepal.ai\/blog\/wp-content\/uploads\/2026\/02\/image-21-18x10.png 18w, https:\/\/crepal.ai\/blog\/wp-content\/uploads\/2026\/02\/image-21.png 1236w\" data-sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" src=\"data:image\/svg+xml;base64,PHN2ZyB3aWR0aD0iMSIgaGVpZ2h0PSIxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjwvc3ZnPg==\" style=\"--smush-placeholder-width: 1024px; --smush-placeholder-aspect-ratio: 1024\/568;\" \/><\/figure>\n<\/figure>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"why-do-captions-overflow-the-screen\"><strong>Why do captions overflow the screen?<\/strong><\/h3>\n\n\n\n<p>Adjust <code>combineTokensWithinMilliseconds<\/code> to split long lines, or set <code>maxWidth<\/code> on your container.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"can-i-customize-colors-animations\"><strong>Can I customize colors\/animations?<\/strong><\/h3>\n\n\n\n<p>Yes. Change <code>#FFD700<\/code> (gold) to any color. Adjust <code>spring()<\/code> config for different bounce styles.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\" \/>\n\n\n\n<p>The TikTok caption workflow is smoother than expected. Once you understand <code>Caption<\/code> \u2192 <code>createTikTokStyleCaptions()<\/code> \u2192 render, it&#8217;s just React and CSS.<\/p>\n\n\n\n<p>Biggest gotcha? FPS mismatch. Fix that first.<\/p>\n\n\n\n<p>For new projects, use the Remotion TikTok template \u2014 handles Whisper transcription and gives you the full setup.<\/p>\n\n\n\n<p>If you want to streamline this even further and reduce manual effort, try <strong>Crepal<\/strong>. Our tool automates many of these processes, saving you time and eliminating the common pitfalls.<\/p>\n\n\n\n<p><strong><a href=\"https:\/\/crepal.ai\/?utm_source=chatgpt.com\" target=\"_blank\" rel=\"noreferrer noopener nofollow\">Visit Crepal here now<\/a><\/strong>!<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\" \/>\n\n\n\n<p><strong>Previous posts:<\/strong><\/p>\n\n\n\n<figure class=\"wp-block-embed is-type-wp-embed is-provider-crepal-content-center wp-block-embed-crepal-content-center\"><div class=\"wp-block-embed__wrapper\">\n<blockquote class=\"wp-embedded-content\" data-secret=\"IVl7VDvcf8\"><a href=\"https:\/\/crepal.ai\/blog\/aivideo\/blog-how-to-fix-remotion-render-failed\/\">How to Fix \u201cRemotion Render Failed\u201d (FFmpeg\/FFprobe, Missing Assets, Decode Errors)<\/a><\/blockquote><iframe class=\"wp-embedded-content lazyload\" sandbox=\"allow-scripts\" security=\"restricted\" style=\"position: absolute; visibility: hidden;\" title=\"\u300a How to Fix \u201cRemotion Render Failed\u201d (FFmpeg\/FFprobe, Missing Assets, Decode Errors) \u300b\u2014CrePal Content Center\" data-src=\"https:\/\/crepal.ai\/blog\/aivideo\/blog-how-to-fix-remotion-render-failed\/embed\/#?secret=iEOjsLrZ0J#?secret=IVl7VDvcf8\" data-secret=\"IVl7VDvcf8\" width=\"600\" height=\"338\" frameborder=\"0\" marginwidth=\"0\" marginheight=\"0\" scrolling=\"no\" src=\"data:image\/svg+xml;base64,PHN2ZyB3aWR0aD0iMSIgaGVpZ2h0PSIxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjwvc3ZnPg==\" data-load-mode=\"1\"><\/iframe>\n<\/div><\/figure>\n\n\n\n<figure class=\"wp-block-embed is-type-wp-embed is-provider-crepal-content-center wp-block-embed-crepal-content-center\"><div class=\"wp-block-embed__wrapper\">\n<blockquote class=\"wp-embedded-content\" data-secret=\"WbHJkSRqWc\"><a href=\"https:\/\/crepal.ai\/blog\/aivideo\/blog-how-to-install-remotion-skills\/\">How to Install Remotion Agent Skills and Verify They Work (in 5 Minutes)<\/a><\/blockquote><iframe class=\"wp-embedded-content lazyload\" sandbox=\"allow-scripts\" security=\"restricted\" style=\"position: absolute; visibility: hidden;\" title=\"\u300a How to Install Remotion Agent Skills and Verify They Work (in 5 Minutes) \u300b\u2014CrePal Content Center\" data-src=\"https:\/\/crepal.ai\/blog\/aivideo\/blog-how-to-install-remotion-skills\/embed\/#?secret=mYvhALVYZA#?secret=WbHJkSRqWc\" data-secret=\"WbHJkSRqWc\" width=\"600\" height=\"338\" frameborder=\"0\" marginwidth=\"0\" marginheight=\"0\" scrolling=\"no\" src=\"data:image\/svg+xml;base64,PHN2ZyB3aWR0aD0iMSIgaGVpZ2h0PSIxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjwvc3ZnPg==\" data-load-mode=\"1\"><\/iframe>\n<\/div><\/figure>\n\n\n\n<figure class=\"wp-block-embed is-type-wp-embed is-provider-crepal-content-center wp-block-embed-crepal-content-center\"><div class=\"wp-block-embed__wrapper\">\n<blockquote class=\"wp-embedded-content\" data-secret=\"I9SzeVbMYS\"><a href=\"https:\/\/crepal.ai\/blog\/aivideo\/blog-ltx-2-full-vs-distilled-model\/\">LTX-2 Full vs Distilled Model: Which One Should You Download?<\/a><\/blockquote><iframe class=\"wp-embedded-content lazyload\" sandbox=\"allow-scripts\" security=\"restricted\" style=\"position: absolute; visibility: hidden;\" title=\"\u300a LTX-2 Full vs Distilled Model: Which One Should You Download? \u300b\u2014CrePal Content Center\" data-src=\"https:\/\/crepal.ai\/blog\/aivideo\/blog-ltx-2-full-vs-distilled-model\/embed\/#?secret=GCdinWyIbm#?secret=I9SzeVbMYS\" data-secret=\"I9SzeVbMYS\" width=\"600\" height=\"338\" frameborder=\"0\" marginwidth=\"0\" marginheight=\"0\" scrolling=\"no\" src=\"data:image\/svg+xml;base64,PHN2ZyB3aWR0aD0iMSIgaGVpZ2h0PSIxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjwvc3ZnPg==\" data-load-mode=\"1\"><\/iframe>\n<\/div><\/figure>\n","protected":false},"excerpt":{"rendered":"<p>Dora is here. I was scrolling through TikTok last week and kept seeing these animated captions \u2014 you know, the ones where each word pops up with a little bounce and highlight as it&#8217;s spoken. They&#8217;re everywhere now. Product reviews, tutorials, memes, all of it. So naturally, I wondered: could I build this in Remotion? [&hellip;]<\/p>\n","protected":false},"author":5,"featured_media":5092,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_gspb_post_css":"","_uag_custom_page_level_css":"","footnotes":""},"categories":[8],"tags":[],"class_list":["post-5091","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-aivideo"],"blocksy_meta":[],"uagb_featured_image_src":{"full":["https:\/\/crepal.ai\/blog\/wp-content\/uploads\/2026\/02\/image-16.png",1376,768,false],"thumbnail":["https:\/\/crepal.ai\/blog\/wp-content\/uploads\/2026\/02\/image-16-150x150.png",150,150,true],"medium":["https:\/\/crepal.ai\/blog\/wp-content\/uploads\/2026\/02\/image-16-300x167.png",300,167,true],"medium_large":["https:\/\/crepal.ai\/blog\/wp-content\/uploads\/2026\/02\/image-16-768x429.png",768,429,true],"large":["https:\/\/crepal.ai\/blog\/wp-content\/uploads\/2026\/02\/image-16-1024x572.png",1024,572,true],"1536x1536":["https:\/\/crepal.ai\/blog\/wp-content\/uploads\/2026\/02\/image-16.png",1376,768,false],"2048x2048":["https:\/\/crepal.ai\/blog\/wp-content\/uploads\/2026\/02\/image-16.png",1376,768,false],"trp-custom-language-flag":["https:\/\/crepal.ai\/blog\/wp-content\/uploads\/2026\/02\/image-16-18x10.png",18,10,true]},"uagb_author_info":{"display_name":"Dora","author_link":"https:\/\/crepal.ai\/blog\/author\/dora\/"},"uagb_comment_info":11,"uagb_excerpt":"Dora is here. I was scrolling through TikTok last week and kept seeing these animated captions \u2014 you know, the ones where each word pops up with a little bounce and highlight as it&#8217;s spoken. They&#8217;re everywhere now. Product reviews, tutorials, memes, all of it. So naturally, I wondered: could I build this in Remotion?&hellip;","_links":{"self":[{"href":"https:\/\/crepal.ai\/blog\/wp-json\/wp\/v2\/posts\/5091","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/crepal.ai\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/crepal.ai\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/crepal.ai\/blog\/wp-json\/wp\/v2\/users\/5"}],"replies":[{"embeddable":true,"href":"https:\/\/crepal.ai\/blog\/wp-json\/wp\/v2\/comments?post=5091"}],"version-history":[{"count":1,"href":"https:\/\/crepal.ai\/blog\/wp-json\/wp\/v2\/posts\/5091\/revisions"}],"predecessor-version":[{"id":5099,"href":"https:\/\/crepal.ai\/blog\/wp-json\/wp\/v2\/posts\/5091\/revisions\/5099"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/crepal.ai\/blog\/wp-json\/wp\/v2\/media\/5092"}],"wp:attachment":[{"href":"https:\/\/crepal.ai\/blog\/wp-json\/wp\/v2\/media?parent=5091"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/crepal.ai\/blog\/wp-json\/wp\/v2\/categories?post=5091"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/crepal.ai\/blog\/wp-json\/wp\/v2\/tags?post=5091"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}