ucsandman/Magnetic

🧲 Free, open-source Final Cut Pro-style video editor for Windows (MIT). Magnetic timeline, edit-by-transcript, silence removal, keyframes, smart-render export.

2

stars

100

commits

TypeScript

primary language

Aug 14, 2026

updated

electron
ffmpeg
final-cut-pro
magnetic-timeline
nle
react
typescript
video-editor
webcodecs
webgl2
whisper
windows
Browse cluster: Electron desktop music players β†’

README

🧲 Magnetic

A free, open-source, Final Cut Pro-style non-linear video editor for Windows.

License: MIT Platform Electron React TypeScript

Magnetic timeline Β· WebCodecs/WebGL2 playback Β· edit-by-transcript Β· auto silence removal Β· keyframe animation Β· smart-render export. Built for long-form creator workflows β€” multi-hour recordings play with a flat memory footprint and export at disk speed.

Magnetic β€” blade, ripple delete, and the gap closes itself

Portfolio project. The design is an homage to Apple's Final Cut Pro β€” the magnetic timeline, three-panel layout, JKL transport, and edit grammar are modeled on it as the design reference. Not affiliated with Apple; no Apple trademarks are used and "Magnetic" is an original product name.

New to Magnetic? Start with the User Guide β€” a five-minute first edit, the magnetic-timeline workflow, edit-by-transcript, and troubleshooting.

✨ Features

Organize & import

  • Library model β€” Libraries β†’ Events β†’ Projects; import copies media into the .mglib bundle (hardlinks instead of copies when source and library share a volume)
  • Filmstrip browser β€” hover-skim filmstrips, favorite/reject ratings, text search, background thumbnail/waveform generation
  • Missing-media relink β€” assets whose file vanished get an alert badge; relink accepts a replacement whose duration matches Β±1 frame

Edit

  • Magnetic timeline β€” spine + connected clips, lanes, gap clips, snapping, zoom, skimming; clips never overlap, edits ripple; minimap strip with drag-to-navigate when the sequence is wider than one screen, and the view pages along with the playhead during playback
  • Edit grammar β€” append E, insert W, connect Q, overwrite D, ripple delete, lift; select A / blade B / trim T tools; trim ripples, edit points roll, clip bodies slip; drag-rearrange with magnetic collision; full undo/redo
  • Viewer β€” JKL transport (tap again for faster), frame-accurate stepping (←/β†’, Β±10 with Shift), in/out marks with play-the-marked-range (/), source/sequence timecode you can click and type to seek, loop playback (Ctrl+L), fullscreen review (Shift+F), live audio meter, transport buttons in both source and sequence modes, and Space plays the browser selection when nothing else is up
  • Review grid β€” watch up to 9 selected clips side by side (browser "Watch N" button); click a cell for its audio, double-click to open it in the viewer
  • Resizable layout β€” drag the panel splitters (double-click one to reset it); Reset Layout button restores the defaults
  • Clipboard β€” copy/paste/duplicate clips (Ctrl+C/V/D), paste-connect (Ctrl+Shift+V), paste attributes incl. keyframes (Ctrl+Alt+V); one undo step per paste
  • Keyframe animation β€” animate transform, opacity, and color params from the Inspector (linear/smooth easing); diamonds on timeline clips; WYSIWYG in export
  • Markers β€” color-coded notes (M at the playhead) that ride the media through blades, trims, and moves; ruler diamonds seek on click, the Inspector edits note/color, Ctrl+;/Ctrl+' navigate; agents drop and read them too
  • Transitions β€” cross dissolve, wipe left/right, fade-to-black
  • Titles β€” free text + 3 presets, rendered as live canvas layers
  • Color board β€” exposure / contrast / saturation / temperature per clip

Playback & long-form

  • Sequence playback β€” WebCodecs decode β†’ WebGL2 compositor; per-clip transforms (position/scale/rotation/opacity); AudioContext-clocked A/V sync
  • Multi-GB clips β€” streaming demux fetches samples on demand with bounded memory; a 10.85 GB, 4.2-hour clip plays video + audio with a flat ~87 MB renderer heap
  • Windowed audio β€” long clips stream 10-second audio windows double-buffered ahead of the clock instead of decoding whole files

