Home / Articles / Blog

HyperFrames vs Remotion: Make Videos from HTML with Claude Code
HyperFramesRemotionClaude CodeVideoHTML to VideoPlaywrightffmpegOpen Source

HyperFrames vs Remotion: Make Videos from HTML with Claude Code

I turned one of my blog posts into a 45-second vertical short with HyperFrames, then rendered it again with plain Playwright and ffmpeg. Here is what the framework adds, how it compares with Remotion, and the Remotion licence rule people skip.

AIShrey Vijayvargiya

I already turn blog posts into Instagram carousels. An OpenRouter call on the server reads a list post and spits out slides, and I wrote about it in how I convert blog lists into carousels. Carousels are easy because they're just images.

Video is the next step, and the one I keep putting off. The idea in my head has been simple for a while: Claude reads a post, writes every scene as JSON, each scene is a small code template, and something joins the frames into an MP4. No timeline editor, no After Effects.

Then HyperFrames showed up. HeyGen open-sourced it with the line "Write HTML. Render video. Built for agents", and it picked up more than 21,000 GitHub stars in a month. That's almost exactly the engine I wanted to build, so I tested it on a real post. I took my system design chai stall post, turned it into a 45-second vertical short with HyperFrames, then rendered the same short again with plain Playwright and ffmpeg to see what the framework actually does for me. Remotion gets a fair look too, including the licence part people skip.

What HyperFrames is, in one paragraph

HyperFrames turns an HTML page into a video. You write normal HTML and CSS, mark which elements appear when with data-start and data-duration, and animate them with a paused GSAP timeline. The renderer opens the page in headless Chrome, jumps to each frame, takes a picture, and ffmpeg stitches the pictures into an MP4, so the same input gives the same output every time. It's Apache-2.0, needs Node 22 or newer plus ffmpeg, and ships as one npm package called hyperframes.

The "built for agents" part is real. hyperframes init drops a CLAUDE.md and an AGENTS.md into the project, and there's a Claude Code plugin:

bash
claude plugin marketplace add heygen-com/hyperframes
claude plugin install hyperframes@hyperframes

After that, /hyperframes inside Claude Code knows the rules of the format.

What I built

One repo, four files that matter:

FileJob
scenes.jsonThe script. One object per scene, with a type and a duration
build.mjsTurns scenes into one HyperFrames composition (short/index.html)
render-plain.mjsThe no-framework version: Playwright + ffmpeg
prompts/write-scenes.mdThe prompt Claude Code uses to write scenes.json from any post

Here's the whole flow:

Claude only writes the JSON. The templates, the build script and the renderer do the rest.

The repo is shreyvijayvargiya/blog-to-short. The stack is Node 22, GSAP, HyperFrames 0.8.223, Playwright and ffmpeg. There's no database, and you don't need an API key to render.

Step 1: Claude writes the scenes

I didn't want Claude writing raw HTML for every video. That's where things drift: a different font every time, text running off the screen, a scene that lasts 1.2 seconds. So Claude only writes data, and the design lives in templates.

The short has three scene types: a hook (5 seconds), up to six point scenes (6 seconds each) and an outro (4 seconds). That adds up to 45 seconds.

json
{
  "fps": 30,
  "width": 1080,
  "height": 1920,
  "scenes": [
    { "type": "hook", "duration": 5, "kicker": "system design",
      "title": "6 lines.", "accent": "One chai stall.",
      "sub": "A reel went viral. 400 people came." },
    { "type": "point", "duration": 6, "n": 1,
      "problem": "Slow reads", "fix": "Add a cache",
      "story": "The menu goes on a board. Nobody opens the notebook." },
    { "type": "outro", "duration": 4,
      "title": "Full post with diagrams", "accent": "ihatereading.in" }
  ]
}

The prompt in prompts/write-scenes.md sets hard limits: three words max for problem and fix so they fit on one line, 11 words max for story, durations that add up to 45, and "use the post's own analogy, don't add facts". Those limits came from the checks in step 3. I didn't guess them.

Step 2: scenes become one HTML file

build.mjs is about 110 lines. For each scene it picks a template, gives every element an id, and adds GSAP tweens at the scene's start time. This is the template and motion for a point scene:

js
point: (s, i) => `
  <p class="count" id="s${i}-c"><span class="y">${String(s.n).padStart(2, "0")}</span> / ${String(points).padStart(2, "0")}</p>
  <h2 class="problem" id="s${i}-p">${esc(s.problem)}</h2>
  <div class="arrow" id="s${i}-a"></div>
  <h2 class="fix" id="s${i}-f"><span class="hl" id="s${i}-h"></span><span class="fx">${esc(s.fix)}</span></h2>
  <p class="story" id="s${i}-s">${esc(s.story)}</p>`,
