oxframe vs HyperFrames

Pick oxframe when the video is motion graphics an agent should check and render in seconds without a browser. Pick HyperFrames when the video is a web page: HTML, CSS, GSAP, Lottie or Three.js you already have, rendered in headless Chrome.

At a glance

Checked against the sources below on October 22, 2026.
oxframeHyperFrames
LanguageRustHTML, CSS and JavaScript (GSAP, Anime.js, WAAPI, Lottie, Three.js)
How a frame is drawnNative renderer, CPU or GPUHeadless Chrome through Puppeteer, encoded by FFmpeg
LicenseMIT or Apache-2.0Apache-2.0
TelemetryNoneAnonymous render telemetry in the CLI
InstallRust and ffmpegNode.js 22+ and FFmpeg; Chrome is driven through Puppeteer
Timing modelSignals and an async timeline: wait_for, all, markersdata-start and data-duration on elements, plus a paused GSAP timeline
Live previewoxframe dev: timeline, markers, tunableshyperframes preview with live reload, and Studio
An agent sees the framesoxframe check: storyboard and JSON report, MCP serverhyperframes check, skills and a Claude Code plugin
Frame shapes16:9, 9:16 and 1:1 from one sceneResolution presets per project
Parallel renderingrender --jobs N, the same bytes for any NParallel Chrome workers, about 256 MB each
Web content in the frameNoAnything a page can show

One scene in both

The scene from the Remotion migration guide, written for HyperFrames as HTML and GSAP: a title that rises in and a counter from 0 to 100 %. Four seconds at 1080p and 30 fps.

HyperFrames

<!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/gsap@3.14.2/dist/gsap.min.js"></script>
    <style>
      * { margin: 0; padding: 0; box-sizing: border-box; }
      html, body { width: 1920px; height: 1080px; overflow: hidden; background: #141414; }
      #root {
        width: 100%; height: 100%; display: flex; flex-direction: column;
        align-items: center; justify-content: center; color: white; font-family: sans-serif;
      }
      #card { width: 100%; height: 100%; display: flex; flex-direction: column; align-items: center; justify-content: center; }
      #title { font-size: 96px; opacity: 0; transform: translateY(60px); }
      #line { font-size: 40px; opacity: 0; }
    </style>
  </head>
  <body>
    <!-- The Remotion "Release 2.0" scene from /docs/migrate/remotion, as HTML. -->
    <div id="root" data-composition-id="main" data-start="0" data-duration="4" data-width="1920" data-height="1080">
      <div id="card" class="clip" data-start="0" data-duration="4" data-track-index="0">
        <h1 id="title">Release 2.0</h1>
        <p id="line">0% faster builds</p>
      </div>
    </div>
    <script>
      const tl = gsap.timeline({ paused: true });
      tl.to('#title', { opacity: 1, duration: 0.66, ease: 'none' }, 0);
      tl.to('#title', { y: 0, duration: 0.8, ease: 'back.out(1.7)' }, 0);
      tl.set('#line', { opacity: 1 }, 1);
      const percent = { v: 0 };
      tl.to(percent, {
        v: 100, duration: 2, ease: 'none',
        onUpdate: () => (document.getElementById('line').textContent = `${Math.round(percent.v)}% faster builds`),
      }, 1);
      tl.to({}, { duration: 1 }, 3);
      window.__timelines['main'] = tl;
      tl.seek(0);
    </script>
  </body>
</html>

oxframe

use oxframe::prelude::*;

#[oxframe::scene]
pub async fn hello(view: View) {
    let title = view.add(Txt::new().text("Release 2.0").h1().opacity(0.0).y(-20.0));
    // Runs alongside the rest of the scene, like an element that is already mounted.
    spawn(async move {
        all((
            title.opacity().to(1.0, 0.66),
            title.y().to(-80.0, 0.8).ease(presets::OUT_BACK),
        ))
        .await;
    });
    wait_for(1.0).await; // <Sequence from={30}> at 30 fps

    let percent = Signal::new(0.0);
    let line = view.add(Txt::new().body().y(40.0));
    line.text().bind({
        let percent = percent.clone();
        move || format!("{:.0}% faster builds", percent.get())
    });
    percent.to(100.0, 2.0).await;
    wait_for(1.0).await;
}