Audio & transcript

  • Audio β€” mixing, per-clip volume/pan, fade in/out
  • Roles β€” tag clips Dialogue / Music / SFX (looped beds auto-tag as music); role stripes on the timeline, Dia/Mus/SFX mute buttons in the toolbar silence a whole role across playback and export
  • Loudness normalization β€” one click measures a clip's source loudness (ffmpeg EBU R128, cached) and sets its volume to the βˆ’14 LUFS streaming standard; agents can propose it for every dialogue clip at once
  • Auto-ducking β€” "Duck under dialogue" dips a music bed βˆ’12 dB wherever speech is detected (ramped, merged across short pauses so it never pumps); dips draw as darker bands on the bed and apply identically in live playback, offline render, and smart-render export
  • Detach audio & split edits β€” pull audio onto its own lane and trim its edges independently for J-cuts and L-cuts
  • Loop to fill β€” loop a connected music bed to the end of the spine in one action; seam ticks on the timeline, whole-bed fade envelope, one undo step
  • Edit-by-transcript β€” local whisper.cpp transcription, word-click seek, select-words-and-cut (button or Delete), filler-word removal, transcript search; agents get the same power as a cut_words MCP tool that matches a spoken quote and proposes the cut
  • Auto silence removal β€” background audio analysis, tunable dead-air detection with timeline preview, one-click jump-cuts, one-step undo
  • Rough Cut β€” one button merges silence + filler detection into a single pass (one aggressiveness slider) and ghost-renders it before anything commits: red hatched strikethrough over the cuts, a green preview strip showing the result, Accept lands it as one undo step, Discard leaves zero history; then review each AI-badged cut on the ruler and reject individual cuts spell-checker style. Imported footage gets hands-off draft cards ("2 cuts Β· 4.1s tighter") β€” one click appends and proposes
  • Copilot β€” chat with Claude that answers and edits: it sees the clips, detected dead air, and transcript, and performs edits (blade, ripple delete, trim, roll, slip, move, transitions) against a working copy only β€” every batch previews as a ghost diff with a per-change checklist (dependent changes link into one decision), a scrubbable before/after with real rendered frames, applies as one undo step on Accept (partial accepts replay transactionally), and vanishes without a trace on Discard; accepted AI edits leave session-only attribution (clip dot + "Edited by Copilot" in the Inspector). There is no export tool, and every proposed sequence passes the timeline's invariant guard. Two providers, picked in the Copilot tab: your Claude subscription via a locally installed, signed-in Claude Code CLI (auto-selected when detected; each turn spawns the CLI as a fresh process β€” total turn time depends mostly on the model, not the spawn), or your own Anthropic API key (app settings, never logged) β€” with Rough Cut, the human+agent co-editing roadmap in action (docs/agent-copilot-vision.md)
  • Agent Access (MCP) β€” Magnetic is an MCP server: Claude Code or any MCP client connects over loopback (claude mcp add magnetic -- node scripts/magnetic-mcp.mjs), reads the open timeline (clips, roles, markers, transcript), and proposes edits that arrive as the same ghost-diff preview with a timeline banner β€” including the high-level cut_words (quote β†’ cut), duck_music, and normalize_loudness tools. Nothing applies until you Accept, there is no export tool over MCP, the sidebar toggle severs all connections instantly (token-protected, 127.0.0.1 only), and requests arriving mid-drag queue until your gesture finishes
  • Voice cleanup β€” one-click background denoise (ffmpeg FFT) per asset or as part of Rough Cut; playback and export automatically prefer the cleaned track, originals untouched
  • Flow score β€” every accepted AI pass grades itself (residual dead air, untransitioned jump cuts, slivers): a colored score chip plus clickable orange flags on the ruler; the Copilot runs the same check on its working copy before proposing
  • Captions β€” burned-in captions derived live from the transcript (pop-in / karaoke / block presets); SRT/VTT sidecar export

Finish & export

  • Export β€” H.264/AAC MP4 at 1080p/720p/source via bundled ffmpeg, with progress and cancel; WYSIWYG (exported pixels match the live compositor)
  • Smart-render export β€” when the video track is a single, possibly trimmed, visually untouched asset, export stream-copies the H.264 bitstream and renders only the mixed audio: a 6-hour VOD exports at roughly disk speed, bit-identical video
  • Keyboard-shortcut overlay β€” Shift+? lists every live binding
  • NSIS installer β€” npm run package, binaries bundled

