Skip to content

Commit 888fe3d

Browse files
authored
Merge pull request #67 from Sportinger/staging
v1.4.2 — Three.js 3D Layer System, Model Import & Effect Reordering
2 parents 74b8948 + 4114b64 commit 888fe3d

88 files changed

Lines changed: 4362 additions & 471 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

README.md

Lines changed: 45 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,31 @@
22

33
# MasterSelects
44

5-
<h3>Browser-based Video Compositor</h3>
5+
<h3>Browser-based Video Compositor & 3D Engine</h3>
6+
7+
<br>
8+
9+
<table><tr><td align="center" style="border:none;background:#0d1117;">
10+
<h1>&#9889; 660 KB <sub>gzip</sub></h1>
11+
<sup><b>initial load</b></sup>
12+
</td></tr></table>
13+
614

715
<p>
8-
GPU-first editing with <b>30 effects</b>, <b>37 blend modes</b>, <b>76 AI tools</b>, and only <b>13 dependencies</b>.<br>
9-
Built from scratch in <b>2,500+ lines of WGSL</b> and <b>120k lines of TypeScript</b>.
16+
GPU-first editing with <b>30 effects</b>, <b>37 blend modes</b>, <b>76 AI tools</b>, <b>real 3D via Three.js</b>, and only <b>14 dependencies</b>.<br>
17+
Built from scratch in <b>2,400+ lines of WGSL</b> and <b>138k lines of TypeScript</b>.<br>
18+
Import <b>OBJ, glTF, GLB, FBX</b> models directly into the timeline.
1019
</p>
1120

1221
<p>
13-
<a href="https://github.com/Sportinger/MasterSelects/releases"><img src="https://img.shields.io/badge/version-1.4.0-blue.svg" alt="Version"></a>
22+
<a href="https://github.com/Sportinger/MasterSelects/releases"><img src="https://img.shields.io/badge/version-1.4.2-blue.svg" alt="Version"></a>
1423
<a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-green.svg" alt="License"></a>
1524
<a href="https://app.fossa.com/projects/custom%2b61097%2fmasterselects"><img src="https://app.fossa.com/api/projects/custom%2b61097%2fmasterselects.svg?type=shield" alt="FOSSA Status"></a>
1625
</p>
1726

1827
<p>
1928
<a href="#"><img src="https://img.shields.io/badge/WebGPU-990000?style=flat-square&logo=webgpu&logoColor=white" alt="WebGPU"></a>
29+
<a href="#"><img src="https://img.shields.io/badge/Three.js-000000?style=flat-square&logo=threedotjs&logoColor=white" alt="Three.js"></a>
2030
<a href="#"><img src="https://img.shields.io/badge/React_19-61DAFB?style=flat-square&logo=react&logoColor=black" alt="React 19"></a>
2131
<a href="#"><img src="https://img.shields.io/badge/TypeScript-3178C6?style=flat-square&logo=typescript&logoColor=white" alt="TypeScript"></a>
2232
<a href="#"><img src="https://img.shields.io/badge/Vite-646CFF?style=flat-square&logo=vite&logoColor=white" alt="Vite"></a>
@@ -31,29 +41,36 @@
3141

3242
---
3343

34-
## Security Model
44+
## Supported Formats
3545

36-
MasterSelects is a **local-first editor**. Your timeline, media processing, rendering, and most AI-adjacent operations stay on your machine unless you explicitly call an external API. The project now has explicit trust boundaries instead of relying on "it's just localhost".
46+
Decoding depends on what the **browser** supports — the container is just the wrapper, the codec inside is what matters.
3747

