The open-source CapCut alternative
Go to file
Andrés Villagrán b1b3b1c9d1 feat(export): MediaRecorder fallback when WebCodecs encoders are missing
The existing exporter uses mediabunny, which delegates to the
WebCodecs VideoEncoder and AudioEncoder. On browsers where those
APIs are not exposed (the same environments that needed the decode
fallback in #794), export currently fails with
"AudioEncoder is not supported by this browser" before producing
any output.

This change adds a parallel fallback path in SceneExporter:

- New helpers \`isWebCodecsExportSupported()\` and
  \`detectMediaRecorderSupport()\` live in a tiny dependency-free
  module so they can be unit-tested without dragging in WASM.
- New \`exportWithMediaRecorder\` drives the existing CanvasRenderer
  at real-time wall-clock, captures the output canvas with
  \`HTMLCanvasElement.captureStream(fps)\`, mixes the project audio
  buffer through a \`MediaStreamAudioDestinationNode\`, and records
  the combined MediaStream with MediaRecorder, producing a WebM
  blob the same browser can play back natively.
- \`SceneExporter.export()\` detects \`!isWebCodecsExportSupported()\`
  at entry and delegates to the new path. The WebCodecs path is
  unchanged for capable browsers.

Trade-offs of the fallback path:

- Output is always WebM (most browsers don't expose an MP4 muxer
  via MediaRecorder). The user's requested format is honoured
  best-effort.
- Rendering runs at real time, so a 60-second timeline takes
  roughly 60 seconds to export. mediabunny's WebCodecs path
  continues to render faster than real-time on capable browsers.
- Bitrate is mapped per quality preset to a numeric value
  compatible with \`MediaRecorder.videoBitsPerSecond\`.

Unit tests cover the feature-detection helpers across the four
states of \`VideoEncoder\` / \`AudioEncoder\` / \`MediaRecorder\`
availability.
2026-05-16 21:47:34 -04:00
.github chore: switch from biome to eslint + prettier; fix ton of lint issues 2026-05-02 03:27:23 +02:00
.vscode chore: switch from biome to eslint + prettier; fix ton of lint issues 2026-05-02 03:27:23 +02:00
apps feat(export): MediaRecorder fallback when WebCodecs encoders are missing 2026-05-16 21:47:34 -04:00
docs feat: migrate GPU renderer from WebGL to wgpu/WASM 2026-04-01 13:57:32 +02:00
eslint/rules chore: switch from biome to eslint + prettier; fix ton of lint issues 2026-05-02 03:27:23 +02:00
notes docs: move NOTES.md file 2026-05-02 16:38:06 +02:00
rust refactor: split animation helpers by domain 2026-04-26 20:44:07 +02:00
script feat: migrate time utilities from typescript to rust WASM 2026-03-31 04:03:52 +02:00
.dockerignore docker + update readme 2026-02-23 09:38:38 +01:00
.gitignore chore: ignore migrations file 2026-05-02 16:26:09 +02:00
.npmrc fix: CDN instead of app for databooty 2025-07-02 16:25:08 +03:00
.prettierignore chore: switch from biome to eslint + prettier; fix ton of lint issues 2026-05-02 03:27:23 +02:00
.prettierrc.json chore: switch from biome to eslint + prettier; fix ton of lint issues 2026-05-02 03:27:23 +02:00
.tokeignore chore: add .tokeignore 2026-04-14 22:10:15 +02:00
AGENTS.md chore: switch from biome to eslint + prettier; fix ton of lint issues 2026-05-02 03:27:23 +02:00
Cargo.lock refactor: split animation helpers by domain 2026-04-26 20:44:07 +02:00
Cargo.toml feat: wasm compositor 2026-04-13 05:01:51 +02:00
LICENSE chore: update copyright year 2026-05-06 22:53:51 +02:00
README.md docs: fix readme 2026-05-02 16:51:01 +02:00
biome.json chore: delete ffmpeg public assets 2026-04-21 13:00:26 +02:00
bun.lock chore: switch from biome to eslint + prettier; fix ton of lint issues 2026-05-02 03:27:23 +02:00
docker-compose.yml chore: remove transcription envs 2026-04-04 08:53:10 +02:00
eslint.config.mjs chore: switch from biome to eslint + prettier; fix ton of lint issues 2026-05-02 03:27:23 +02:00
package.json chore: switch from biome to eslint + prettier; fix ton of lint issues 2026-05-02 03:27:23 +02:00
tsconfig.json refactor: restructure files to their domains, new preview overlay system, and dep graph 2026-04-20 11:31:17 +02:00
turbo.json fix: turbo 2026-04-15 00:41:52 +02:00
wrangler.jsonc chore: normalize line endings 2026-03-29 15:56:48 +02:00

README.md

OpenCut Logo

OpenCut

A free, open-source video editor for web, desktop, and mobile.

Sponsors

Thanks to Vercel and fal.ai for their support of open-source software.

Vercel OSS Program Powered by fal.ai

Why?

  • Privacy: Your videos stay on your device
  • Free features: Most basic CapCut features are now paywalled
  • Simple: People want editors that are easy to use - CapCut proved that

Project Structure

  • apps/web/: Next.js web application
  • apps/desktop/: Native desktop app built with GPUI (in progress)
  • rust/: Platform-agnostic core: GPU compositor, effects, masks, and WASM bindings. We're actively migrating business logic here from TypeScript.
  • docs/: Architecture and subsystem documentation

Getting Started

Prerequisites

Note: Docker is optional but recommended for running the local database and Redis. If you only want to work on frontend features, you can skip it.

Setup

  1. Fork and clone the repository

  2. Copy the environment file:

    # Unix/Linux/Mac
    cp apps/web/.env.example apps/web/.env.local
    
    # Windows PowerShell
    Copy-Item apps/web/.env.example apps/web/.env.local
    
  3. Start the database and Redis:

    docker compose up -d db redis serverless-redis-http
    
  4. Install dependencies and start the dev server:

    bun install
    bun dev:web
    

The application will be available at http://localhost:3000.

The .env.example has sensible defaults that match the Docker Compose config — it should work out of the box.

Desktop setup

Desktop is opt-in. If you're only working on the web app, skip this entirely.

If you want to get ready for apps/desktop, see apps/desktop/README.md. It's a two-step setup: Rust toolchain first, then desktop native dependencies.

Local WASM development

Only needed if you're editing rust/wasm and want the web app to use your local build instead of the published package.

Prerequisites — install these once before anything else:

# Rust toolchain
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

# build the WASM package
cargo install wasm-pack

# reruns the build on file changes, used by bun dev:wasm
cargo install cargo-watch
  1. Build the package once from the repo root:

    bun run build:wasm
    
  2. Register the generated package for linking:

    cd rust/wasm/pkg
    bun link
    
  3. Link apps/web to the local package:

    cd apps/web
    bun link opencut-wasm
    
  4. Rebuild on changes while you work:

    bun dev:wasm
    

To switch apps/web back to the published package, run:

cd apps/web
bun add opencut-wasm

Self-Hosting with Docker

To run everything (including a production build of the app) in Docker:

docker compose up -d

The app will be available at http://localhost:3100.

Contributing

We welcome contributions! While we're actively developing and refactoring certain areas, there are plenty of opportunities to contribute effectively.

🎯 Focus areas: Timeline functionality, project management, performance, bug fixes, and UI improvements outside the preview panel.

⚠️ Avoid for now: Preview panel enhancements (fonts, stickers, effects) and export functionality - we're refactoring these with a new binary rendering approach.

See our Contributing Guide for detailed setup instructions, development guidelines, and complete focus area guidance.

Quick start for contributors:

  • Fork the repo and clone locally
  • Follow the setup instructions in CONTRIBUTING.md
  • Working on apps/desktop? See apps/desktop/README.md for setup
  • Create a feature branch and submit a PR

License

MIT LICENSE


Star History Chart