Explicitly out of scope: multicam, color wheels/curves/scopes, proxy workflow (preview proxies for non-decodable codecs only), compound clips, 360Β°/HDR, plugins, Motion templates, ProRes encode, object tracking, collaboration.

πŸ–₯️ Requirements

  • Windows 10/11 x64
  • Node.js 24+, npm 10+

πŸš€ Setup

# 1. Install dependencies
npm install

# 2. Fetch native binaries (ffmpeg, ffprobe, whisper.cpp, base.en model)
#    into resources/bin/ β€” pinned URLs, sha256-verified, idempotent
npm run fetch-binaries

# 3. Generate test media into fixtures/ (uses the fetched ffmpeg + Windows SAPI TTS)
npm run fixtures

Development

npm run dev          # start electron-vite dev server + app (HMR)

Build & test

npm run typecheck    # tsc across main, preload, renderer, shared, e2e
npm run lint         # eslint
npm test             # vitest unit tests
npm run build        # electron-vite production build -> out/
npm run test:e2e     # Playwright Electron E2E (requires `npm run build` first)
npm run package      # NSIS installer + win-unpacked -> dist/ (bundles resources/bin)

The installer bundles ffmpeg/ffprobe/whisper and the base.en model (~250 MB of binaries), so the packaged app has no first-run download. e2e/packaged.spec.ts boots dist/win-unpacked/Magnetic.exe and verifies the bundled binaries resolve; it is skipped until npm run package has produced that output.

Scripts

ScriptWhat it does
devRun the app in development with HMR
buildProduction build to out/
typecheckTypecheck node (main/preload/shared/e2e) and web (renderer) projects
lintESLint over the repo
testVitest unit tests (src/**/*.test.ts)
test:e2ePlaywright _electron E2E suite against the built app
packageelectron-builder NSIS installer into dist/
fetch-binariesDownload + sha256-verify ffmpeg/ffprobe/whisper/model into resources/bin/
fixturesGenerate deterministic test media into fixtures/
formatPrettier write

⌨️ Keyboard shortcuts

Generated from the live shortcut registry (scripts/dump-shortcuts.mjs); Shift+? in the app shows the same list. Shortcuts are suppressed while typing in text fields. J/K/L and Space are contextual β€” they drive the source viewer when it has a clip open, otherwise the timeline.

ShortcutAction
SpacePlay / pause (viewer or sequence)
JViewer: play reverse (again: faster) Β· Timeline: pause
KPause
LPlay forward (again: faster)
← / β†’Step back / forward one frame
Shift+← / Shift+β†’Step back / forward 10 frames
Home / EndGo to the start / end (viewer or timeline)
↑ / ↓Jump to the previous / next edit point
I / O / XMark in / mark out / clear in-out points
/Play the marked range (in to out)
Ctrl+LLoop playback on / off
Shift+FFullscreen viewer
EAppend browser selection to the spine
WInsert browser selection at the playhead
QConnect browser selection at the playhead
DOverwrite at the playhead with browser selection
DeleteRipple delete the selected clips
Shift+DeleteLift the selected clips, leaving a gap
ASelect tool
BBlade tool
TTrim tool (edges ripple, edit points roll, body slips)
Ctrl+BBlade at the playhead (selected clips, or the clip under it)
Ctrl+TAdd a 1 s cross dissolve at the edit point nearest the playhead
MAdd a marker at the playhead
Ctrl+; / Ctrl+'Jump to the previous / next marker
Ctrl+Z / Ctrl+Shift+ZUndo / redo
NToggle snapping
SToggle skimming
= / -Zoom the timeline in / out
Shift+ZZoom the timeline to fit the sequence
Ctrl+4Show or hide the Inspector
Ctrl+EExport the sequence as a movie
Ctrl+Shift+TShow or hide the timeline transcript
Ctrl+Shift+DShow or hide binary diagnostics
Shift+?Show the keyboard shortcut overlay
EscapeReturn focus to browser Β· close the clip grid Β· exit fullscreen

πŸ—οΈ Architecture