38-
**Current protections:**
39-
- **Native Helper bridge:** Binds to `127.0.0.1` only and requires a random startup Bearer token for HTTP and WebSocket bridge operations
40-
- **Dev bridge hardening:** Vite `/api/ai-tools` and local file routes require a per-session token and reject non-loopback browser origins
41-
- **Path restrictions:** Local file reads, listings, uploads, and locate/search operations are restricted to explicit allowed roots instead of arbitrary disk access
42-
- **AI tool policy:** External bridge calls run through caller restrictions and approval/confirmation gates instead of getting unrestricted editor control
43-
- **Secret handling:** API keys are stored in encrypted IndexedDB, `.keys.enc` import/export is disabled, and logs redact common secret/token patterns
44-
- **Security verification:** CI includes secret scanning plus JS and Rust security checks, and the repo has dedicated tests for bridge auth, file access, and tool policy behavior
48+
<table>
49+
<tr><th colspan="2">Import (Decode)</th></tr>
50+
<tr><td><b>Containers</b></td><td>MP4, MOV, WebM, MKV, AVI, M4V</td></tr>
51+
<tr><td><b>Video codecs</b></td><td>H.264 (AVC), H.265 (HEVC)¹, VP8, VP9, AV1</td></tr>
52+
<tr><td><b>Audio codecs</b></td><td>AAC, MP3, Opus, Vorbis, FLAC, WAV/PCM</td></tr>
53+
<tr><td><b>Image</b></td><td>PNG, JPG, WebP, GIF, BMP, AVIF, SVG</td></tr>
54+
<tr><td><b>3D Models</b></td><td>OBJ, glTF, GLB, FBX — rendered via Three.js with lighting</td></tr>
55+
<tr><td><b>Download</b></td><td>YouTube, TikTok, Instagram, Twitter/X, Vimeo + <a href="https://github.com/yt-dlp/yt-dlp/blob/master/supportedsites.md">all yt-dlp sites</a> via Native Helper</td></tr>
56+
<tr><th colspan="2">Export (Encode)</th></tr>
57+
<tr><td><b>Containers</b></td><td>MP4, WebM</td></tr>
58+
<tr><td><b>Video codecs</b></td><td>H.264, H.265¹, VP9, AV1 — GPU-accelerated via WebCodecs</td></tr>
59+
<tr><td><b>Audio codecs</b></td><td>AAC (MP4), Opus (WebM)</td></tr>
60+
<tr><td><b>Interchange</b></td><td>FCPXML (Final Cut Pro / DaVinci Resolve), PNG sequence</td></tr>
61+
</table>
4562

46-
**Known boundary:** this is still not "perfect sandboxing". Same-user local processes, malicious browser extensions, and compromised same-origin code can still be dangerous. The goal here is **clear, test-covered local trust boundaries**, not pretending a browser app is magically zero-risk.
63+
¹ H.265 decode/encode depends on OS & hardware — full support on Windows, partial on macOS/Linux.
4764

48-
See [Security.md](docs/Features/Security.md) for the full trust model, secret handling, bridge details, and current limitations.
65+
> **MOV** files work because they share the same ISO BMFF container as MP4 — any MOV with H.264/H.265 inside plays fine. **MKV** works if it contains browser-decodable codecs (H.264, VP9, etc.). Files with unsupported codecs (e.g. ProRes in MOV) fall back to the Native Helper decode path when available.
4966
5067
---
5168

5269
## What Makes This Different
5370

5471
Most browser-based video editors share a pattern: Canvas 2D compositing, heavyweight dependency trees, and CPU-bound rendering that falls apart at scale. This project takes a fundamentally different approach.
5572

56-
**GPU-first architecture.** Preview, scrubbing, and export all run through the same **WebGPU ping-pong compositor**. Video textures are imported as `texture_external` (**zero-copy**, no CPU roundtrip). **37 blend modes**, 3D rotation, and inline color effects all execute in a **single WGSL composite shader** per layer. No Three.js, no GSAP, no Canvas 2D fallback in the hot path.
73+
**GPU-first architecture.** Preview, scrubbing, and export all run through the same **WebGPU ping-pong compositor**. Video textures are imported as `texture_external` (**zero-copy**, no CPU roundtrip). **37 blend modes**, 3D rotation, and inline color effects all execute in a **single WGSL composite shader** per layer. **Three.js** is lazily loaded only for 3D model rendering — no GSAP, no Canvas 2D fallback in the hot path.
5774

5875
**Zero-copy export pipeline.** Frames are captured as `new VideoFrame(offscreenCanvas)` directly from the GPU canvas. **No `readPixels()`**, no `getImageData()`, no staging buffers in the default path. The GPU renders, **WebCodecs encodes**. That's it.
5976

@@ -161,23 +178,25 @@ cargo run --release # WebSocket :9876, HTTP :9877
161178
| **AI Control** | Local HTTP bridge for external agents to steer the running editor |
162179
| **Download** | yt-dlp integration for YouTube, TikTok, Instagram, Twitter/X, Vimeo, and other supported sites |
163180

164-
**Export codecs:** H.264, H.265, VP9, AV1 via WebCodecs (production). ProRes, DNxHR, FFV1, UTVideo, MJPEG via experimental FFmpeg WASM path (single-threaded, requires custom build).
165-
166181
**Platforms:** Windows, Linux, macOS. Building the Native Helper requires Rust. Downloads also require `yt-dlp`. See [Native Helper docs](tools/native-helper/README.md) for platform-specific setup.
167182

168183
---
169184

170-
## Security Model
185+
## Security
186+
187+
MasterSelects is a **local-first editor**. Editing, rendering, caching, and most analysis stay in the browser unless you explicitly invoke an external provider or the Native Helper.
171188