js
point: (i, t) => `
  tl.from("#s${i}-p", { x: -120, opacity: 0, duration: 0.5, ease: "power3.out" }, ${t + 0.25});
  tl.from("#s${i}-a", { scaleY: 0, transformOrigin: "top center", duration: 0.4 }, ${t + 0.9});
  tl.from("#s${i}-h", { scaleX: 0, transformOrigin: "left center", duration: 0.45 }, ${t + 1.3});
  tl.from("#s${i}-s", { opacity: 0, y: 30, duration: 0.6 }, ${t + 1.7});`,

Each scene ends up as a <section class="clip"> with data-start and data-duration, inside one root element with data-composition-id="main". The timeline gets registered on window.__timelines["main"], and that's the whole contract HyperFrames needs.

Two small choices saved me time later. Fonts and GSAP sit inside the project (short/assets/) instead of loading from a CDN, so a render never depends on the network. And the design is the same dark ihatereading look as my banners: near-black background, one yellow, Inter 900 headlines.

Step 3: check, look, then render

bash
npm run build     # scenes.json -> short/index.html
npm run check     # lint + runtime + layout + motion + contrast

My first version failed check with eight errors. I had used GSAP to hide and show each scene (autoAlpha), so the plain renderer would know which scene was on screen. HyperFrames manages clip visibility itself, and its linter caught the conflict straight away: "Do not animate these properties on clip elements." I removed those lines and the errors were gone.

The second catch was better. The checker warned that one story line was on screen for 3.86 seconds, under the reading time it wants for 16 words (about half a second plus 0.3 seconds a word). It was right. I cut every story to 11 words or fewer and brought it in a bit earlier, which is where the 11-word rule in the prompt comes from.

It still warns that the film has no sound and that eight scenes in one file should really be sub-compositions. Both are fair, and both are fine for a first version.

Before rendering, npx hyperframes snapshot --at 3,20,38,44 saves PNG frames and a contact sheet, so you can look at the result. That's how I found the outro URL running off both edges of the screen. Then:

bash
npm run render    # -> renders/hyperframes.mp4

On a 2-core cloud machine it took 109 seconds for 1,350 frames (45 seconds at 30fps). The file is 1080×1920, H.264, 2.0 MB.

The same short without HyperFrames

To see what I was really getting, I wrote the renderer myself. render-plain.mjs opens the same index.html in Playwright and loops over every frame:

js
for (let f = 0; f < frames; f++) {
  const t = f / fps;
  await page.evaluate((t) => {
    // HyperFrames does this part for you. Without it, it's your job.
    document.querySelectorAll(".clip").forEach((el) => {
      const s = Number(el.dataset.start), d = Number(el.dataset.duration);
      el.style.visibility = t >= s && t < s + d ? "visible" : "hidden";
    });
    window.__timelines.main.seek(t, false);
  }, t);
  const png = await page.screenshot({ type: "png" });
  ffmpeg.stdin.write(png);
}

It took 113 seconds and gave me a 1.2 MB file (different encoder settings, CRF 18). I pulled frames at 7.6 and 20 seconds from both videos and put them side by side. They match.

Same HTML, same frames. The difference is how much of the loop you write yourself.

For one fixed template like mine, 40 lines of Playwright gets you the same video at the same speed, so rendering alone doesn't justify the framework. What you pay for is everything around it:

  • the linter that caught my visibility bug and the reading-time problem
  • snapshots and contact sheets so an agent can look at its own work
  • a preview studio, a block registry, audio and caption tools
  • rendering on many workers, in Docker, on AWS Lambda or Google Cloud Run
  • skills that teach Claude the format, so it doesn't invent its own

If Claude is the one writing the video, those checks are the difference between "it rendered" and "it's watchable".

Where Remotion fits

Remotion is the older, bigger name here (about 69k stars). It has the same core idea, videos as code, but you write React components instead of HTML. You start with npx create-video@latest, and it renders on your server, on Lambda, on Vercel, or in the browser.

If you already think in React, Remotion feels natural. Your components, props and hooks all carry over, and years of docs, examples and community answers sit behind it.

Read the licence first. Remotion isn't MIT or Apache. From its LICENSE file (version 4.0.539, checked 31 Oct 2026), you can use it free, including commercially, if you are:

  • an individual
  • a for-profit company with up to 3 employees
  • a non-profit
  • evaluating it and not using it commercially yet

Bigger for-profit companies need a paid company licence from remotion.pro. I couldn't read the current prices off the pricing page, so check it yourself. The file also says the licence changes slightly in Remotion 5.0.