flowchart TB
    subgraph rendererBox["Renderer (src/renderer)"]
        ui["React UI<br/>browser Β· viewer Β· timeline Β· inspector"]
        store["zustand timeline store<br/>undo / redo"]
        engine["Playback engine<br/>streaming WebCodecs demux + decode β†’ WebGL2 compositor<br/>AudioContext-clocked sync"]
    end
    subgraph sharedBox["Shared (src/shared)"]
        kernel["Magnetic timeline kernel<br/>pure TS Β· immutable Sequence<br/>every op returns its inverse"]
    end
    subgraph mainBox["Main process (src/main)"]
        ipc["IPC<br/>zod-validated handlers"]
        lib["LibraryStore<br/>.mglib JSON + media"]
        jobs["JobQueue<br/>filmstrip Β· waveform Β· proxy<br/>transcribe Β· envelope"]
        exp["Export sink<br/>rawvideo pipe β†’ ffmpeg<br/>or smart-render stream-copy"]
    end
    ui --> store
    store --> kernel
    store --> engine
    ui -->|window.api preload bridge| ipc
    ipc --> lib
    ipc --> jobs
    ipc --> exp
  • src/shared/timeline/ β€” the magnetic-timeline kernel: pure functions over an immutable Sequence; clip positions are derived by summation so overlaps are unrepresentable; every op returns its inverse for undo
  • src/main/ β€” Electron main process: window, menu, library persistence, background jobs, export (including the smart-render planner); every IPC handler zod-validates its payload
  • src/preload/ β€” contextBridge bridge exposing the typed window.api
  • src/renderer/ β€” React UI (dark FCP-style 3-panel shell), zustand state, and the playback engine (streaming WebCodecs demux + decode, WebGL2 compositing, AudioContext-clocked sync)
  • scripts/ β€” binary fetcher, fixture generator, shortcut-table dump
  • e2e/ β€” Playwright Electron tests; resources/bin/ and fixtures/ are gitignored artifacts

The visual language is documented in DESIGN.md (tokens, type scale, spacing, motion β€” Apple dark-mode chrome where the footage is the hero) and PRODUCT.md (register, users, brand personality).

Renderer security: contextIsolation: true, nodeIntegration: false, sandbox: true, CSP set in index.html, no remote URLs; library files are served over a custom mfile:// protocol scoped to the library, except video/audio playback, which goes through a token-guarded loopback HTTP server (src/main/media-server.ts) because Chromium's media pipeline cannot seek large files through Electron custom-protocol responses. Ctrl+Shift+D toggles a diagnostics overlay that spawn-verifies the bundled binaries.

πŸ“Έ Screenshots

Filmstrip browser Filmstrip browser β€” hover-skim, ratings, background jobsViewer Viewer β€” JKL transport, frame stepping, in/out marks
Edit-by-transcript Edit-by-transcript β€” word-click seek, delete text to cutExport dialog Export β€” H.264/AAC MP4 with progress + cancel
Shortcut overlay Shift+? overlay β€” every live binding500-clip timeline 500-clip timeline β€” 0.7 ms median draw

Known limitations

  • The "Explicitly out of scope" list above (multicam, scopes, compound clips, …) is deferred by design
  • Reverse playback: J in the timeline pauses; true reverse playback is viewer-only
  • 4K media plays at degraded fidelity (frame drops accepted; no proxy editing pipeline)
  • Codecs WebCodecs cannot decode natively (e.g. ProRes) play through an auto-generated H.264 preview proxy; export always re-renders from source
  • Export encodes H.264/AAC MP4 only; no ProRes/HEVC encode
  • Auto-transcribe skips assets over 30 minutes (manual transcribe still works at any length)
  • Windows-only: packaging, binary fetching, and E2E are wired for win32

πŸ“„ License

MIT β€” free for everyone, for any use.

The fetch-binaries script downloads third-party tools that are not part of this repository and carry their own licenses: FFmpeg/FFprobe (GPL builds from gyan.dev), whisper.cpp (MIT), and the Whisper base.en model (MIT).

Support

If my tools save you time, you can support my work here:

Sponsor on GitHub Buy Me a Coffee

Contributors

ucsandman

100 commits

ucsandman/Magnetic

