A fully local, in-browser video editor. Nothing is uploaded — everything (media, rendering, export) runs on your device.
- Import video / image / audio, drag onto a multi-track timeline
- Trim, split, duplicate, and detach audio from clips
- Text overlays with fonts, color, stroke, background, and drag-to-position
- Transform (scale/rotate/position), filters (brightness/contrast/saturation/blur), and per-clip volume/fades
- Undo/redo, save/load project as JSON
- Export to
.webmviacanvas.captureStream()+MediaRecorder - Responsive: desktop three-pane layout, mobile slide-up sheets
React + TypeScript, Zustand for state, Vite for dev/build. No backend — the whole app is static files.
npm install
npm run dev # start the dev server
npm run build # type-check + production build to dist/
npm run preview # serve the production build locallynpm run build produces a single self-contained dist/index.html (JS/CSS
inlined via vite-plugin-singlefile). Asset URLs are built for deployment
at https://squidly1408.github.io/apps/Cutroom/ (see base in
vite.config.ts) — upload the contents of dist/ to that path. npm run preview serves it locally from that same subpath for a pre-upload check.
Deploying somewhere else? Change the base constant at the top of
vite.config.ts to match — './' makes dist/index.html fully portable
(openable directly via file://, or served from any path).
src/
types.ts Clip / Asset / Track / project-file types
store/ Zustand stores: project, assets, undo/redo history, UI (toasts/sheets)
lib/ Pure helpers: clip math, snapping, file loading, project save/load
audio/audioEngine.ts WebAudio graph (one gain node per media asset)
render/canvasRenderer.ts Draws the composited frame + selection handles onto <canvas>
playback/ Play/pause/seek/scrub, driven by requestAnimationFrame
export/ canvas.captureStream() + MediaRecorder export pipeline
components/ UI: TopBar, MediaLibrary, Preview, Inspector, Timeline, modals
styles/global.css App-wide styles (dark theme, responsive breakpoints)
Media assets (and the WebAudio/<video>/<audio> elements backing them)
are kept out of undo/redo history and out of saved project files — a saved
project only stores clip layout; media is re-linked by name when you reopen
a project and re-import the same files.
