oxframe vs Shotstack

Pick oxframe when you want the video as code in your repository, rendered on your own machine or CI with no per-minute bill. Pick Shotstack when you want a hosted render API, a template editor for non-developers and no install at all.

At a glance

Checked against the sources below on October 22, 2026.
oxframeShotstack
How a video is describedA Rust sceneA JSON edit: a timeline of tracks and clips
Where it rendersYour machine or your CIShotstack’s cloud, through a render queue
Getting the fileIt is written to diskPoll the render by id, then download; output URLs last 24 hours
Price$0; your own computeFrom $10 for 50 minutes; plans from $39 a month for 250 minutes
DevelopmentThe same renderer everywhereA stage sandbox; v1 renders without watermarks
Data from outsideAny Rust value: a CSV row, JSON, an APITemplates with merge fields
LayoutRows, columns, wrapping text, themesPositions and alignment per clip
Charts, code, mathBuilt in: axes, plots, code with diffs, matricesImages and video you render elsewhere
An agent sees the framesoxframe check with a JSON reportRender, download, look
Account and keyNoneAn API key in the x-api-key header
LicenseMIT or Apache-2.0Proprietary service

One scene in both

The edit from Shotstack’s Hello World guide: “HELLO WORLD” fading in and out over five seconds, with a music track, at 1024 × 576. The Shotstack side is the JSON you POST to /edit/stage/render; the oxframe side is the scene you render with oxframe render. We did not time the Shotstack render: it needs an account, and its time includes the queue.

Shotstack

{
  "timeline": {
    "tracks": [
      {
        "clips": [
          {
            "asset": {
              "type": "rich-text",
              "text": "HELLO WORLD",
              "font": {
                "family": "Montserrat",
                "color": "#ffffff",
                "size": 32
              },
              "align": {
                "horizontal": "left"
              }
            },
            "start": 0,
            "length": 5,
            "transition": {
              "in": "fade",
              "out": "fade"
            }
          }
        ]
      },
      {
        "clips": [
          {
            "asset": {
              "type": "audio",
              "src": "https://s3-ap-southeast-2.amazonaws.com/shotstack-assets/music/moment.mp3",
              "effect": "fadeOut"
            },
            "start": 0,
            "length": "end"
          }
        ]
      }
    ]
  },
  "output": {
    "format": "mp4",
    "size": {
      "width": 1024,
      "height": 576
    }
  }
}

oxframe

use oxframe::prelude::*;

#[oxframe::scene]
pub async fn hello(view: View) {
    // A rich-text clip, left-aligned, white, 32 px; it fades in and out.
    let title = view.add(
        Txt::new()
            .text("HELLO WORLD")
            .font_size(32.0)
            .fill(Color::WHITE)
            .to_edge(Edge::Left)
            .opacity(0.0),
    );
    title.fade_in().await;
    // Shotstack holds the frame still under its music. Without a soundtrack,
    // oxframe check calls four still seconds dead time, so the title drifts.
    title.scale().to(Vec2::new(1.08, 1.08), 4.0).ease(presets::LINEAR).await;
    title.fade_out().await;
}

pub fn project() -> Project {
    // The audio track becomes Audio::track("moment.mp3").master_fade(0.0, 1.0) with
    // the file next to the project; the playground has no files, so it is left out.
    Project::new("hello").size(1024, 576).scene(hello())
}

What a thousand videos cost

Take a thousand 30-second videos a month: 500 minutes of video.

  • Shotstack, from its pricing page on 2026-10-22: the Pro plan, $99 a month for 750 minutes, or $100 pay as you go at 20¢ a minute. The Starter plan’s 250 minutes are not enough.
  • oxframe: no fee. On a laptop, a 27.5-second explainer renders in 8.4 seconds on 8 jobs (the measurement on the home page), so 500 minutes of similar video take about two and a half hours of one machine. On GitHub Actions that is runner time, free for public repositories.

The trade is time and hardware against a bill and a dependency. A hosted API wins when you render rarely or cannot run anything yourself; code wins when you render often, need logic in the video or want the data to stay with you.

Moving an edit over

A Shotstack edit maps onto a scene almost clip by clip:

Shotstackoxframe
a clip with start and lengtha node, with .await and wait_for for time
transition: { in: "fade", out: "fade" }fade_in(), fade_out(); scenes change with transition(…)
rich-text and title assetsTxt, or components such as TitleCard and LowerThird
image and video assetsImg, Video (feature video)
an audio trackAudio::track(…) with fades and ducking under a voice
merge fields {{NAME}}a Rust value, read from the environment, a file or main
output.sizeProject::size(w, h), or .aspects(…) for several shapes at once

A video per row of a CSV

Where Shotstack fills a template’s merge fields for each request, oxframe reads the row in the scene, and a shell loop renders one video per row.

use oxframe::prelude::*;

/// One row of the CSV, passed in by the loop that renders it.
fn field(name: &str) -> String {
    std::env::var(name).unwrap_or_default()
}

#[oxframe::scene]
pub async fn card(view: View) {
    let title = view.add(TitleCard::new(format!("Thank you, {}", field("NAME"))).subtitle(format!(
        "{} pull requests merged this year",
        field("PRS")
    )));
    title.enter().await;
    title.next().await;
    wait_for(1.5).await;
}

pub fn project() -> Project {
    Project::new("thanks").scene(card())
}
# people.csv:  name,prs
#              Ada,42
#              Grace,17
cargo build --release
tail -n +2 people.csv | while IFS=, read -r name prs; do
  NAME="$name" PRS="$prs" target/release/thanks render --out "out/$name.mp4"
done

On the benchmark machine the loop renders 3 cards of 2.42 s each, 1080p at 60 fps, in 14.65 s on the CPU, one after another (2026-10-22).

Rows are independent, so a CI matrix or xargs -P renders them in parallel. NAME=… oxframe check on the longest name catches a title that does not fit its card before the batch runs.

When to pick Shotstack

  • Nobody on the team should install or run a renderer.
  • Non-developers edit templates in a web editor.
  • You render a few videos a month and a bill is simpler than a machine.

When to pick oxframe

  • You render many videos and do not want a per-minute bill.
  • The video needs logic: charts from data, layout that adapts, code walkthroughs.
  • Data and output should stay on your machine or in your CI.
  • An agent writes the scene and should check its frames.

FAQ

Is oxframe an open-source Shotstack alternative?

For the rendering part, yes: oxframe is MIT or Apache-2.0 and renders on your machine. It is a library and a CLI, not a hosted API, so there is no endpoint to call; you run the render where you want it.

Can I keep my Shotstack JSON?

Not as is. oxframe does not read Shotstack edits; each clip becomes a line or two of a scene, as in the table above. An agent with the oxframe skill can port an edit for you.

How do I render one video per row of data?

Read the row in the scene (from the environment, a file or main) and render once per row from a loop or a CI matrix. The example above renders a thank-you card per contributor.

How fast is oxframe compared to Shotstack?

We have not timed Shotstack: it needs an account, and a cloud render includes the queue. oxframe renders a 27.5-second explainer in 8.4 seconds on a laptop with 8 jobs.

Do I need an account or an API key?

No. oxframe sends nothing anywhere; the only network use is the optional playground on oxframe.dev.

Sources

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

Other comparisons

Get started with oxframe