🎬

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 값에 따라 스텝 크기를 스케일링하는 방식이라, 블러 강도를 바꿔도 텍스처 패스가 추가로 필요 없다.

실시간 미리보기 파이프라인

여기가 제일 영리한 부분이다.

  1. 메인 스레드에서 requestAnimationFrame으로 루프를 돌린다
  2. 현재 시각에 보이는 클립만 필터링한다 (정렬된 배열 + 이진 검색)
  3. 비디오 프레임을 HTMLVideoElement에서 createImageBitmap()으로 뽑는다
  4. ImageBitmap을 Web Worker에 transfer(복사 아님)한다
  5. 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 안에 끝내야 한다.

작업JSRust/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 없는 결정론적 타이밍이다.

렌더링 파이프라인 상세

프레임 합성 흐름

Main Thread requestAnimationFrame 루프 → 현재 시각의 가시 클립 필터링(이진 검색) → buildRenderFrame()으로 키프레임 평가 + Transform/Effects 병합
Video Decode HTMLVideoElementcreateImageBitmap() → ImageBitmap을 Worker에 transfer(제로카피)
Web Worker Comlink 프록시 → WASM Compositor.renderFrame() → WebGPU 파이프라인 실행 → OffscreenCanvas에 직접 출력
GPU 텍스처 업로드(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%를 커버하는 것.

실전 순서

1

tooscut.app 접속 → 브라우저에서 바로 편집기 실행 (설치 불필요)

2

File System Access API로 로컬 파일을 직접 참조 — 업로드/다운로드 없이 편집 가능

3

타임라인에 비디오·오디오·이미지·텍스트·도형 클립 배치

4

키프레임 패널에서 위치·크기·투명도·효과에 베지어 커브 애니메이션 적용

5

내보내기 시 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 정의의 오픈소스는 아님 (상업 호스팅 제한)

사용 사례

YouTube 영상 컷 편집 — 데스크톱 앱 설치 없이 빠르게 출장 중 빌린 PC에서 영상 편집이 필요할 때 Chromebook 등 앱 설치가 제한된 환경 프라이버시 우선 — 파일이 서버에 올라가면 안 되는 경우