A command-line video editor. Edits are JSON documents; titles and graphics are HTML and CSS, drawn without a browser.

One binary for Linux (x64, arm64), macOS (Apple silicon) and Windows (x64). The codecs come from FFmpeg's libraries; the planner, compositor and layout engine are written in Rust. MIT licensed.

Install

Linux, macOS
curl -fsSL https://raw.githubusercontent.com/geneva-render/geneva/main/scripts/install.sh | sh
Windows (PowerShell)
irm https://raw.githubusercontent.com/geneva-render/geneva/main/scripts/install.ps1 | iex

Or download an archive from Releases. H.264 is encoded with the bundled OpenH264, or with x264 when it is installed on the system.

Everyday commands

geneva trim match.mp4 -o goal.mp4 --from 41:10 --to 41:40
geneva concat day1.mp4 day2.mp4 -o trip.mp4 --crossfade 1s
geneva convert talk.mov -o talk.mp4 --for web
geneva subtitles talk.mp4 -o talk-subbed.mp4 --burn talk.srt
geneva probe talk.mp4

Each command is turned into a JSON document and rendered. --show-timeline prints it. All commands: docs/cli.md.

A document

A name card over ten seconds of footage. The card is an HTML file; it plays the same in a browser.

card.html
<style>
  @keyframes slide-in { from { translate: -100% } to { translate: 0 } }
  @keyframes fade-out { from { opacity: 1 } to { opacity: 0 } }
  .card {
    animation: slide-in 0.5s ease-out, fade-out 0.3s 3.7s;
    position: absolute; left: 4.4%; top: 7.5%; width: 33%;
    display: flex; flex-direction: column; gap: 6px; padding: 14px 24px;
    background: #0a0f14cc; border-left: 5px solid #c4362f;
  }
  .card h1 { margin: 0; font: 600 29px Liberation Sans; color: #f2f5f7 }
  .card p  { margin: 0; font: 500 13px Liberation Sans; color: #94a6b6 }
</style>
<div class="card">
  <h1>Dragon CRS-17</h1>
  <p>BERTHING AT THE ISS &middot; NASA</p>
</div>
edit.json
{
  "geneva": "1.1",
  "output": { "width": 1280, "height": 720, "fps": 30 },
  "assets": { "iss": { "src": "iss.mp4" }, "card": { "src": "card.html" } },
  "layers": [
    { "id": "footage", "clips": [ { "source": { "kind": "video", "asset": "iss" } } ] },
    { "id": "card", "clips": [ { "source": { "kind": "html", "asset": "card" },
                                 "start": "2s", "duration": "4s" } ] }
  ]
}
shell
$ geneva render edit.json -o dragon.mp4
note[N600]: smart cut: 165 of 300 frames copied from the source, 135 encoded in 1 run around the cuts and overlays
note[N600]: H.264 runs encoded with the system's x264 (build 164) at CRF 18
wrote dragon.mp4 (300 frames, 10s of video)
The full example, examples/lower-third.json, also adds word-by-word captions from a Whisper-format transcript. Footage: NASA, public domain.

Frames with nothing drawn on them are copied from the source when it is H.264 and x264 is installed, and otherwise go from decoder to encoder without passing through the compositor. Errors are reported before rendering, with a code and the path in the document: error[E200]: unknown asset "crad".

Motion graphics

Both of these are one HTML file and a short JSON document. No footage in the first; the second cuts to footage at the end.

1920x1080, 30 fps, 14 s. About 40 s to render on 4 vCPUs with no GPU. Fonts: Inter, JetBrains Mono (SIL OFL), shipped with the document as font assets. Source: race.html, race.json.
1920x1080, 30 fps, 13 s. About 70 s to render on 4 vCPUs with no GPU. Fonts: Inter, JetBrains Mono (SIL OFL), shipped with the document as font assets. Source: opening.html, opening.json. Footage: "Shibuya Crossing, Tokyo, Japan" by Basile Morin, CC BY-SA 4.0, 960x540 scaled up to fill the frame; this video is under the same licence.

What the markup supports

Flexbox and block layout, absolute positioning, gradients, box-shadow, filter: blur(), clip-path: polygon(), mix-blend-mode, background-clip: text, web fonts shipped as assets, and CSS @keyframes played as written.

Not supported: CSS grid, floats, transitions, static transform (transforms come from animations), and JavaScript. An unsupported declaration is reported by name and skipped. The full list is in docs/timeline.md.

Speed

Seven jobs, rendered by geneva, by Playwright screenshotting each frame for ffmpeg, and by Remotion. Time to render, median of three rounds. One machine: 4 vCPUs of an Intel Xeon at 2.1 GHz, 16 GB, no GPU. All three encode x264 CRF 23, preset medium.

JobgenevaPlaywright + ffmpegRemotion
Social clip, 60 s, 1080x192060.8 s140.6 s270.3 s
Broadcast package, 3 min52.2 s244.2 s324.3 s
Static captions, 4 min95.6 s300.4 s444.7 s
Dynamic captions, 90 s58.1 s190.9 s164.7 s
Opening, 13 s10.0 s64.2 s19.1 s
Intro, 14 s30.4 s145.0 s63.4 s
Kinetic typography, 16 s14.2 s56.2 s30.2 s

geneva with its defaults, which copy frames the edit leaves alone. With frame copying off and the encoder pinned to the others' settings, the broadcast package takes 99.4 s and the static captions 140.7 s. Method, caveats and scripts: docs/benchmarks.md.

For scripts and agents

geneva guide prints the manual, geneva explain <code> explains any diagnostic, and --format json works on every command. The document format has a published JSON schema. See docs/agents.md.

Status