172-
MasterSelects is local-first: editing, rendering, caching, and most analysis stay in the browser unless you explicitly invoke an external provider or the Native Helper.
189+
- **API keys:** stored in IndexedDB with per-browser Web Crypto encryption
190+
- **Native Helper:** binds to `127.0.0.1` only, requires a random startup Bearer token for HTTP and WebSocket
191+
- **Dev bridge:** Vite `/api/ai-tools` and local file routes require a per-session token and reject non-loopback origins
192+
- **Local file access:** restricted to explicit allowed roots (project root, temp, Desktop, Documents, Downloads, Videos)
193+
- **AI tool policy:** external bridge calls run through caller restrictions and approval gates
194+
- **Secret handling:** logs redact common secret/token patterns; `.keys.enc` export disabled
195+
- **CI checks:** secret scanning, JS and Rust security audits, dedicated tests for bridge auth and file access policy
173196

174-
- **API keys:** stored in IndexedDB with per-browser Web Crypto encryption. This protects against casual inspection, not against same-origin script execution.
175-
- **Dev bridge:** sensitive Vite routes require a per-session Bearer token and only accept localhost browser origins.
176-
- **Native Helper:** binds to `127.0.0.1` and requires a random startup token for HTTP and WebSocket bridge operations.
177-
- **Local file access:** limited to explicit roots such as the project root, temp, Desktop, Documents, Downloads, and Videos, plus optional `MASTERSELECTS_ALLOWED_FILE_ROOTS`.
178-
- **Key export:** `.keys.enc` export/import is disabled until passphrase-based encryption is implemented.
197+
**Known boundary:** this is not perfect sandboxing. Same-user local processes, malicious browser extensions, and compromised same-origin code can still be dangerous. The goal is **clear, test-covered local trust boundaries**.
179198

180-
See [Security docs](docs/Features/Security.md) for the trust model and known limitations, and [Native Helper docs](tools/native-helper/README.md) for auth examples.
199+
See [Security.md](docs/Features/Security.md) for the full trust model and limitations.
181200

182201
---
183202

