ossclip.

A local-first CLI that turns a talking-head take into a finished short: filler words and dead air cut, word-timed kinetic captions, face-aware framing, and LLM-planned, code-rendered on-screen graphics — title cards, stat cards, diagrams, terminal and chat mockups — planned against a hand-built, Zod-typed scene library. Plus a cover image sized for the platform.

Your footage never leaves your machine. Transcription is local (whisper.cpp), rendering is local (Remotion); the only network calls are the LLM planning ones — on your own API key, or on your existing Claude Code subscription.

Scope, honestly: ossclip is at its best polishing a take you have already cut down. For long-form input, --clip <seconds> selects the single strongest window (the producer's judgement, sentence-snapped) and produces only that — one clip, not N. Without it, 20 minutes in is a polished 20 minutes out.

AI can make mistakes: the cut, captions and graphics are generated — review the output before publishing.

working end to end · pre-1.0 Star on GitHub Before and after: the raw take beside the produced short

Left: the raw take. Right: what ossclip produce returns.

A produce run in the terminal: transcribe, analyze, cut, captions, scenes, render — each step reporting what it did

One ossclip produce run, end to end — every stage says what it did and why.

Install

Two commands, on macOS, Linux, or Windows (plain PowerShell — no WSL, no admin rights). The only prerequisite is Node ≥ 22 from nodejs.org.

npm install -g ossclip
ossclip setup

ossclip setup provisions everything into one folder (~/.ossclip): a static ffmpeg build, a prebuilt whisper.cpp whisper-cli (on macOS both come via Homebrew), and the transcription model — small.en, ~466 MB, the biggest piece of a ~600 MB total. It shows the plan with sizes and asks before downloading, resumes interrupted downloads, verifies checksums, and skips anything you already have — an ffmpeg already on your PATH stays yours. Paths are recorded in ~/.ossclip/config.json, so nothing edits your PATH. Uninstall = npm rm -g ossclip plus deleting ~/.ossclip.

Setup also offers to save an LLM key for --produce: a logged-in Claude Code is detected automatically, or paste an ANTHROPIC_API_KEY or GEMINI_API_KEY. Skip it freely — cut + captions run fully local without one. If anything looks wrong later, ossclip doctor prints a line per prerequisite and the exact fix.

Licence note, since setup downloads binaries: the static ffmpeg builds it fetches (BtbN) are GPL, downloaded onto your machine at your request — nothing GPL ships inside the MIT npm package.

Manual install (if you'd rather own the toolchain)

brew install ffmpeg whisper-cpp        # macOS; Linux/Windows: ffmpeg from your package manager,
                                       # whisper-cli from https://github.com/ggml-org/whisper.cpp/releases

mkdir -p ~/.ossclip/models
curl -L -o ~/.ossclip/models/ggml-small.en.bin \
  https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-small.en.bin

ossclip doctor checks all of it — with the per-platform command for anything missing. Binaries can live anywhere: point OSSCLIP_FFMPEG / OSSCLIP_WHISPER (or config.json) at them. Setup and manual install compose — setup only ever fills the gaps doctor would flag.

small.en is the default. A mistranscribed word ends up in your captions and on a graphic, so accuracy beats speed here — and --produce runs a repair pass over the transcript before anything is drawn. ossclip setup --model <name> downloads base.en (~142 MB) or medium.en (~1.5 GB) instead.

Quick start

# The whole thing: cut + captions + LLM-planned graphics + cover
ossclip produce input.mp4 --produce -o out.mp4

# Just the cut and captions — no LLM, no network
ossclip produce input.mp4 -o out.mp4

# See what would be cut and why, without rendering
ossclip produce input.mp4 --no-render

# Long-form in, one short out: the strongest ~60s window
ossclip produce podcast.mp4 --produce --clip 60 -o clip.mp4

# Check the whole toolchain first
ossclip doctor

# Open the editor over what a run produced (bare `edit` opens a project picker)
ossclip edit "<work directory>"

# Landscape (YouTube/desktop) instead of the 9:16 default
ossclip produce input.mp4 --produce --aspect 16:9 -o out.mp4

How it works

transcribe repair analyze cut captions produce scenes route & verify render (+ cover)

The work directory

Every run writes <input dir>/.ossclip/<name>-<hash>/ — the hash is of the source's CONTENT, so the same footage reuses its cache (rename it and you still land in the same place; --workdir starts a separate project, deleting the directory forces a clean run): the transcript, the analysis, production.json, render-props.json, report.txt, usage.json, the cached LLM plan, and command.json — the recorded invocation the editor's Render button replays. It is a cache: delete it to force a clean run; keep it and re-runs are near-instant.

Each run appends to usage.json — one entry per run with its provider, models, tokens and cost — and stamps the same provenance onto production.json, so a workdir always says who planned it even when a later run answers entirely from cache.

Edits survive re-runs. Everything you change in the editor lands in overrides.json — a file the producer never writes. Re-running produce re-plans the video and keeps your edits. Re-rendering from the editor replays the same configuration as the recorded run — flags, aspect, and provider (the resolved provider is pinned into command.json, so an env-detected choice can't drift).

Models & cost

Without --llm, ossclip picks: GEMINI_API_KEYANTHROPIC_API_KEY → the Claude Code CLI (your Pro/Max subscription — plan usage, not API credits). Calls are tiered: editorial judgement (the beat sheet, transcript repair) goes to the main model; mechanical calls go to a smaller sibling.

▸ llm: 3 calls · 130,749 in / 8,321 out tokens · ~$0.72 of API-rate work, covered by the subscription · 92s

Reported token counts are exact where the provider reports them and marked (est) where derived; a model with no known price gets tokens and no cost guess. Per-call breakdown in report.txt, raw records in usage.json.

Editing

The ossclip editor: preview, transform handles, inspector and timeline

Select, drag, retype — every edit lands in overrides.json, which the producer never overwrites.

ossclip edit "<work directory>"     # opens http://127.0.0.1:5174
ossclip edit                        # bare: pick from recent projects, or browse
Direct manipulationClick to select, drag to move, corner handles to resize, panel fields for precision. Graphic boxes, elements, video framing, pip bubble — every element on every component is grabbable.
TimelineZoom 1–16×, scrub, drag blocks in time, resize edges, split at the playhead (⌘B — each half inherits the scene's edits), soft-delete with a way back. The view follows the cursor.
CaptionsDouble-click a word to retype it (1:1 — timings stay). Position and scale per scene, or applied to every scene at once.
Transcript viewThe whole transcript, searchable — ‹ › chevrons (or Enter/⇧Enter) walk the matches — in a drag-resizable pane that follows playback; click a word to jump the preview, double-click to fix it.
Render from the editorReplays the recorded produce invocation — same flags, same provider — with live progress, elapsed time, token/cost lines, cancel, and a collapsible scrolling log. Survives a refresh.
PreviewFills the window in either aspect; ⌘+scroll zooms the view 25%–800% — magnify to inspect or shrink to arrange, never an edit — and ⌥-drag pans.
ProjectsUndo/redo in the top bar, and Open switches projects in place: recent produce runs plus a folder browser, no server restart.

Keybinds

Press ? in the editor for this list in-app.

transport

spaceplay / pause
J / K / Lreverse · pause · forward (tap again to speed up)
← / →step one frame back / forward
⌘/ctrl + ← / →jump to and select the previous / next scene

selection

clickselect scene or element
⌥ + ← / →select previous / next scene
escclear selection · close dialogs

editing

⌘/ctrl + Bsplit the scene at the playhead
delete / backspacedelete the selected scene (restorable)
⌘/ctrl + Zundo
⌘/ctrl + ⇧ + Zredo (⌘Y works too)
⌘/ctrl + Ssave
double-click caption wordretype it in place
drag element / corner handlesmove · resize
drag picturepan the video framing

view

⌘/ctrl + scroll on previewzoom the view (never edits)
⌥-drag / middle-dragpan the zoomed view
⌘/ctrl + scroll on timelinezoom the timeline
?the in-app reference

Scene components

TitleCardThe headline moment — eyebrow, title, an emphasized stat, subtitle.
StatCardOne number that matters: label | value, optional caption.
RuleCardA kicker and a rule to live by, optionally with a struck counter-line.
StrikethroughRevealNegation/contrast — big words, struck lines, or ✗/✓ verdict marks per line.
BulletListAn enumeration — one bullet per listed item, optional kicker title.
FlowDiagramA causal chain as chips with arrows (TEAM → AI AGENTS → CHURN).
TerminalMockStylized terminal windows for anything about running code or agents.
ChatMockA chat exchange, or the single comment-CTA bubble.
ScreenshotFrameA framed screenshot look with a label chip.

Every component sizes its type to the slot it is given (nothing overflows the platform-safe area), animates in with a staggered rise, and leaves through a uniform exit fade. All text on them is editable in place.

Layouts

Layout places the video; the component always renders. Portrait (9:16) and landscape (16:9) each get frame-appropriate geometry — the split axis follows the frame's long edge.

layoutarrangement
full-bleedthe talking head, full frame
blurred-behindspeaker blurred and dimmed behind a centred graphic
video-topvideo band on top, graphic below (portrait)
pip-bubblebig graphic + the speaker in a round bubble (roundness and placement editable per scene)
graphic-onlythe graphic owns the frame
lower-thirdpicture whole, card in the bottom band (broadcast lower third)
split-left / split-rightspeaker fills one half, graphic the other (landscape; stacks in portrait)

produce flags

--producerun the LLM producer brain. Without it: cut + captions only
--clip <seconds>produce only the strongest ~N-second window of a long take, sentence-snapped (requires --produce; a short-enough source is produced whole). The report says what was chosen and why
--intent "<text>"what the video should be — steers the producer's editorial choices
--speaker "<who>"who is on camera — helps repair recognise a mangled name
--cleanup <level>exact | light | standard | aggressive — how hard to cut silence and fillers
--aspect <ratio>9:16 (default) or 16:9 — landscape export with landscape-native layouts
--source-fit <mode>cover crops to fill; contain shows the whole frame inset — the landscape-source escape hatch
--llm <provider>claude | claude-cli | gemini | mock
--llm-model / --llm-fast-modeloverride the editorial / mechanical model (same disables tiering)
--whisper-model <name>transcription model for this run (base.en | small.en | medium.en)
--no-repairskip the ASR mishearing repair
--scenes <path>hand-authored scenes JSON — no LLM in the loop
--source-is-editedthe source already has burned-in graphics — keep ossclip's off them (also what turns the source-text scan on)
--blooper-marker <word>say the word on camera and the flubbed take is cut, back to the start of the sentence it spoiled. Off unless given
--no-cover / --cover <path>skip, or redirect, the cover image
--no-renderstop after writing the props
--workdir <dir>where the cache lives

Configuration

~/.ossclip/config.json — all optional:

{
  "model": "small.en",
  "speaker": "Ahsan, host of the Code with Ahsan channel",
  "fastModel": "claude-haiku-4-5-20251001",
  "ffmpegPath": "ffmpeg",
  "whisperPath": "whisper-cli",
  "modelDir": "~/.ossclip/models",
  "browserExecutable": "/path/to/chrome",
  "pricing": { "claude-opus-5": { "inputPerMTok": 15, "outputPerMTok": 75 } }
}

Provider keys come from the environment, and ossclip loads .env files before choosing a provider — first hit wins per key, a real environment variable always beats a file: $OSSCLIP_ENV_FILE.env walking up from the cwd → ~/.ossclip/.env.

Env vars override the file: OSSCLIP_FFMPEG, OSSCLIP_FFPROBE, OSSCLIP_WHISPER, OSSCLIP_MODEL_DIR, OSSCLIP_MODEL, OSSCLIP_FAST_MODEL, OSSCLIP_SPEAKER, OSSCLIP_BROWSER.

Licensing

ossclip's own code is MIT — see the repo's LICENSE.

Rendering uses Remotion, which is source-available under its own two-tier licence, not MIT: free for individuals, non-profits, and for-profit companies up to the size stated in its terms; larger for-profit companies need a paid Remotion Company Licence. If you use ossclip inside a company, check Remotion's LICENSE — its terms are authoritative.

ossclip is built findings-first: every fix traces to a numbered defect from a real render — the running log lives in docs/PHASE1-FINDINGS.md. This page: docs/site/index.html, self-contained, ready for GitHub Pages.