Tooscut — 브라우저에서 돌아가는 GPU 가속 영상 편집기
WebGPU + Rust/WASM으로 네이티브급 실시간 합성을 브라우저에서 구현한 방법
Tooscut은 브라우저 탭 하나에서 돌아가는 NLE(Non-Linear Editor)다. DaVinci Resolve나 Premiere Pro 같은 데스크톱 편집기를 설치하지 않고, URL 접속만으로 영상 편집을 시작할 수 있다.
근데 "브라우저 영상 편집기"라고 하면 보통 장난감 수준을 떠올린다. Tooscut이 다른 건 렌더링 엔진 전체가 Rust/WASM + WebGPU로 돌아간다는 점이다. JavaScript는 UI만 담당하고, 실제 프레임 합성은 GPU에서 처리한다.
아키텍처 3계층
React UI(TanStack Start) → TypeScript 렌더 엔진 → Rust/WASM 합성기. 코드베이스는 TypeScript 80%, Rust 20% 비율이다. Rust 20%가 성능의 핵심을 전부 담당한다.
GPU 가속이 실제로 뭘 하는가
밝기·대비·채도·블러·색상 회전 — 이 효과들이 전부 하나의 WGSL 프래그먼트 셰이더 안에서 돌아간다. 슬라이더를 움직이면 GPU 유니폼 값만 바뀌고, 셰이더가 즉시 재실행된다. CPU는 건드리지 않는다.
블러를 예로 들면, 13×13 가우시안 커널이 셰이더 안에 하드코딩되어 있다. sigma 값에 따라 스텝 크기를 스케일링하는 방식이라, 블러 강도를 바꿔도 텍스처 패스가 추가로 필요 없다.
실시간 미리보기 파이프라인
여기가 제일 영리한 부분이다.
- 메인 스레드에서
requestAnimationFrame으로 루프를 돌린다 - 현재 시각에 보이는 클립만 필터링한다 (정렬된 배열 + 이진 검색)
- 비디오 프레임을
HTMLVideoElement에서createImageBitmap()으로 뽑는다 - ImageBitmap을 Web Worker에 transfer(복사 아님)한다
- Worker 안의 WASM 컴포지터가 WebGPU로 합성해서 OffscreenCanvas에 직접 그린다
핵심은 4번이다. ImageBitmap transfer는 제로카피 — 메인 스레드의 메모리를 Worker로 소유권만 넘기는 거라 복사 비용이 없다. 텍스처 업로드도 copy_external_image_to_texture로 GPU에 직접 올린다.
메모리를 어떻게 관리하는가
WASM linear memory는 V8 힙에 카운트되지 않는다. GPU 버퍼는 VRAM에, Bitmap은 네이티브 할당. 비디오 파일은 전체를 메모리에 올리지 않고 on-demand로 디코딩한다. 이 구조 덕분에 4K 영상도 브라우저 탭 메모리 제한에 걸리지 않는다.
내보내기는 어떻게
FrameRendererPool로 여러 Web Worker를 병렬로 돌린다. 각 Worker에 독립 WASM 컴포지터가 있어서 프레임을 나눠서 렌더링한다. MediaBunny 라이브러리가 MP4 먹싱을 담당한다.
JS에서 직접 WebGPU를 쓰면 안 되나?
된다. WebGPU는 JavaScript API니까 JS에서 직접 쓰는 게 원래 정석이다. GPU에서 돌아가는 셰이더 코드(WGSL)는 JS든 Rust든 완전히 동일하고, 블러·밝기·대비 같은 이펙트 성능도 차이 없다.
차이가 나는 건 GPU 밖에서 CPU가 처리하는 부분이다. 매 프레임마다 키프레임 보간(베지어 커브), 4×4 매트릭스 연산, 유니폼 버퍼 패킹(128바이트, 16바이트 정렬)을 16.6ms 안에 끝내야 한다.
| 작업 | JS | Rust/WASM |
|---|---|---|
| 키프레임 보간 | V8 JIT 최적화 의존 | 컴파일 타임 최적화, SIMD 가능 |
| 유니폼 버퍼 패킹 | ArrayBuffer 수동 오프셋 계산 | Pod derive로 구조체 → 바이트 자동 변환 |
| 오디오 믹싱 (128샘플 단위) | GC 지터 위험 | GC 없음 — 실시간 오디오에 치명적 차이 |
결론: 트랙 3~4개, 이펙트 1~2개면 JS로 충분하다. 트랙 10개 이상 + 키프레임 수십 개 + 오디오 이펙트 체인이 붙으면 JS의 GC(가비지 컬렉션)가 랜덤으로 프레임을 튀게 만든다. 특히 오디오는 ~128샘플(~2.9ms) 단위 콜백인데 GC가 끼면 소리가 끊긴다. Tooscut이 Rust를 택한 진짜 이유는 속도보다 GC 없는 결정론적 타이밍이다.
렌더링 파이프라인 상세
프레임 합성 흐름
requestAnimationFrame 루프 → 현재 시각의 가시 클립 필터링(이진 검색) → buildRenderFrame()으로 키프레임 평가 + Transform/Effects 병합
HTMLVideoElement → createImageBitmap() → ImageBitmap을 Worker에 transfer(제로카피)
Compositor.renderFrame() → WebGPU 파이프라인 실행 → OffscreenCanvas에 직접 출력
copy_external_image_to_texture) → 버텍스 셰이더(풀스크린 쿼드) → 프래그먼트 셰이더(이펙트 체인) → 알파 블렌딩 합성
GPU 이펙트 셰이더 구현
모든 이펙트가 단일 WGSL 프래그먼트 셰이더 안에서 순차 적용된다. 유니폼 버퍼 하나(128바이트)로 전체 파라미터를 전달.
| 이펙트 | 셰이더 구현 | 성능 특성 |
|---|---|---|
| 블러 | 13×13 가우시안 커널, sigma 기반 스텝 스케일링 | 싱글 패스 — 추가 텍스처 불필요 |
| 밝기 | color.rgb * brightness |
곱셈 1회 |
| 대비 | (rgb - 0.5) * contrast + 0.5 |
벡터 연산 1회 |
| 채도 | luminance(dot product) 기반 mix(grayscale, color, saturation) |
dot + mix 연산 |
| 색상 회전 | RGB → HSL 변환 → H += radian → HSL → RGB | 색공간 변환 2회 |
| 트랜지션 | UV 좌표 기반 smoothstep 마스킹 (Wipe L/R/U/D) |
픽셀별 조건 분기 없음 |
적용 순서: 블러 → 밝기 → 대비 → 채도 → 색상회전 → 트랜지션 → 불투명도 → clamp
Rust/WASM 크레이트 구조
| 크레이트 | 역할 | 핵심 기술 |
|---|---|---|
| compositor | GPU 합성 엔진 — 미디어/텍스트/도형/라인 렌더링 | wgpu, glyphon, cosmic-text |
| keyframe | 키프레임 보간 엔진 — Linear/Step/Bezier | 시간적 일관성 캐시(순차 O(1), 시크 O(log n)) |
| audio-engine | AudioWorklet 멀티트랙 믹서 — EQ/컴프레서/리버브 | ~128 샘플 단위 실시간 PCM 출력 |
| types | 공유 타입 정의 — tsify-next로 TS 타입 자동 생성 | serde + wasm-bindgen |
메모리 관리 전략
-
●
WASM linear memory — V8 힙에 카운트되지 않음. 브라우저 DevTools의 JS 힙 사이즈와 별도
-
●
GPU 버퍼 — VRAM에 저장. TextureManager가 동일 크기 텍스처는 재생성 없이 데이터만 업데이트
-
●
비디오 디코딩 — 전체 로딩 X. on-demand 버퍼링 윈도우 방식. 프리뷰는 HTMLVideoElement(브라우저 최적화), 내보내기는 MediaBunny(프레임 정확도)
-
●
ImageBitmap transfer — 메인 스레드 → Worker 이동 시 소유권 이전(제로카피). 복사 비용 없음
타임라인 데이터 구조
상태 관리: Zustand + zundo(temporal 미들웨어) → Undo/Redo 지원
클립 배열: 항상 startTime 기준 정렬 유지 → O(log n + k) 이진 검색으로 가시 클립 탐색
트랙: 비디오+오디오가 항상 쌍으로 생성/삭제. 트랙 인덱스 = z-order
키프레임: 프로퍼티별 KeyframeTrack → 정렬된 Keyframe 배열. 보간: Linear, Step, Bezier(커스텀 큐빅)
브라우저 호환성
| 브라우저 | 지원 | 비고 |
|---|---|---|
| Chrome / Edge | ✓ | WebGPU 완전 지원 |
| Brave | ✓ | Chromium 기반 — 문제 없이 동작 |
| Firefox | ⚠ | WebGPU 지원 미성숙 — 크래시 보고 있음 |
| Safari | ✗ | WebGPU 미지원 |
개발자 배경
Mohamad Mohebifar — Codemod 공동 창업자 & CTO. Meta, Brex, Shopify 출신. 코드 변환/트랜스파일러 10년+ 경력. WorldSkills 2015 동메달(Web Design & Development). 목표는 \"Photopea의 영상 편집 버전\" — 설치 없이 일상적 편집 작업의 80%를 커버하는 것.
실전 순서
tooscut.app 접속 → 브라우저에서 바로 편집기 실행 (설치 불필요)
File System Access API로 로컬 파일을 직접 참조 — 업로드/다운로드 없이 편집 가능
타임라인에 비디오·오디오·이미지·텍스트·도형 클립 배치
키프레임 패널에서 위치·크기·투명도·효과에 베지어 커브 애니메이션 적용
내보내기 시 FrameRendererPool이 병렬 Worker로 렌더링 → MP4 출력
장점
- ✓ 설치 제로 — URL 접속만으로 전문 편집기 사용
- ✓ GPU 가속 실시간 이펙트 — 슬라이더 조작 시 렌더링 지연 없음
- ✓ Local-first — 미디어 파일이 서버에 전송되지 않음
- ✓ Rust/WASM 엔진 — 네이티브의 90~95% 성능
단점
- ✗ WebGPU 필수 — Safari 미지원, Firefox 불안정 (사실상 Chrome 전용)
- ✗ 8K 멀티트랙이나 Color Grading 같은 하이엔드 작업은 DaVinci Resolve 영역
- ✗ Elastic License 2.0 — OSI 정의의 오픈소스는 아님 (상업 호스팅 제한)