docs/Features/3D-Layers.md

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
# 3D Layer System
2+
3+
MasterSelects supports After Effects-style 3D layers via an integrated Three.js renderer. Layers can be toggled between 2D and 3D mode, 3D model files (OBJ, glTF, GLB, FBX) can be imported directly as timeline clips, and primitive 3D meshes can be created from the Media Panel.
4+
5+
## Architecture
6+
7+
```
8+
[2D Layers] --> Existing WebGPU Compositor (unchanged)
9+
|
10+
[3D Layers] --> Three.js Scene --> OffscreenCanvas --> copyExternalImageToTexture --> Compositor
11+
^
12+
[Camera + Lighting]
13+
```
14+
15+
Three.js renders all 3D-flagged layers into an OffscreenCanvas. The result is imported into the existing WebGPU compositor as a single texture layer. 2D layers continue through the existing pipeline unchanged. Zero overhead when no 3D layers exist (Three.js is lazily loaded via dynamic import).
16+
17+
## Features
18+
19+
### Per-Layer 3D Toggle
20+
- Any video/image layer can be toggled to 3D via the **"2D/3D" button** in the Transform panel
21+
- 3D layers become textured planes in a Three.js scene with perspective camera
22+
- Toggling back to 2D resets Position Z, Rotation X/Y to 0
23+
24+
### 3D Model Import
25+
- Drag **OBJ, glTF, GLB, FBX** files into the timeline
26+
- Model clips are automatically set to 3D (cannot be switched to 2D)
27+
- Models are auto-centered and normalized to fit the viewport
28+
- Default lighting: Ambient (0.6) + Directional (0.8)
29+
- OBJ without MTL: gets default gray MeshStandardMaterial
30+
- Wireframe debug toggle: **"Wire" button** in Transform panel (blue wireframe)
31+
32+
### Primitive Mesh Creation
33+
Create 3D mesh primitives from the Media Panel via **+ Add > Mesh** or right-click context menu:
34+
35+
| Primitive | Three.js Geometry | Default Size |
36+
|-----------|------------------|--------------|
37+
| Cube | `BoxGeometry` | 0.6 x 0.6 x 0.6 |
38+
| Sphere | `SphereGeometry` | radius 0.35, 32x24 segments |
39+
| Plane | `PlaneGeometry` | 0.8 x 0.8 |
40+
| Cylinder | `CylinderGeometry` | radius 0.25, height 0.6 |
41+
| Torus | `TorusGeometry` | radius 0.3, tube 0.1 |
42+
| Cone | `ConeGeometry` | radius 0.3, height 0.6 |
43+
44+
- Mesh items are stored in a "Meshes" folder in the Media Panel
45+
- Drag to timeline creates a clip with `is3D: true` and `meshType`
46+
- Default material: `MeshStandardMaterial` (color #aaaaaa, metalness 0.3, roughness 0.6)
47+
- Wireframe toggle supported
48+
- Default clip duration: 10 seconds (max 1 hour)
49+
- All transform properties (position, rotation, scale) and keyframe animation supported
50+
51+
### Transform Controls (3D Mode)
52+
| Property | 2D Mode | 3D Mode |
53+
|----------|---------|---------|
54+
| Position | X, Y | X, Y, Z |
55+
| Scale | All, X, Y | All, X, Y (+ Z for models) |
56+
| Rotation | Z | X, Y, Z (AE-style: `Nx + remainder`) |
57+
| Opacity | Yes | Yes (compositor-level) |
58+
| Blend Mode | Yes | Yes (all 37 modes) |
59+
60+
### AE-Style Rotation Display
61+
Rotation values are displayed as `2x +30.0°` (2 revolutions + 30 degrees = 750°):
62+
- **Multiplier (`2x`)**: Drag to change in 360° increments
63+
- **Remainder (`+30.0°`)**: Fine rotation within the revolution
64+
- Both are independently draggable and keyframeable
65+
66+
### Composition Camera
67+
Per-composition camera with configurable properties:
68+
- Position (x, y, z)
69+
- Target / Look-at (x, y, z)
70+
- FOV (default: 50°)
71+
- Near/Far planes
72+
73+
Camera distance is auto-calculated so default-transform layers fill the viewport exactly.
74+
75+
## Keyframe Animation
76+
All 3D properties are fully keyframeable:
77+
- `position.z`, `rotation.x`, `rotation.y`, `rotation.z`, `scale.z`
78+
- Keyframe lanes for 3D properties are hidden when clip is in 2D mode
79+
- `scale.z` keyframes work for model clips
80+
81+
## Export
82+
3D layers are included in video export. The export pipeline uses the same `engine.render()``RenderDispatcher``process3DLayers()` → Three.js path as the preview.
83+
84+
## Effect Support
85+
GPU effects (blur, color correction, etc.) are applied as post-processing on the 3D scene output. They work identically to 2D layers since Three.js renders to a texture first.
86+
87+
## Effect Reordering
88+
Effects can be reordered via drag-and-drop:
89+
- Drag the **≡ handle** on each effect to reorder
90+
- Only the handle initiates drag (sliders/controls are not blocked)
91+
- Order affects render output (effects are chained sequentially)
92+
- Undo/redo supported
93+
94+
## Key Files
95+
96+
| File | Purpose |
97+
|------|---------|
98+
| `src/engine/three/ThreeSceneRenderer.ts` | Three.js scene renderer (HMR singleton) |
99+
| `src/engine/three/types.ts` | Layer3DData, CameraConfig types |
100+
| `src/engine/render/RenderDispatcher.ts` | 3D layer routing (`process3DLayers`) |
101+
| `src/engine/render/LayerCollector.ts` | Model layer passthrough |
102+
| `src/stores/timeline/clip/addModelClip.ts` | Model clip creation (file-based) |
103+
| `src/stores/timeline/meshClipSlice.ts` | Primitive mesh clip creation |
104+
| `src/services/layerBuilder/LayerBuilderService.ts` | Model layer builder |
105+
| `src/engine/export/ExportLayerBuilder.ts` | Export 3D layer support |
106+
| `src/components/panels/properties/TransformTab.tsx` | 3D transform UI |
107+
| `src/engine/featureFlags.ts` | `use3DLayers` flag |
108+
109+
## Supported File Formats
110+
111+
| Format | Loader | Notes |
112+
|--------|--------|-------|
113+
| `.obj` | OBJLoader | Blender default export, no materials without .mtl |
114+
| `.gltf` | GLTFLoader | Khronos standard, text-based |
115+
| `.glb` | GLTFLoader | Binary glTF, most common for web |
116+
| `.fbx` | GLTFLoader (fallback) | Autodesk format, limited support |
117+
118+
## Limitations
119+
120+
- No PBR material editor yet (models use default or embedded materials)
121+
- No shadow casting between layers
122+
- Camera is per-composition, not keyframeable yet
123+
- Single 3D layer: opacity/blend handled by compositor. Multiple 3D layers: opacity via Three.js material
124+
- Model clips need file re-authorization after page refresh (same as video clips)

0 commit comments

Comments
 (0)