🧲 Free, open-source Final Cut Pro-style video editor for Windows (MIT). Magnetic timeline, edit-by-transcript, silence removal, keyframes, smart-render export.

2

stars

100

commits

TypeScript

primary language

Aug 14, 2026

updated

electron
ffmpeg
final-cut-pro
magnetic-timeline
nle
react
typescript
video-editor
webcodecs
webgl2
whisper
windows
Browse cluster: Electron desktop music players β†’

README

🧲 Magnetic

A free, open-source, Final Cut Pro-style non-linear video editor for Windows.

License: MIT Platform Electron React TypeScript

Magnetic timeline Β· WebCodecs/WebGL2 playback Β· edit-by-transcript Β· auto silence removal Β· keyframe animation Β· smart-render export. Built for long-form creator workflows β€” multi-hour recordings play with a flat memory footprint and export at disk speed.

Magnetic β€” blade, ripple delete, and the gap closes itself

Portfolio project. The design is an homage to Apple's Final Cut Pro β€” the magnetic timeline, three-panel layout, JKL transport, and edit grammar are modeled on it as the design reference. Not affiliated with Apple; no Apple trademarks are used and "Magnetic" is an original product name.

New to Magnetic? Start with the User Guide β€” a five-minute first edit, the magnetic-timeline workflow, edit-by-transcript, and troubleshooting.

✨ Features

Organize & import

  • Library model β€” Libraries β†’ Events β†’ Projects; import copies media into the .mglib bundle (hardlinks instead of copies when source and library share a volume)
  • Filmstrip browser β€” hover-skim filmstrips, favorite/reject ratings, text search, background thumbnail/waveform generation
  • Missing-media relink β€” assets whose file vanished get an alert badge; relink accepts a replacement whose duration matches Β±1 frame

Edit

  • Magnetic timeline β€” spine + connected clips, lanes, gap clips, snapping, zoom, skimming; clips never overlap, edits ripple; minimap strip with drag-to-navigate when the sequence is wider than one screen, and the view pages along with the playhead during playback
  • Edit grammar β€” append E, insert W, connect Q, overwrite D, ripple delete, lift; select A / blade B / trim T tools; trim ripples, edit points roll, clip bodies slip; drag-rearrange with magnetic collision; full undo/redo
  • Viewer β€” JKL transport (tap again for faster), frame-accurate stepping (←/β†’, Β±10 with Shift), in/out marks with play-the-marked-range (/), source/sequence timecode you can click and type to seek, loop playback (Ctrl+L), fullscreen review (Shift+F), live audio meter, transport buttons in both source and sequence modes, and Space plays the browser selection when nothing else is up
  • Review grid β€” watch up to 9 selected clips side by side (browser "Watch N" button); click a cell for its audio, double-click to open it in the viewer
  • Resizable layout β€” drag the panel splitters (double-click one to reset it); Reset Layout button restores the defaults
  • Clipboard β€” copy/paste/duplicate clips (Ctrl+C/V/D), paste-connect (Ctrl+Shift+V), paste attributes incl. keyframes (Ctrl+Alt+V); one undo step per paste
  • Keyframe animation β€” animate transform, opacity, and color params from the Inspector (linear/smooth easing); diamonds on timeline clips; WYSIWYG in export
  • Markers β€” color-coded notes (M at the playhead) that ride the media through blades, trims, and moves; ruler diamonds seek on click, the Inspector edits note/color, Ctrl+;/Ctrl+' navigate; agents drop and read them too
  • Transitions β€” cross dissolve, wipe left/right, fade-to-black
  • Titles β€” free text + 3 presets, rendered as live canvas layers
  • Color board β€” exposure / contrast / saturation / temperature per clip

Playback & long-form

  • Sequence playback β€” WebCodecs decode β†’ WebGL2 compositor; per-clip transforms (position/scale/rotation/opacity); AudioContext-clocked A/V sync
  • Multi-GB clips β€” streaming demux fetches samples on demand with bounded memory; a 10.85 GB, 4.2-hour clip plays video + audio with a flat ~87 MB renderer heap
  • Windowed audio β€” long clips stream 10-second audio windows double-buffered ahead of the clock instead of decoding whole files