pub fn project() -> Project {
    Project::new("hello").size(1920, 1080).fps(30).scene(hello())
}

Render time

4 s of 1920 × 1080 video, median of three runs after a warm-up, 2026-10-22, Intel(R) Core(TM) Ultra 7 255H, 16 threads, Linux.
ToolCommandTime
HyperFrames 0.8.185npx hyperframes render --fps 30 (the Remotion scene as HTML)4.00 s
oxframe 0.20.4render --cpu0.53 s
oxframe 0.20.4render (GPU)1.46 s

Each time is the whole render command, start to file. oxframe's scene is already compiled; after a one-line edit, cargo build adds about 0.6 s. On a scene this short the GPU spends more time starting than drawing, so the CPU renderer wins. The script is bench/vs/measure.sh: run it to time both on your machine.

A page that is filmed, or a scene that is drawn

HyperFrames, from HeyGen, treats a video as an HTML page with a timeline. Elements carry data-start and data-duration, a paused GSAP timeline holds the motion, and the renderer seeks the page frame by frame in headless Chrome before FFmpeg encodes it. Anything a browser can show can be in the video, and an agent that writes web pages already knows how to write one.

oxframe draws the frame itself. A scene is Rust code with signals: a title, a list, a chart, a code block, each a node with properties that animate in an async timeline. There is no page to lay out and no browser to start, so the same scene renders in a fraction of the time and the same code gives the same bytes on every machine.

The trade is plain. HyperFrames wins when the content is web content. oxframe wins when the content is motion graphics, the render runs in CI, and an agent has to check its own frames: oxframe check reports overflowing text, contrast, safe areas and motion that is too fast to read, with the node and the time of each problem.

When to pick HyperFrames

  • The video reuses web content: HTML, CSS, Tailwind, Lottie or Three.js.
  • Your agent works best in HTML and you do not want a compiled language.
  • You want a large catalog of ready blocks and HeyGen in the pipeline.

When to pick oxframe

  • Renders should take seconds and run without Chrome, on a laptop or a free CI runner.
  • An agent should get a report of what is wrong in the frames, not just a video.
  • You want no telemetry at all and byte-identical renders.
  • One scene should give 16:9, 9:16 and 1:1.

Moving from HyperFrames

There is no HyperFrames guide yet. The Remotion guide covers the same ideas (a timeline of elements that fade, move and count) and the scene above is the same scene in both.

FAQ

Is HyperFrames open source?

Yes, Apache-2.0, with no per-render fees. oxframe is MIT or Apache-2.0. Both are free for companies; the difference is the renderer and the tooling, not the license.

Does HyperFrames need Chrome?

Yes. Its README says the renderer seeks each frame in headless Chrome and encodes with FFmpeg; it needs Node.js 22+ and FFmpeg. oxframe needs Rust and ffmpeg and no browser.

Which one is easier for an AI agent?

An agent writes HTML more fluently than Rust, which favors HyperFrames on the first draft. oxframe gives the agent a check: a storyboard and a JSON report of what is wrong, with the node and the time, so it can fix a frame without a human looking.

Does oxframe collect telemetry?

No. Neither the CLI nor the SDK sends anything. The browser playground compiles your scene on compile.oxframe.dev; nothing else leaves your machine.

Can oxframe render HTML or Lottie?

No. oxframe draws shapes, text, code, SVG, images, video clips, charts and 3D meshes. For web content, HyperFrames or Remotion is the better fit.

Sources

Checked on October 22, 2026. Found something out of date? Open an issue on oxframe-web.

Other comparisons

Get started with oxframe