
브라우저에서 실행되는 로컬 우선(Local-First) AI 비디오 편집기를 만들었습니다
요약
React 기반의 오픈 소스 로컬 우선(Local-First) AI 비디오 편집기인 Timeline Studio를 소개합니다. 서버 업로드 없이 브라우저 내에서 AI 워크플로우를 직접 실행하여 음성 생성, 오디오 전사, 피사체 분석 등을 수행합니다.
핵심 포인트
- 브라우저 내 직접 실행을 통한 로컬 우선 AI 워크플로우 구현
- 음성 해설 생성, 오디오 전사, 피사체 분석 등 핵심 기능 제공
- 타임라인을 단일 신뢰 원천(Source of Truth)으로 설계
- PWA 지원 및 지속적인 모델 캐싱을 통한 성능 최적화
AI 비디오 편집기가 유용한 작업을 수행하기 전에 미디어를 서버에 업로드할 필요가 없다면 어떨까요?
그 질문이 바로 React로 구축된 오픈 소스 로컬 우선 (Local-First) 비디오 편집기인 Timeline Studio가 되었습니다. 이 도구는 핵심 AI 워크플로우 (AI workflows)가 브라우저에서 직접 실행되면서 음성 해설 (voiceovers) 생성, 오디오 전사 (transcribes audio), 피사체 분석, 말하는 초상화 (talking portraits) 제작, 그리고 완성된 비디오 내보내기를 수행합니다.
라이브 데모를 시도하거나, GitHub에서 소스 코드를 살펴보고,
을 시청할 수 있습니다.이 포스트는 브라우저가 데스크톱 편집기를 대체했다는 주장이 아닙니다. 그렇지 않습니다. 이것은 놀라울 정도로 큰 AI 미디어 파이프라인 (media pipeline)을 클라이언트로 옮기는 과정에서 무엇이 작동했고, 무엇이 예상치 못하게 어려워졌으며, 무엇을 배웠는지에 대한 실질적인 기록입니다.
제품 아이디어
첫 번째 버전은 단순한 이미지-음성 해설 (image-to-voiceover) 도구로 시작되었습니다. 사용자는 이미지를 업로드하고, 스크립트를 입력하고, 음성을 생성하고, 결과를 미리 본 다음, 비디오를 내보낼 수 있었습니다.
하지만 유용한 편집기에는 단순히 '해피 패스 (happy-path)' 생성 버튼 이상의 것이 필요합니다. 사용자는 클립을 이동하고, 나레이션을 겹치고, 자막을 편집하고, 오디오와 텍스트를 동기화하며, 음악을 추가하고, 실수를 복구하며, 내보내기 도구가 실제로 무엇을 생성할지 이해해야 합니다.
그 결과 프로젝트는 다음과 같은 기능을 갖춘 CapCut 스타일의 데스크톱 작업 공간으로 발전했습니다:
- 이미지, 비디오, 스티커, 자막, 소스 오디오, 음성 해설 및 음악 트랙;
- 클립 분할, 크기 조정, 이동, 복제, 잠금, 숨기기 및 삭제;
- 겹치는 음성 해설과 자막을 위한 자동 레인 (automatic lanes);
- 파형 인식 자막 (waveform-aware captions) 및 확대 가능한 타임라인;
- 브라우저 측 MP4/WebM 내보내기;
- 설치 가능한 PWA 동작 및 지속적인 모델 캐싱 (persistent model caching).
중요한 아키텍처 결정은 타임라인(timeline)을 신뢰할 수 있는 단일 원천(source of truth)으로 만드는 것이었습니다. AI 기능은 고립된 다운로드를 생성하지 않습니다. 대신 에디터 에셋(editor assets)과 타임라인 클립(timeline clips)을 생성하거나 업데이트합니다.
서로 다른 런타임(runtime)을 사용하는 브라우저 AI 스택
이 애플리케이션의 모든 부분을 잘 처리할 수 있는 단일 모델이나 런타임은 존재하지 않습니다. Timeline Studio는 여러 전문화된 경로를 사용합니다:
| 작업 | 모델/런타임 | 실행 |
|---|---|---|
| 중국어 및 다국어 TTS | Piper/VITS ONNX | WASM |
| ... |
모델은 지연 로딩(lazy-loaded)됩니다. 에디터를 연다고 해서 나중에 사용할 수도 있는 모든 신경망(neural network)을 즉시 다운로드해서는 안 됩니다.
예를 들어, 영어 음성 경로는 선택된 목소리가 필요할 때만 Kokoro를 가져옵니다:
const { KokoroTTS } = await import("kokoro-js");
const tts = await KokoroTTS.from_pretrained(
...
Piper는 다국어 음성 카탈로그를 처리하며, 중국어 텍스트는 병음(pinyin)을 인식하는 전용 경로를 거칩니다. 핵심적인 제품 규칙은 음성 생성에 실패했을 경우 이를 시각적으로 명확히 보여주어야 한다는 것입니다. 다른 화자로 조용히 대체(fallback)하는 것은 UI상으로는 성공한 것처럼 보이게 하면서 잘못된 결과를 생성할 수 있기 때문입니다.
워커(Workers)는 선택 사항이 아닙니다
모델 추론(inference), 오디오 디코딩(audio decoding), 프레임 분석(frame analysis), 그리고 내보내기(export)는 모두 메인 스레드(main thread)를 멈추게 할 수 있습니다. 진행 상황 보고(progress reporting)와 에디터의 응답성(responsiveness)을 확보하기 위해 작업을 웹 워커(Web Workers)로 옮기는 것이 필수적이었습니다.
비전 워커(vision worker)는 모델 인스턴스를 첫 로드 이후에도 활성 상태로 유지하며 분석 요청을 직렬화(serialize)합니다:
let detectorPromise = null;
let backgroundRemoverPromise = null;
let analysisQueue = Promise.resolve();
...
세션을 워밍업(warm) 상태로 유지하면 반복적인 작업 속도가 훨씬 빨라집니다. 선택된 워크로드를 직렬화하면 메모리 스파이크(memory spikes)와 실행 제공자(execution-provider) 간의 충돌도 방지할 수 있습니다. 이는 특히 여러 개의 큰 ONNX 그래프가 브라우저 리소스를 두고 경쟁할 때 매우 중요합니다.
비디오 비전은 시간적(temporal)이어야 합니다
비디오의 첫 번째 프레임에서 피사체 탐지(subject detection)를 실행하는 것은 쉽습니다. 하지만 피사체가 움직이는 즉시 그 결과는 틀린 것이 됩니다.
Timeline Studio는 적응형 간격(adaptive intervals)으로 비디오를 사전 분석(pre-analyzes)하고, 타임스탬프가 찍힌 YOLOS 기하 정보(geometry)와 MODNet 마스크(masks)를 저장합니다. 미리보기(Preview), 스마트 크롭(smart crop), 캡션 회피(caption avoidance), 배경 제거(background removal) 및 내보내기(export) 기능은 모두 현재 소스 시간의 분석 기록을 참조하여 처리됩니다.
이를 통해 재사용 가능한 시간적 트랙(temporal track)이 생성됩니다:
비디오 시간 ──► 0.0s ───── 0.8s ───── 1.6s ───── 2.4s
피사체 박스(subject box) A B C D
매트 마스크(matte mask) A' B' C' D'
더 긴 비디오의 경우, 추론 시간(inference time)과 메모리를 제한하기 위해 더 넓은 샘플링 간격을 사용합니다. 이는 일종의 타협안이지만, 클립 전체에 걸쳐 첫 프레임의 결과값을 그대로 고정해버리는 것보다는 훨씬 정직한 방식입니다.
WebGPU를 위한 LivePortrait 재작성
말하는 아바타(talking-avatar) 파이프라인은 이 프로젝트에서 가장 어려운 부분이었습니다.
JoyVASA는 음성 오디오를 동작으로 변환합니다. 그다음 LivePortrait가 외형 추출(appearance extraction), 동작 추출(motion extraction), 입술 리타겟팅(lip retargeting), 스티칭(stitching), 워핑(warping) 및 SPADE 생성을 통해 해당 동작을 소스 초상화에 적용합니다.
기존의 결합된 생성기(combined generator)에는 랭크-5(rank-5) 연산과 두 개의 5D GridSample 노드가 포함되어 있었습니다. 기존의 네이티브 Linux 플러그인은 브라우저에서 실행할 수 없었으며, 그래프가 ONNX Runtime Web의 WebGPU 실행 제공자(execution provider)와 직접적으로 호환되지 않았습니다.
저는 5D 샘플링을 배치 처리된 4D GridSample 연산과 깊이(depth)를 따른 선형 보간(linear interpolation)으로 재작성했습니다. 재작성된 그래프는 노드 수가 277개에서 316개로 늘어났지만, 브라우저에서 실행할 수 있게 되었습니다. 전체 512×512 출력을 원본 그래프와 비교했을 때 다음과 같은 결과가 나왔습니다:
- 최대 절대 오차(maximum absolute error):
7.8976e-6; - 평균 절대 오차(mean absolute error):
1.3853e-7; - 99.9 백분위 오차(99.9th-percentile error):
1.7881e-6.
이 수치들은 매우 중요했습니다. 핵심 신경망 연산자(neural-network operator)를 교체할 때 "비슷해 보인다"는 식의 검증 전략은 충분하지 않기 때문입니다.
이제 브라우저 파이프라인은 혼합 FP16(mixed-FP16) 256px 미리보기 단계와 혼합 FP16(mixed-FP16) 512px 품질 단계를 제공합니다. 이는 희소 신경 키프레임(sparse neural keyframes)을 렌더링하고, 인코딩을 위해 이를 보간하며, 프레임 사이의 재사용 가능한 외형 특징(appearance feature)을 GPU에 유지합니다.
아직 실시간은 아닙니다. 검증에 사용된 하드웨어에서는 512px 신경망 프레임(neural frames)이 여전히 비용이 많이 듭니다. 따라서 UI는 느린 작업이 즉각적으로 처리되는 것처럼 속이는 대신, 실제 진행 상황을 보고합니다.
대용량 아바타 번들 또한 Git 저장소에 저장되지 않습니다. 이는 50MB 단위로 분할되어 불변(immutable) 상태이며, 특정 리비전에 고정된 Hugging Face 모델 저장소에서 제공됩니다.
자동 자막에는 전사(transcription) 이상의 것이 필요합니다
Whisper는 텍스트와 타임스탬프(timestamps)를 반환하지만, 편집기 내부의 자막 품질은 정렬(alignment)에도 달려 있습니다.
현재의 자막 파이프라인은 WASM 워커(worker)에서 Whisper small q8를 사용합니다. Tiny 모델들이 더 빠르긴 했지만, 중국어 테스트에서 불안정한 환각(hallucinations) 현상이 발생했습니다. WebGPU 또한 이 워크로드에 대해 안정성이 떨어졌기 때문에, 해당 경로가 재검증될 때까지 자막은 의도적으로 WASM에 머물러 있습니다.
전사(transcription) 후에는 세그먼트 경계(segment boundaries)를 인근 소스 오디오 에너지(source-audio energy) 쪽으로 미세하게 조정합니다. 이를 통해 자막 바가 거친 청크 타임스탬프(chunk timestamps)를 맹목적으로 신뢰하는 대신 파형(waveform)과 일치하도록 만듭니다.
중국어 정제(cleanup)는 보수적이고 문맥을 인식(context-aware)하도록 설계되었습니다. 광범위한 재작성(rewriting)은 자막을 더 매끄럽게 읽히게 만들 수 있지만, 화자가 실제로 말한 내용을 변경할 위험이 있습니다. 이는 자동 전사 경로에서는 좋지 않은 트레이드오프(trade-off)입니다.
타임라인을 편집기처럼 느껴지게 만들기
AI 데모는 종종 추론(inference)에만 집중하고 상호작용 디자인(interaction design)을 과소평가합니다. 실제로 많은 작업은 일반적인 편집기 동작을 구현하는 데 투입되었습니다:
- 이전 결과물을 교체하는 대신 재생 헤드(playhead) 위치에 생성된 보이스오버(voiceover)를 삽입하기;
- 연결된 자막을 보이스오버와 함께 이동하기;
- 클립이 겹칠 때 추가 레인(lane)을 생성하고 나중에 이를 압축하기;
- 모든 줌(zoom) 레벨에서 클립의 기하학적 구조(geometry)를 시간상 정확하게 유지하기;
- 매우 짧은 클립의 경우 시각적으로 늘리는 대신 투명한 히트 타겟(hit target) 사용하기;
- 사용자가 텍스트 필드를 편집하는 동안에는 Delete 및 Backspace 키가 작동하지 않도록 클립 범위(clip-scoped)로 제한하기;
- 미리보기 및 내보내기 자막 레이아웃을 WYSIWYG(What You See Is What You Get) 방식으로 유지하기.
타임라인 줌(Timeline zoom)은 임의의 CSS 스케일이 아닌 가시적 지속 시간(visible duration)을 기반으로 합니다. 가장 넓은 레벨에서는 긴 프로젝트를 보여줄 수 있으며, 가장 확대된 레벨에서는 프레임 중심의 눈금(frame-oriented ruler ticks)에 도달합니다:
export function getTimelineTrackWidthPercent(duration, zoom) {
const visibleDuration = getTimelineVisibleDuration(zoom);
return Math.max(100, (duration / visibleDuration) * 100);
...
이를 통해 사용자가 얼마나 확대하거나 축소하더라도 5초 길이의 클립은 항상 5초를 나타내게 됩니다.

저장 비용을 숨기지 않으면서 모델 캐싱하기
수백 메가바이트를 반복해서 다운로드하는 것은 브라우저 AI 애플리케이션을 사용할 수 없게 만듭니다. Timeline Studio는 모델 자산(assets)에는 캐시 우선(cache-first) 동작을, 앱 셸(app shell)에는 네트워크 우선(network-first) 동작을 사용하는 서비스 워커(service worker)를 등록합니다.
async function cacheFirst(request) {
const cache = await caches.open(MODEL_CACHE_NAME);
const cached = await cache.match(request);
...
모델 URL은 리비전 고정(revision-pinned)되어 있어, 캐시된 그래프와 그 런타임 가정(runtime assumptions)이 서로 조용히 어긋나는 일이 발생하지 않습니다. 또한 앱은 브라우저 저장 공간이 부족해지면 모델 캐시를 비울 수 있습니다.
로컬 우선 (Local-first) 방식이 "공짜"를 의미하지는 않습니다. 이는 서버 비용과 업로드 지연 시간 (upload latency)을 모델 다운로드, 디바이스 저장 공간, 메모리 압박 (memory pressure), 그리고 하드웨어 가변성 (hardware variability)과 맞바꾸는 것입니다. 이러한 비용들은 진행 상황 UI (progress UI)와 에러 메시지에 명확히 나타나야 합니다.
내보내기 (Export)는 또 다른 렌더링 엔진입니다
미리보기 캔버스 (preview canvas)와 내보낸 비디오는 크롭 기하학 (crop geometry), 자막 (captions), 스티커 (stickers), 필터 (filters), 전환 효과 (transitions), 그리고 현재 타임라인 시간 (timeline time)에 대해 일치해야 합니다.
내보내기 과정은 브라우저에서 시각적 캔버스와 오디오 트랙을 합성하고, 지원되는 네이티브 포맷을 녹화하며, MP4 트랜스코딩 (transcoding)이 필요한 경우에만 FFmpeg WASM을 사용합니다. 만약 MP4 변환에 실패하면, 에디터는 사용자의 작업물을 버리는 대신 성공적으로 렌더링된 WebM을 보존합니다.
브라우저의 코덱 (codec) 지원이 상당히 다양하기 때문에, 이러한 폴백 계층 구조 (fallback hierarchy)가 매우 중요하다는 것이 밝혀졌습니다.
내가 배운 점
가장 큰 교훈은 브라우저 AI가 단순히 모델 로딩의 문제가 아니라, 시스템의 문제라는 점이었습니다.
모델 선택, 실행 프로바이더 (execution providers), 워커 (workers), 캐싱 (caching), 메모리 수명 (memory lifetime), 타임라인 의미론 (timeline semantics), 렌더링 (rendering), 코덱 (codecs), 그리고 실패 상태 (failure states)를 하나의 제품으로서 설계해야 합니다. 오케스트레이션 (orchestration)이 부실한 빠른 모델은 여전히 느린 에디터를 만듭니다. 시간적 데이터 (temporal data)가 없는 정확한 모델은 시각적으로 부정확한 출력을 만들 수 있습니다. 타임라인에 깔끔하게 삽입되지 않은 성공적인 추론 (inference)은 여전히 좋지 않은 편집 경험을 제공합니다.
브라우저는 이미 오늘날 많은 애플리케이션이 사용하는 것보다 훨씬 더 많은 로컬 미디어 지능 (local media intelligence)을 수행할 능력이 있습니다. 하지만 최상의 결과는 브라우저의 제약 사항을 인정하는 데서 옵니다. 공격적으로 지연 로딩 (lazy-load)하고, 신중하게 캐싱하며, 무거운 작업은 메인 스레드 (main thread)에서 분리하고, 그래프 재작성 (graph rewrites)을 수치적으로 검증하며, 저하된 결과물을 "성공" 메시지 뒤에 절대 숨기지 마십시오.
Timeline Studio는 여전히 발전 중이며, 여러분의 기여를 환영합니다. 로컬 우선 (local-first) 크리에이티브 툴링 (creative tooling)을 실험해보고 싶다면, GitHub 저장소를 확인하거나 라이브 에디터를 사용해 보세요. 그리고 브라우저 AI 스택 (browser AI stack)의 어떤 부분을 다음에 탐구하기를 원하는지 저에게 알려주세요.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기