Audio & transcript

  • Audio β€” mixing, per-clip volume/pan, fade in/out
  • Roles β€” tag clips Dialogue / Music / SFX (looped beds auto-tag as music); role stripes on the timeline, Dia/Mus/SFX mute buttons in the toolbar silence a whole role across playback and export
  • Loudness normalization β€” one click measures a clip's source loudness (ffmpeg EBU R128, cached) and sets its volume to the βˆ’14 LUFS streaming standard; agents can propose it for every dialogue clip at once
  • Auto-ducking β€” "Duck under dialogue" dips a music bed βˆ’12 dB wherever speech is detected (ramped, merged across short pauses so it never pumps); dips draw as darker bands on the bed and apply identically in live playback, offline render, and smart-render export
  • Detach audio & split edits β€” pull audio onto its own lane and trim its edges independently for J-cuts and L-cuts
  • Loop to fill β€” loop a connected music bed to the end of the spine in one action; seam ticks on the timeline, whole-bed fade envelope, one undo step
  • Edit-by-transcript β€” local whisper.cpp transcription, word-click seek, select-words-and-cut (button or Delete), filler-word removal, transcript search; agents get the same power as a cut_words MCP tool that matches a spoken quote and proposes the cut
  • Auto silence removal β€” background audio analysis, tunable dead-air detection with timeline preview, one-click jump-cuts, one-step undo
  • Rough Cut β€” one button merges silence + filler detection into a single pass (one aggressiveness slider) and ghost-renders it before anything commits: red hatched strikethrough over the cuts, a green preview strip showing the result, Accept lands it as one undo step, Discard leaves zero history; then review each AI-badged cut on the ruler and reject individual cuts spell-checker style. Imported footage gets hands-off draft cards ("2 cuts Β· 4.1s tighter") β€” one click appends and proposes
  • Copilot β€” chat with Claude that answers and edits: it sees the clips, detected dead air, and transcript, and performs edits (blade, ripple delete, trim, roll, slip, move, transitions) against a working copy only β€” every batch previews as a ghost diff with a per-change checklist (dependent changes link into one decision), a scrubbable before/after with real rendered frames, applies as one undo step on Accept (partial accepts replay transactionally), and vanishes without a trace on Discard; accepted AI edits leave session-only attribution (clip dot + "Edited by Copilot" in the Inspector). There is no export tool, and every proposed sequence passes the timeline's invariant guard. Two providers, picked in the Copilot tab: your Claude subscription via a locally installed, signed-in Claude Code CLI (auto-selected when detected; each turn spawns the CLI as a fresh process β€” total turn time depends mostly on the model, not the spawn), or your own Anthropic API key (app settings, never logged) β€” with Rough Cut, the human+agent co-editing roadmap in action (docs/agent-copilot-vision.md)
  • Agent Access (MCP) β€” Magnetic is an MCP server: Claude Code or any MCP client connects over loopback (claude mcp add magnetic -- node scripts/magnetic-mcp.mjs), reads the open timeline (clips, roles, markers, transcript), and proposes edits that arrive as the same ghost-diff preview with a timeline banner β€” including the high-level cut_words (quote β†’ cut), duck_music, and normalize_loudness tools. Nothing applies until you Accept, there is no export tool over MCP, the sidebar toggle severs all connections instantly (token-protected, 127.0.0.1 only), and requests arriving mid-drag queue until your gesture finishes
  • Voice cleanup β€” one-click background denoise (ffmpeg FFT) per asset or as part of Rough Cut; playback and export automatically prefer the cleaned track, originals untouched
  • Flow score β€” every accepted AI pass grades itself (residual dead air, untransitioned jump cuts, slivers): a colored score chip plus clickable orange flags on the ruler; the Copilot runs the same check on its working copy before proposing
  • Captions β€” burned-in captions derived live from the transcript (pop-in / karaoke / block presets); SRT/VTT sidecar export

Finish & export

  • Export β€” H.264/AAC MP4 at 1080p/720p/source via bundled ffmpeg, with progress and cancel; WYSIWYG (exported pixels match the live compositor)
  • Smart-render export β€” when the video track is a single, possibly trimmed, visually untouched asset, export stream-copies the H.264 bitstream and renders only the mixed audio: a 6-hour VOD exports at roughly disk speed, bit-identical video
  • Keyboard-shortcut overlay β€” Shift+? lists every live binding
  • NSIS installer β€” npm run package, binaries bundled

