A headless Ubuntu server with no GPU can turn an HTML file into a 1080p MP4 in about 14 seconds. On a box with 8 GB of RAM or less, though, HyperFrames drops to its slowest render path unless you tell it otherwise. Here you install HyperFrames on Ubuntu, get hyperframes doctor green, and render your first video over plain SSH, with the two flags that halved render time in testing.
You’ll set up Node.js, FFmpeg and the managed Chrome Headless Shell, render locally and in Docker, preview in Studio over an SSH tunnel, and add the Claude Code plugin. A timing table compares 2 and 4 cores, and the last section runs the same clip through Remotion. Built and run in October 2026 with HyperFrames 0.8.197 on an Ubuntu 26.04 VM with no GPU, then smoke-tested on Ubuntu 24.04, so every timing below is CPU-only.
Check What Renders Locally and What Costs Money
HyperFrames is an Apache 2.0 framework from HeyGen: you write a composition as an HTML page with timing attributes and a paused GSAP timeline, and the CLI seeks every frame in headless Chrome and pipes the frames into FFmpeg. Local rendering is free and needs no account. Nothing in this guide signs in to HeyGen, and every MP4 below came out of a box that never had HeyGen credentials.
The paid parts are optional and sit outside the CLI’s local render path. Know them before you build a pipeline around the tool:
hyperframes cloudrenders on HeyGen’s infrastructure instead of your own Chrome and FFmpeg.- The Claude.ai and ChatGPT connector builds and renders the video inside your HeyGen account, with no local project folder.
- HeyGen media such as avatars and generated clips uses HeyGen credits when a composition asks for them.
- Your coding agent (Claude Code, Codex, Cursor) bills its own plan when it writes compositions for you.
So the answer to “what does HyperFrames cost” is nothing for local CLI renders on your own hardware. The official quickstart describes the agent, credit and connector billing, and the CLI reference covers cloud renders. A render server only needs the four pieces below.
Install Node.js, FFmpeg and unzip
HyperFrames needs Node.js 22 or newer and FFmpeg. It also uses unzip to unpack its own Chrome download. Ubuntu 26.04 cloud images ship it, but the 24.04 template in the lab didn’t, and hyperframes doctor flagged it. The package source for Node is the only thing that differs between the two LTS releases:
| Item | Ubuntu 26.04 | Ubuntu 24.04 |
|---|---|---|
| Node.js from the Ubuntu archive | 22.22.1 (meets the 22+ floor) | 18.19.1 (too old) |
| Node.js source used | Ubuntu archive (nodejs + npm) | NodeSource 22.x (bundles npm) |
| FFmpeg from the archive | 8.0.1 | 6.1.1 |
| unzip preinstalled on the cloud image | Yes | No |
On Ubuntu 26.04 you can take everything from the default archive, because its Node 22 already meets the CLI’s floor. That saves you a third-party repository you would otherwise have to keep updated.
sudo apt update
sudo apt install -y nodejs npm ffmpeg unzip
Ubuntu 24.04’s own Node is 18, which the CLI refuses, so pull Node 22 from NodeSource first. The setup script adds the repository and its key, and the Node.js on Ubuntu guide covers the other install routes if you’d rather use nvm:
curl -fsSL https://deb.nodesource.com/setup_22.x -o nodesource_setup.sh
sudo -E bash nodesource_setup.sh
sudo apt install -y nodejs ffmpeg unzip
Confirm the versions before going further. Anything below v22 on the first line means the old Node is still first on your PATH:
node --version
npm --version
ffmpeg -version | head -1
The Ubuntu 26.04 box printed this, and the first line is the one the CLI checks:
v22.22.1
9.2.0
ffmpeg version 8.0.1-3ubuntu2 Copyright (c) 2000-2025 the FFmpeg developers
On 24.04 the same check returned v22.23.4, npm 10.9.9 and FFmpeg 6.1.1, and that older FFmpeg rendered the test clip without complaint.
Install the HyperFrames CLI with npm
The README runs everything through npx hyperframes, which works but re-resolves the package on each call. On a render server, a global install is cleaner. Point npm’s global prefix at your home directory first so npm install -g does not need sudo and does not write into /usr/local:
mkdir -p ~/.npm-global
npm config set prefix ~/.npm-global
echo 'export PATH="$HOME/.npm-global/bin:$PATH"' >> ~/.bashrc
source ~/.bashrc
Now install the CLI. The plain form takes the latest release, which is what most readers want, because HyperFrames ships several patch releases a week:
npm install -g hyperframes
To reproduce this guide exactly, pin the release that was tested instead. The comment links to the releases page so you can see what changed since:
npm install -g [email protected] #https://github.com/heygen-com/hyperframes/releases
The install pulled 79 packages in 11 seconds and takes 135 MB on disk. Check that the binary is on your PATH:
hyperframes --version
It prints the bare version number, which is what you compare against the releases page when you upgrade:
0.8.197
The first doctor or init run prints an anonymous telemetry notice. On a shared or CI server, turn it off once with hyperframes telemetry disable.
Download Chrome Headless Shell and Run hyperframes doctor
Run the health check before anything else, because it tells you exactly which piece is missing. On a fresh box with Node and FFmpeg in place, the output is green except for Chrome and Docker (trimmed here to the relevant lines):
hyperframes doctor
The ✗ lines are the ones to act on. The full output also lists optional checks (whisper-cpp, Kokoro TTS, MusicGen) that only matter for transcription and generated audio, and they are left out here:
✓ Version 0.8.197 (latest)
✓ Node.js v22.22.1 (linux x64)
✓ CPU 4 cores · Intel(R) Core(TM) i7-6700 CPU @ 3.40GHz
✓ Memory 7.8 GB total · 7.1 GB available
✓ /dev/shm 3968 MB
✓ FFmpeg ffmpeg 8.0.1-3ubuntu2 at /usr/bin/ffmpeg
✓ FFprobe ffprobe 8.0.1-3ubuntu2 at /usr/bin/ffprobe
✗ Chrome Chrome Headless Shell is required for local rendering.
Run: npx hyperframes browser ensure
✗ Docker Not found
https://docs.docker.com/get-docker/
doctor exited 0 even with failed checks, so don’t use its exit code as a CI gate. The JSON form has a top-level ok field, but on this box it read false even when fully render-ready, because the optional whisper, TTS and music checks fail. Gate on the checks a render needs instead:
hyperframes doctor --json | jq -e '[.checks[] | select(.name == "Node.js" or .name == "FFmpeg" or .name == "Chrome")] | length == 3 and all(.ok)'
You get true and exit code 0 once all three checks are present and pass, and exit code 1 otherwise, including when a future release renames one of them. Install jq with apt if your box lacks it. The fix for the Chrome line is the command doctor suggests. HyperFrames downloads its own pinned Chrome Headless Shell into ~/.cache/hyperframes rather than using whatever browser the system has:
hyperframes browser ensure
The download is 113.9 MB, took 12 seconds here, and ends with the path it will use:
Source: download
Path: /home/joshua/.cache/hyperframes/chrome/chrome-headless-shell/linux-152.0.7977.30/chrome-headless-shell-linux64/chrome-headless-shell
│
└ Ready to render.
Skipping this step doesn’t break anything, since the first render downloads the same browser on its own (the log says Browser: download). Running it up front keeps that delay out of your first timed render and out of CI jobs. The cache takes 261 MB.
Create a Project and Render Your First MP4 Headlessly
init scaffolds a project folder with an index.html composition. Pass --non-interactive on a server so it never waits for a prompt:
cd ~
hyperframes init my-video --non-interactive
It creates six files, then installs the four core agent skills into ~/.claude/skills and ~/.agents/skills (set HYPERFRAMES_SKIP_SKILLS=1 if a CI box doesn’t need them):
Created my-video/
AGENTS.md
CLAUDE.md
hyperframes.json
index.html
meta.json
package.json
The generated index.html is a grey 10-second card that says “Title” with an empty timeline. Replace it with something that moves, so the render actually exercises the animation path. Open the file:
cd ~/my-video
vim index.html
Replace its contents with this composition. It is the exact file rendered for every timing in this guide: a title that rises in, a subtitle that fades in at 1 second, and a bar that grows across 6 seconds. Check the same rules in your own files, because breaking any of them gives you a still frame rather than an error.
- Give every timed element
class="clip"plus its owndata-start,data-durationanddata-track-index. - Create the GSAP timeline with
paused: true, so HyperFrames drives it frame by frame. - Register the timeline on
window.__timelinesunder the same id asdata-composition-id(mainhere).
Paste the full file:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=1920, height=1080" />
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/gsap.min.js"></script>
<style>
:root {
--ground: #0f172a;
--ink: #f8fafc;
--accent: #38bdf8;
--font-display: sans-serif;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
html, body {
width: 1920px;
height: 1080px;
overflow: hidden;
background: var(--ground);
}
#root {
width: 100%;
height: 100%;
position: relative;
font-family: var(--font-display);
color: var(--ink);
}
#title {
position: absolute;
top: 380px;
width: 100%;
text-align: center;
font-size: 120px;
font-weight: 700;
letter-spacing: -0.03em;
}
#subtitle {
position: absolute;
top: 560px;
width: 100%;
text-align: center;
font-size: 48px;
color: var(--accent);
}
#bar {
position: absolute;
left: 360px;
top: 680px;
width: 1200px;
height: 12px;
background: var(--accent);
transform-origin: left center;
}
</style>
</head>
<body>
<div
id="root"
data-composition-id="main"
data-start="0"
data-duration="10"
data-width="1920"
data-height="1080"
>
<h1 id="title" class="clip" data-start="0" data-duration="10" data-track-index="0">Rendered on Linux</h1>
<p id="subtitle" class="clip" data-start="1" data-duration="9" data-track-index="1">HTML in, MP4 out, no display server</p>
<div id="bar" class="clip" data-start="2" data-duration="8" data-track-index="2"></div>
</div>
<script>
const tl = gsap.timeline({ paused: true });
tl.fromTo("#title", { opacity: 0, y: 60 }, { opacity: 1, y: 0, duration: 1, ease: "power3.out" }, 0);
tl.fromTo("#subtitle", { opacity: 0 }, { opacity: 1, duration: 0.8 }, 1);
tl.fromTo("#bar", { scaleX: 0 }, { scaleX: 1, duration: 6, ease: "none" }, 2);
tl.to("#title", { scale: 1.05, duration: 2, yoyo: true, repeat: 1 }, 5);
window.__timelines["main"] = tl;
tl.seek(0);
</script>
</body>
</html>
Lint it before rendering. Lint catches the mistakes that otherwise produce a still frame, such as a missing class="clip" or a timeline registered under the wrong id:
hyperframes lint
A clean composition returns no findings. On your own files, treat a missing_data_no_timeline warning seriously, because a composition with no registered timeline makes every render wait out a 45-second readiness timeout, according to the HyperFrames troubleshooting docs.
◆ Linting my-video/index.html
◇ 0 errors, 0 warnings
Render it. With no -o, you get a timestamped file in renders/, so name the output when you want to compare runs:
hyperframes render -o renders/first.mp4
On the 4-core, 7.8 GB VM the first render finished in 31 seconds, a cold run whose compile step (4.5 seconds) included the first download of fonts and GSAP into the cache. Repeat runs took 26.6 to 26.8 seconds. Every render still pulls GSAP from jsDelivr and an Inter font from Google Fonts unless they are cached, so warm those caches first on an air-gapped CI runner. You can also vendor them into the project. Read the second and third lines of the summary, because they say which capture path ran:
◇ /home/joshua/my-video/renders/first.mp4
637.8 KB · 10.0s video · rendered in 31.0s
screenshot capture · software gpu · compile 4.5s · extract 0.0s · audio 0.0s · probe 0.1s · setup 0.4s · capture 25.9s · encode (during capture) 25.9s · assemble 0.1s
Screenshot capture (slower): BeginFrame did not run. Needs chrome-headless-shell and no --resolution upscale. Heavy compositions can stall on software GL.
The MP4 is valid (H.264, 1920×1080, 30 fps, exactly 10.0 seconds), but “screenshot capture” with one browser on a 4-core box is the slow path, and the hint blames chrome-headless-shell even though that is exactly what was running. The real cause is in the next section.
Turn Off the Low-Memory Profile on 8 GB Servers
HyperFrames switches to a “low-memory” render profile when the machine has 8 GB of RAM or less, and that profile pins the render to one worker and forces screenshot capture. A VM sold as 8 GB reports slightly under that (7,936 MB here), so it always trips. The full render log states it plainly:
[INFO] [Render] Low-memory render profile active: screenshot capture, auto-worker calibration skipped, pinned to 1 worker. Override with --no-low-memory-mode or PRODUCER_LOW_MEMORY_MODE=false. {"renderJobId":"ce7c80c4-a074-463d-b31d-123388cd2ea1","totalMemMb":7936,"thresholdMb":8192}
The threshold is conservative for simple compositions. Sampling free during a 4-worker render, the whole box peaked at 1,823 MB used against 923 MB idle, so four Chrome workers cost under 1 GB on this clip. That leaves plenty of headroom on an 8 GB VM, and it means you can trust the core count rather than the RAM figure when you size a box for short motion graphics. Turn the profile off and set the worker count to your core count:
hyperframes render --no-low-memory-mode --workers 2 -o renders/fast.mp4
On the same VM cut to 2 cores, the render dropped from 26 seconds to 14.5 seconds, the capture path switched to BeginFrame, and the output size didn’t change:

Keep the profile on when the compositions are heavy (several videos, large images, 4K) or the box also runs other services, since each worker launches its own Chrome. To make the override stick for every render in a shell, export PRODUCER_LOW_MEMORY_MODE=false in ~/.bashrc instead of passing the flag each time.
Compare HyperFrames Render Times on 2 and 4 Cores
Every number below is the “rendered in” figure from the CLI summary for the same 300-frame, 1920×1080 composition on the same Proxmox VM (Intel i7-6700, host CPU type, 7.8 GB RAM, no GPU), with the core count changed between runs. Repeated runs landed within 0.6 seconds of each other:
| Render command | 2 cores | 4 cores | Capture path |
|---|---|---|---|
render (defaults, low-memory profile on) | 26.2 to 26.8 s | 26.6 to 26.8 s | screenshot, 1 worker |
render --no-low-memory-mode (auto workers) | 17.6 to 17.7 s | 16.4 s | BeginFrame |
render --no-low-memory-mode --workers 1 | not run | 15.4 s | BeginFrame |
render --no-low-memory-mode --workers 2 | 14.1 to 14.5 s | not run | BeginFrame |
render --no-low-memory-mode --workers 4 | 14.7 s | 9.6 s | BeginFrame |
PRODUCER_FORCE_SCREENSHOT=true, 2 workers | 20.6 s | not run | screenshot |
--quality draft, low-memory off | not run | 15.2 s | BeginFrame |
render --docker (warm image) | 30.6 to 30.8 s | not run | screenshot |
render --docker --workers 2 (also passed --no-low-memory-mode, which had no effect) | 23.3 s | not run | screenshot |
The table says more about the defaults than about the hardware, so read it in this order before you size a render box:
- Core count does nothing while the low-memory profile is on, because it pins one worker. 2 and 4 cores both sat at about 26.5 seconds.
- With the profile off, setting
--workersto the core count beat auto calibration on both sizes: 14.1 against 17.6 seconds on 2 cores, 9.6 against 16.4 on 4. - Asking for more workers than cores bought nothing (14.7 seconds with 4 workers on 2 cores).
- Draft quality saved one second and produced a larger file, 668.8 KB against 637.8 KB.
So if you only change one thing, pass --no-low-memory-mode --workers with your core count, because that alone took the 4-core box from 26.6 seconds to 9.6. Draft quality isn’t worth using for speed on short clips, since it barely moves the time and you lose quality for a bigger file.
The Ubuntu 24.04 VM, with only 2 cores and 3.8 GB of RAM, rendered the same file in 34.1 seconds on defaults and 14.6 seconds with --no-low-memory-mode --workers 2. Every local standard-quality render on the 26.04 VM produced a 637.8 KB file (637.9 KB on 24.04 with its older FFmpeg), and two default renders were byte-identical (same md5), which is what HyperFrames means by deterministic output.
Render Inside Docker with render –docker
--docker builds a renderer image with a pinned Chromium, FFmpeg and font set, then runs the render inside it, which is what you want when a CI job and a laptop must produce the same pixels. Install Docker from the Ubuntu archive and give your user access to the socket. The Docker CE install guide covers Docker’s own repository if you prefer it:
sudo apt install -y docker.io
sudo usermod -aG docker $USER
newgrp docker
Re-run hyperframes doctor afterwards and both the Docker and Docker running lines should turn green. Then render with the flag:
hyperframes render --docker -o renders/docker-first.mp4
The first run builds hyperframes-renderer from a node:22.23.3-bookworm-slim base, and that build is most of the wait:
30fps · standard · auto workers (2 cores detected)
Building Docker image: hyperframes-renderer:0.8.197 (linux/amd64)...
...
Successfully built 27fa437fd392
Successfully tagged hyperframes-renderer:0.8.197
638.2 KB · 10.0s video · rendered in 30.5s
screenshot capture · software gpu · compile 4.0s · extract 0.0s · audio 0.0s · probe 0.1s · setup 0.0s · capture 26.5s · encode (during capture) 26.5s · assemble 0.0s
638.2 KB · rendered in 5m 50.5s
Plan for that first-run cost on a CI runner. It took 5 minutes 50 seconds end to end, and the image uses 3.33 GB of disk (962 MB of content). Later renders reuse the image and take about 31 seconds on 2 cores, which is slower than a tuned local render because the container keeps the screenshot path. Bake the image into your runner cache, or pre-build it in a setup job, so you only pay the build once per HyperFrames release rather than on every pipeline run. Two Docker renders were byte-identical to each other, though not to the local renders (638.2 KB against 637.8 KB), because the container ships its own FFmpeg and Chrome.
One quirk in the tested release: --no-low-memory-mode did not reach the container, whose log still said the low-memory profile was active, and exporting PRODUCER_LOW_MEMORY_MODE=false on the host before render --docker didn’t change that either (30.1 seconds, one worker). The CLI reference notes that low-memory detection reads host RAM rather than container limits and advises setting PRODUCER_LOW_MEMORY_MODE explicitly for containerized renders, but a host-side export didn’t take effect through --docker in this release. What did pass through was --workers 2, which cut the Docker render to 23.3 seconds, so pass --workers explicitly when Docker renders feel slow.
Preview the Composition in Studio over SSH
hyperframes preview starts the Studio editor (pass --port to pick the port, 3002 here) and binds it to 127.0.0.1 only, so on a remote server you reach it through an SSH tunnel rather than by opening a firewall port. Start it in the background on the server without trying to open a browser:
cd ~/my-video
hyperframes preview --background --no-open --port 3002
Then, from your workstation, forward the port. Replace the user and address with your server’s:
ssh -N -L 3002:localhost:3002 [email protected]
Open http://localhost:3002 in your local browser. Studio shows the composition, a scrubber and one timeline track per clip, and HeyGen’s comparison guide says it edits the same DOM the renderer captures and saves changes back to source:

Stop the background server when you’re done with hyperframes preview --stop from the project folder. The Studio desktop app (.deb and AppImage) is a separate download for Linux workstations, signs in with a free HeyGen account, and is not needed on a render server.
Install HyperFrames Skills and the Claude Code Plugin
The agent path is the same CLI with instructions layered on top: skills teach Claude Code, Codex or Cursor the composition rules (paused timeline, class="clip", no wall-clock animation), and the agent then runs lint, preview and render itself. hyperframes init already installed the four core skills above. To install or refresh them in any folder without creating a project:
npx hyperframes skills update
For Claude Code, the versioned plugin bundles the full skill set. Auto-update is off by default for third-party marketplaces, so either enable it in Claude Code under /plugin, Marketplaces, hyperframes, Enable auto-update, or run claude plugin update hyperframes@hyperframes now and then. Install Claude Code first if the box doesn’t have it (the Claude Code on Linux guide walks through it), then add the marketplace and the plugin:
claude plugin marketplace add heygen-com/hyperframes
claude plugin install hyperframes@hyperframes
Both commands work without a Claude login. No API key is needed yet. The marketplace step also prints a warning that at least 84 files were checked out as Git LFS pointer files. Those are the repo’s regression-test videos, and the plugin installs fine without them:
✔ Successfully added marketplace: hyperframes (declared in user settings)
Installing plugin "hyperframes@hyperframes"...✔ Successfully installed plugin: hyperframes@hyperframes (scope: user)
claude plugin list then shows hyperframes@hyperframes enabled at version 0.8.198, one release ahead of the npm CLI at the time, because the plugin tracks the repository rather than npm. The lab box had no Claude login, so the agent loop itself is not shown here. Once you’re signed in, the loop HeyGen documents runs like this:
- Run
claudeinside~/my-video. - Invoke the entry skill as
/hyperframes:hyperframeswith a request such as “make a 10-second product intro for https://example.com”. - Let the agent lint and preview its edits, then render the result yourself with
--no-low-memory-mode --workers.
Run that last render yourself. An agent will accept the low-memory default, and you lose half your throughput. The Claude Code cheat sheet covers the slash commands and permission modes you’ll want for unattended runs, and MCP vs RAG vs agent skills explains how skills differ from an MCP connector like HeyGen’s.
Fix Common Headless Render Errors
These came up on the lab VMs, with the exact strings HyperFrames printed. Each one either still produced an MP4 or had a one-command fix, so none of them is a reason to give up on a headless server.
“Chrome Headless Shell is required for local rendering”
hyperframes doctor prints this when ~/.cache/hyperframes/chrome is empty, on a fresh install or after hyperframes browser clear. Run hyperframes browser ensure. If a download got interrupted, hyperframes browser ensure --force purges the partial copy and fetches it again.
“Screenshot capture (slower): BeginFrame did not run”
This hint appears under the render summary whenever the slow capture path ran on Linux. It names chrome-headless-shell, but in testing the usual cause was the low-memory profile, which forces screenshot capture on any machine with 8 GB or less even when the managed headless shell is in place. Add --no-low-memory-mode, and if the hint persists, check the next error.
“‘HeadlessExperimental.enable’ wasn’t found; falling back to screenshot mode”
This shows up in the render log when HYPERFRAMES_BROWSER_PATH points at a full Google Chrome install. Full Chrome lacks the HeadlessExperimental protocol that BeginFrame capture needs, so HyperFrames falls back to screenshots:
[BrowserManager] HeadlessExperimental.beginFrame probe failed after 124ms: Protocol error (HeadlessExperimental.enable): 'HeadlessExperimental.enable' wasn't found; falling back to screenshot mode.
With Google Chrome 156 forced this way and auto workers, the render still completed (no hang on this composition) but took 31.3 seconds with one worker, against 17.6 seconds for the managed headless shell on the same 2 cores, both runs with --no-low-memory-mode and auto workers. HyperFrames ignored the installed Chrome entirely until it was forced, so the fix is to unset the variable and let hyperframes browser ensure manage the browser.
“Archive extractor Not found”
doctor reports this on minimal images without unzip, with the hint “Install unzip so HyperFrames can extract its managed Chrome download.” On the Ubuntu 24.04 VM the browser download still worked, but install it anyway with sudo apt install -y unzip so the check passes and future Chrome updates extract cleanly.
HyperFrames vs Remotion on the Same Server
Both tools render video by driving headless Chrome and encoding with FFmpeg, so speed is close. The real differences are the authoring model and the licence. To compare like for like, the same 10-second clip (title, subtitle, growing bar, 1920×1080, 300 frames) was rebuilt as a React component in a blank Remotion 4.0.538 project and rendered on the same 2-core VM:
| HyperFrames | Remotion | |
|---|---|---|
| Licence | Apache 2.0, no usage limits | Free for individuals, companies of up to 3 people, non-profits and evaluation. Larger companies need a paid Company License |
| Authoring | One HTML file plus a GSAP timeline, no build step | React components, bundled before each render |
| Default render, wall time | 28.0 to 29.4 s (1 worker, low-memory profile) | 24.8 to 25.0 s (concurrency 1x) |
| Tuned, 2 parallel workers, wall time | 15.9 s (--no-low-memory-mode --workers 2) | 16.1 s (--concurrency=2) |
| Disk for a starter project | 76 KB project, 135 MB global CLI, 261 MB browser cache | 663 MB of node_modules, including a 236 MB headless shell |
| Scaffold time | 14 s (hyperframes init) | 41.6 s (npx create-video --yes --blank) |
Wall times here include process start and, for Remotion, the bundling step, so they run slightly above the CLI’s own “rendered in” figures. Output sizes are not comparable because the two tools default to different encoder quality. Remotion’s pricing page lists the paid tiers as Remotion for Automators at $0.01 per render with a $100 monthly minimum and Remotion for Creators at $25 per seat per month, both under the Company License. Check the Remotion license and pricing page for the current terms before you commit a team to it.
On a single render server the speed question is a wash once each tool is told to use its cores. Pick HyperFrames when an agent or a designer will write the compositions, when your source is already HTML (the same logic as rendering pages with Puppeteer HTML to PDF), or when a company of more than three people wants to avoid licence review. Pick Remotion when the team already writes React and wants typed composition props and its longer production history. Either way, the first change on a small Linux box is the same: tell the renderer how many cores it has. On this 2-core VM neither default used more than one, and while Remotion scales its default to half the CPU threads (up to 8) on bigger machines, HyperFrames stays at one worker on anything with 8 GB or less.