As a solo builder, Remotion is free for me. For a client with 20 people it isn't, and I'd rather know that before building their video pipeline on it. HyperFrames is Apache-2.0 with no team-size rule, which is part of why it's growing so fast. It even ships a /remotion-to-hyperframes skill for moving projects across.

HyperFrames vs Remotion vs Playwright + ffmpeg

HyperFramesRemotionPlaywright + ffmpeg
You writeHTML, CSS, GSAP (or Lottie, Three.js, CSS animation)React componentsAny HTML, plus the render loop
LicenceApache-2.0Free up to 3 employees, company licence aboveApache-2.0 (Playwright) + ffmpeg (LGPL/GPL builds)
Setupnpx hyperframes init, Node 22+, ffmpeg, Chromenpx create-video@latest, Nodenpm i playwright, ffmpeg
Agent supportClaude Code plugin, CLAUDE.md, lint and checksCommunity skills; you write the rulesNone, you write the prompt and the checks
Checks before renderlint, layout, contrast, reading time, snapshotsStudio previewWhatever you build
Cloud renderDocker, Lambda, Cloud Run, HeyGen cloudLambda, Vercel, server, browserDIY
My 45s short109 s, 2.0 MBNot tested113 s, 1.2 MB
Best forAgents writing videos, HTML peopleReact teams, long-lived video productsOne template, no extra dependencies

I didn't port the short to Remotion, so there's no Remotion number in my test. Everything else in the table comes from the repos and docs linked here.

A rough way to pick. Team size matters for Remotion because of its licence.

Watch someone else do it

Science Based AI's video shows the Claude Code + HyperFrames loop on three kinds of Reels: animated bar charts, explainers timed to a script, and glassy title cards. It shows what the skills can do once you stop limiting yourself to fixed templates like I did.

How I create viral animated reels using HyperFrames + Claude Code, by Science Based AI
How I create viral animated reels using HyperFrames + Claude Code, by Science Based AI

If you want the full process, Colin Blain's Claude + HyperFrames motion design walkthrough goes through script, voiceover, storyboard, animation and sound, with a prompt for each step.

What I'd add next

This is a first version, and it shows. These are the things I want to add:

  1. Sound. A music bed and a soft click on each highlight. HyperFrames' checker already complains about the silence.
  2. Voiceover. hyperframes doctor lists Kokoro as a local text-to-speech option. A voice reading the story lines would make it work with the sound on.
  3. Sub-compositions. One HTML file per scene type, which is what the linter suggests and what makes editing a single scene easy.
  4. A second layout. Code snippets and diagrams for posts like the databases one.
  5. Chat editing, like "make scene 3 slower, change the yellow". This is the one I care about most. The scenes JSON makes it cheap because Claude edits data, not a timeline.

I've written before about explaining LLM output with video and built a prompt-to-video app. This is the first time rendering felt like a solved problem. The writing, the pacing and the design still take the work.

Try it

bash
git clone https://github.com/shreyvijayvargiya/blog-to-short
cd blog-to-short && npm install
npx hyperframes browser ensure      # or set HYPERFRAMES_BROWSER_PATH to any Chromium
npm run build && npm run check && npm run render

Then open Claude Code in the repo, paste prompts/write-scenes.md with the URL of one of your own posts, and render again. If it breaks, the check output usually tells you why.

The repo sits next to my other open-source projects, which I listed in four open-source repos: Tubelog, Cizle, OpenNote and CRM SaaS.

FAQ

Is HyperFrames free?

Yes. It's Apache-2.0, so you can use it commercially at any company size. HeyGen also sells cloud rendering, but local rendering is free.

Is Remotion free for commercial use?

For individuals, non-profits and for-profit companies with up to 3 employees, yes. Larger for-profit companies need a company licence.

Does HyperFrames need React?

No. It's plain HTML and CSS with a GSAP timeline. Lottie, Three.js, Anime.js and CSS animations work too.

Can I convert HTML to video without a framework?

Yes. Open the page in Playwright, seek your animation frame by frame, screenshot each frame and pipe them into ffmpeg. My version is about 40 lines and gave me the same video.

Does Claude Code have a HyperFrames skill?

Yes. Install the plugin with claude plugin marketplace add heygen-com/hyperframes, then claude plugin install hyperframes@hyperframes, and use /hyperframes.

External Links (8)

Explore topics

More in

Weekly letter

Subscribe to iHateReading

Our once-a-week newsletter on programming, jobs, AI, and building products online.

Our once a week newsletter on Programming, Jobs, AI, and Business