Explicitly out of scope: multicam, color wheels/curves/scopes, proxy workflow (preview proxies for non-decodable codecs only), compound clips, 360Β°/HDR, plugins, Motion templates, ProRes encode, object tracking, collaboration.

πŸ–₯️ Requirements

  • Windows 10/11 x64
  • Node.js 24+, npm 10+

πŸš€ Setup

# 1. Install dependencies
npm install

# 2. Fetch native binaries (ffmpeg, ffprobe, whisper.cpp, base.en model)
#    into resources/bin/ β€” pinned URLs, sha256-verified, idempotent
npm run fetch-binaries

# 3. Generate test media into fixtures/ (uses the fetched ffmpeg + Windows SAPI TTS)
npm run fixtures

Development

npm run dev          # start electron-vite dev server + app (HMR)

Build & test

npm run typecheck    # tsc across main, preload, renderer, shared, e2e
npm run lint         # eslint
npm test             # vitest unit tests
npm run build        # electron-vite production build -> out/
npm run test:e2e     # Playwright Electron E2E (requires `npm run build` first)
npm run package      # NSIS installer + win-unpacked -> dist/ (bundles resources/bin)

The installer bundles ffmpeg/ffprobe/whisper and the base.en model (~250 MB of binaries), so the packaged app has no first-run download. e2e/packaged.spec.ts boots dist/win-unpacked/Magnetic.exe and verifies the bundled binaries resolve; it is skipped until npm run package has produced that output.

Scripts

ScriptWhat it does
devRun the app in development with HMR
buildProduction build to out/
typecheckTypecheck node (main/preload/shared/e2e) and web (renderer) projects
lintESLint over the repo
testVitest unit tests (src/**/*.test.ts)
test:e2ePlaywright _electron E2E suite against the built app
packageelectron-builder NSIS installer into dist/
fetch-binariesDownload + sha256-verify ffmpeg/ffprobe/whisper/model into resources/bin/
fixturesGenerate deterministic test media into fixtures/
formatPrettier write

⌨️ Keyboard shortcuts

Generated from the live shortcut registry (scripts/dump-shortcuts.mjs); Shift+? in the app shows the same list. Shortcuts are suppressed while typing in text fields. J/K/L and Space are contextual β€” they drive the source viewer when it has a clip open, otherwise the timeline.

ShortcutAction
SpacePlay / pause (viewer or sequence)
JViewer: play reverse (again: faster) Β· Timeline: pause
KPause
LPlay forward (again: faster)
← / β†’Step back / forward one frame
Shift+← / Shift+β†’Step back / forward 10 frames
Home / EndGo to the start / end (viewer or timeline)
↑ / ↓Jump to the previous / next edit point
I / O / XMark in / mark out / clear in-out points
/Play the marked range (in to out)
Ctrl+LLoop playback on / off
Shift+FFullscreen viewer
EAppend browser selection to the spine
WInsert browser selection at the playhead
QConnect browser selection at the playhead
DOverwrite at the playhead with browser selection
DeleteRipple delete the selected clips
Shift+DeleteLift the selected clips, leaving a gap
ASelect tool
BBlade tool
TTrim tool (edges ripple, edit points roll, body slips)
Ctrl+BBlade at the playhead (selected clips, or the clip under it)
Ctrl+TAdd a 1 s cross dissolve at the edit point nearest the playhead
MAdd a marker at the playhead
Ctrl+; / Ctrl+'Jump to the previous / next marker
Ctrl+Z / Ctrl+Shift+ZUndo / redo
NToggle snapping
SToggle skimming
= / -Zoom the timeline in / out
Shift+ZZoom the timeline to fit the sequence
Ctrl+4Show or hide the Inspector
Ctrl+EExport the sequence as a movie
Ctrl+Shift+TShow or hide the timeline transcript
Ctrl+Shift+DShow or hide binary diagnostics
Shift+?Show the keyboard shortcut overlay
EscapeReturn focus to browser Β· close the clip grid Β· exit fullscreen

πŸ—οΈ Architecture

