Project structure
OpenCut uses a Turborepo monorepo structure with workspaces for apps and shared packages:Directory conventions
lib/- Domain logic specific to OpenCut (actions, commands, video processing)utils/- Small, generic helper functions that could be reused in any applicationservices/- Integrations with external services and APIscore/- The EditorCore singleton and manager system
Core editor system
The editor uses a singleton EditorCore that manages all editor state through specialized managers.Architecture overview
Using EditorCore
In React components
Always use theuseEditor() hook:
useEditor() hook:
- Returns the singleton EditorCore instance
- Subscribes to all manager state changes
- Automatically triggers re-renders when state changes
Outside React components
UseEditorCore.getInstance() directly:
- Utility functions
- Event handlers outside components
- Tests
- Complex multi-step operations
Actions system
Actions are the trigger layer for user-initiated operations. They provide a consistent UX layer with toasts, validation, and keyboard shortcuts.Defining actions
The single source of truth isapps/web/src/lib/actions/definitions.ts:
Implementing actions
Action handlers are defined inapps/web/src/hooks/use-editor-actions.ts:
Using actions in components
Always useinvokeAction() for user-triggered operations:
editor.xxx() calls should be reserved for:
- Internal implementation within commands
- Test code
- Complex multi-step operations where you need fine-grained control
Commands system
Commands handle undo/redo functionality. They live inapps/web/src/lib/commands/ organized by domain.
Command structure
Each command extendsCommand from apps/web/src/lib/commands/base-command.ts:
Commands organization
Commands are organized by domain:Actions + Commands working together
Actions and commands complement each other:- Actions = “What triggered this?” (user shortcuts, button clicks)
- Commands = “How to do it (and undo it)?” (the actual operation)
- User presses
Ctrl+B(keyboard shortcut) - Action system invokes
"split-selected"action - Action handler validates selection and shows toast
- Action handler creates and executes a
SplitElementCommand - Command saves state and performs the split
- User can undo with
Ctrl+Z, which callscommand.undo()
State management
OpenCut uses multiple state management approaches:EditorCore managers
For editor state (timeline, playback, scenes):- Centralized in the EditorCore singleton
- Each manager handles its domain
- Subscribe via
useEditor()hook
Zustand stores
For UI and application state (located insrc/stores/):
- User preferences
- UI panel visibility
- Dialog state
- Feature flags
React state
For component-local state:- Form inputs
- Temporary UI state
- Animation state
Technology stack
Frontend
- Next.js 16 - React framework with App Router
- React 19 - UI library
- TypeScript - Type safety
- Tailwind CSS 4 - Styling
- Radix UI - Accessible component primitives
- Zustand - Lightweight state management
- Motion - Animations
Editor
- FFmpeg.wasm - Video processing in the browser
- WaveSurfer.js - Audio waveform visualization
- HTML Canvas - Preview rendering (being refactored to binary rendering)
Backend
- PostgreSQL - Primary database
- Drizzle ORM - Type-safe database access
- Redis - Caching and rate limiting
- Better Auth - Authentication
Build tools
- Turborepo - Monorepo build system
- Bun - Fast JavaScript runtime and package manager
- Biome - Fast linter and formatter
- Docker - Containerization
Key design patterns
Singleton pattern
The EditorCore uses the singleton pattern to ensure a single source of truth for editor state across the application.Manager pattern
Each domain (timeline, playback, media) has its own manager that encapsulates the logic and state for that domain.Command pattern
Undo/redo functionality is implemented using the command pattern, where each operation is encapsulated in a command object.Observer pattern
Managers use EventEmitter3 to notify subscribers of state changes, allowing React components to re-render when relevant state updates.Repository pattern
Database access is abstracted through repository functions insrc/lib/db/, keeping database logic separate from business logic.
Privacy-first architecture
OpenCut is designed with privacy as a core principle:- Local-first - Video processing happens in the browser using FFmpeg.wasm
- No server-side rendering - Videos never leave the user’s device
- Optional cloud features - Transcription and other cloud features are opt-in
- Minimal analytics - Only anonymized, non-invasive analytics via Databuddy
Preview system refactor
Current approach:- Renders preview using HTML/CSS/Canvas
- Inconsistent with export output
- Limited performance and quality
- Binary rendering engine
- Consistent preview and export
- Better performance and quality
- More accurate representation of final output
Next steps
Contributing
Learn how to contribute to OpenCut
Testing
Understand the testing approach