Idea
A separate, small macOS app that reads a camera (e.g. an Elgato), applies a Hance look live, and registers itself as a system capture device — so Camtasia, OBS, Zoom, or Meet can select "Hance" as their camera and get the film look burned in as you record.
This is the OBS Virtual Camera pattern. Not an in-app preview window, and deliberately not a feature of the main Electrobun app.
Why a separate app
The one part of Hance that genuinely wants to be native. Building it separately means:
- No Electrobun, no webview, no React — a small Swift shell with a settings panel (pick camera, pick look, intensity).
- The main app doesn't absorb a system extension, its signing requirements, or its release cadence.
- Shared look library. Looks are JSON in a known presets dir, so you design a look in Hance and pick it in Hance Live. One library, two apps.
Architecture
Link the renderer, don't pipe to it.
packages/wgpu is currently a binary-only crate whose main.rs is 72 lines of stdin/stdout wrapper around an already-public surface:
pub struct GpuRenderer
pub fn new(width, height, raw_params, lut) -> Result<Self, String>
pub fn render_frame(&mut self, input: &[u8]) -> Vec<u8>
Proposed: add a [lib] target exposing GpuRenderer, keep main.rs as the same thin sidecar wrapper. Nothing existing changes behaviour. Hance Live then links the renderer in-process.
This matters more than it looks. The stdin/stdout pipe is an artifact of talking to Bun, not something Live should inherit:
Components:
| Piece |
Language |
Notes |
| Capture + settings UI |
Swift |
AVFoundation |
| CMIO Camera Extension |
Swift/Obj-C |
macOS 12.3+, system extension in the app bundle, installed via OSSystemExtensionRequest |
| Effect chain |
Rust |
hance-gpu as a lib crate — shared with CLI/desktop, no fork of the look |
Performance: not the obstacle
Benchmarked against the current sidecar through the pipe, so these are pessimistic for an in-process design:
| Resolution |
Per frame |
Throughput |
| 720p |
6.4 ms |
156 fps |
| 1080p |
12.2 ms |
82 fps |
| 4K |
80.1 ms |
12 fps |
1080p sits comfortably inside a 33ms budget at 30fps. 4K live is not viable without optimization.
Prerequisite: the sidecar param protocol
renderFrame ignores its params argument (packages/gpu/src/wgpu-renderer.ts:64) — params and the baked LUT are consumed once at init, so changing a setting means respawning the process.
This has now blocked three separate things: live preview, adjusting a look mid-stream, and this feature. Worth fixing in hance-gpu on its own merits, independent of whether Live is ever built. Should be split into its own issue.
Hard prerequisite: signing
System extensions require a Developer ID signature, notarization, and the system-extension entitlement — and a separate app does not make this cheaper.
Today electrobun.config.ts skips both (codesign: hasDeveloperId, no ELECTROBUN_DEVELOPER_ID set) and the .dmg is uploaded by hand. A signed, notarized release pipeline is the real gate on this whole idea.
Open questions
- Does Camtasia enumerate CMIO virtual cameras? It should, if it uses the standard macOS capture APIs, but it's the kind of app that may do its own device handling. Unverified.
- Does Apple require approval for the system-extension entitlement beyond a Developer ID account?
- Two apps means two release pipelines and a version-compatibility question the first time the look format changes. Acceptable, but worth deciding deliberately.
Cheapest first step
Install OBS, enable its Virtual Camera, and check whether Camtasia lists it as a source. If OBS's virtual cam appears, a CMIO extension from Hance would too — question 1 settled in ~10 minutes without writing any code.
Scope note
macOS-only by nature, not by preference: Windows needs an entirely different mechanism (Media Foundation / DirectShow), and none of the CMIO work transfers.
Near-term alternative (shipped)
#163 exports a look as a .cube LUT, usable in Elgato Camera Hub, OBS, Resolve, and Premiere today — no system extension, no signing work. It carries the colour grade but not halation, bloom, aberration, grain, vignette, or camera shake, which no colour→colour mapping can express.
That covers most of the perceived look for a fraction of the effort. Hance Live remains the only way to get the full effect chain live.
Idea
A separate, small macOS app that reads a camera (e.g. an Elgato), applies a Hance look live, and registers itself as a system capture device — so Camtasia, OBS, Zoom, or Meet can select "Hance" as their camera and get the film look burned in as you record.
This is the OBS Virtual Camera pattern. Not an in-app preview window, and deliberately not a feature of the main Electrobun app.
Why a separate app
The one part of Hance that genuinely wants to be native. Building it separately means:
Architecture
Link the renderer, don't pipe to it.
packages/wgpuis currently a binary-only crate whosemain.rsis 72 lines of stdin/stdout wrapper around an already-public surface:Proposed: add a
[lib]target exposingGpuRenderer, keepmain.rsas the same thin sidecar wrapper. Nothing existing changes behaviour. Hance Live then links the renderer in-process.This matters more than it looks. The stdin/stdout pipe is an artifact of talking to Bun, not something Live should inherit:
Components:
OSSystemExtensionRequesthance-gpuas a lib crate — shared with CLI/desktop, no fork of the lookPerformance: not the obstacle
Benchmarked against the current sidecar through the pipe, so these are pessimistic for an in-process design:
1080p sits comfortably inside a 33ms budget at 30fps. 4K live is not viable without optimization.
Prerequisite: the sidecar param protocol
renderFrameignores itsparamsargument (packages/gpu/src/wgpu-renderer.ts:64) — params and the baked LUT are consumed once atinit, so changing a setting means respawning the process.This has now blocked three separate things: live preview, adjusting a look mid-stream, and this feature. Worth fixing in
hance-gpuon its own merits, independent of whether Live is ever built. Should be split into its own issue.Hard prerequisite: signing
System extensions require a Developer ID signature, notarization, and the system-extension entitlement — and a separate app does not make this cheaper.
Today
electrobun.config.tsskips both (codesign: hasDeveloperId, noELECTROBUN_DEVELOPER_IDset) and the.dmgis uploaded by hand. A signed, notarized release pipeline is the real gate on this whole idea.Open questions
Cheapest first step
Install OBS, enable its Virtual Camera, and check whether Camtasia lists it as a source. If OBS's virtual cam appears, a CMIO extension from Hance would too — question 1 settled in ~10 minutes without writing any code.
Scope note
macOS-only by nature, not by preference: Windows needs an entirely different mechanism (Media Foundation / DirectShow), and none of the CMIO work transfers.
Near-term alternative (shipped)
#163 exports a look as a
.cubeLUT, usable in Elgato Camera Hub, OBS, Resolve, and Premiere today — no system extension, no signing work. It carries the colour grade but not halation, bloom, aberration, grain, vignette, or camera shake, which no colour→colour mapping can express.That covers most of the perceived look for a fraction of the effort. Hance Live remains the only way to get the full effect chain live.