flowchart TB
    subgraph rendererBox["Renderer (src/renderer)"]
        ui["React UI<br/>browser Β· viewer Β· timeline Β· inspector"]
        store["zustand timeline store<br/>undo / redo"]
        engine["Playback engine<br/>streaming WebCodecs demux + decode β†’ WebGL2 compositor<br/>AudioContext-clocked sync"]
    end
    subgraph sharedBox["Shared (src/shared)"]
        kernel["Magnetic timeline kernel<br/>pure TS Β· immutable Sequence<br/>every op returns its inverse"]
    end
    subgraph mainBox["Main process (src/main)"]
        ipc["IPC<br/>zod-validated handlers"]
        lib["LibraryStore<br/>.mglib JSON + media"]
        jobs["JobQueue<br/>filmstrip Β· waveform Β· proxy<br/>transcribe Β· envelope"]
        exp["Export sink<br/>rawvideo pipe β†’ ffmpeg<br/>or smart-render stream-copy"]
    end
    ui --> store
    store --> kernel
    store --> engine
    ui -->|window.api preload bridge| ipc
    ipc --> lib
    ipc --> jobs
    ipc --> exp
  • src/shared/timeline/ β€” the magnetic-timeline kernel: pure functions over an immutable Sequence; clip positions are derived by summation so overlaps are unrepresentable; every op returns its inverse for undo
  • src/main/ β€” Electron main process: window, menu, library persistence, background jobs, export (including the smart-render planner); every IPC handler zod-validates its payload
  • src/preload/ β€” contextBridge bridge exposing the typed window.api
  • src/renderer/ β€” React UI (dark FCP-style 3-panel shell), zustand state, and the playback engine (streaming WebCodecs demux + decode, WebGL2 compositing, AudioContext-clocked sync)
  • scripts/ β€” binary fetcher, fixture generator, shortcut-table dump
  • e2e/ β€” Playwright Electron tests; resources/bin/ and fixtures/ are gitignored artifacts

The visual language is documented in DESIGN.md (tokens, type scale, spacing, motion β€” Apple dark-mode chrome where the footage is the hero) and PRODUCT.md (register, users, brand personality).

Renderer security: contextIsolation: true, nodeIntegration: false, sandbox: true, CSP set in index.html, no remote URLs; library files are served over a custom mfile:// protocol scoped to the library, except video/audio playback, which goes through a token-guarded loopback HTTP server (src/main/media-server.ts) because Chromium's media pipeline cannot seek large files through Electron custom-protocol responses. Ctrl+Shift+D toggles a diagnostics overlay that spawn-verifies the bundled binaries.

πŸ“Έ Screenshots

Filmstrip browser Filmstrip browser β€” hover-skim, ratings, background jobsViewer Viewer β€” JKL transport, frame stepping, in/out marks
Edit-by-transcript Edit-by-transcript β€” word-click seek, delete text to cutExport dialog Export β€” H.264/AAC MP4 with progress + cancel
Shortcut overlay Shift+? overlay β€” every live binding500-clip timeline 500-clip timeline β€” 0.7 ms median draw

Known limitations

  • The "Explicitly out of scope" list above (multicam, scopes, compound clips, …) is deferred by design
  • Reverse playback: J in the timeline pauses; true reverse playback is viewer-only
  • 4K media plays at degraded fidelity (frame drops accepted; no proxy editing pipeline)
  • Codecs WebCodecs cannot decode natively (e.g. ProRes) play through an auto-generated H.264 preview proxy; export always re-renders from source
  • Export encodes H.264/AAC MP4 only; no ProRes/HEVC encode
  • Auto-transcribe skips assets over 30 minutes (manual transcribe still works at any length)
  • Windows-only: packaging, binary fetching, and E2E are wired for win32

πŸ“„ License

MIT β€” free for everyone, for any use.

The fetch-binaries script downloads third-party tools that are not part of this repository and carry their own licenses: FFmpeg/FFprobe (GPL builds from gyan.dev), whisper.cpp (MIT), and the Whisper base.en model (MIT).

Support

If my tools save you time, you can support my work here:

Sponsor on GitHub Buy Me a Coffee

Contributors

ucsandman

100 commits

Languages

TypeScript

94.6%

CSS

2.3%

JavaScript

2.1%