A standalone Tauri v2 example that records a live WebGL canvas through canvas.captureStream() and MediaRecorder, optionally adds microphone audio, and saves the completed video with a native desktop dialog.
The project is the Tauri v2 counterpart to the v1 Canvas Recorder. The browser media pipeline remains easy to inspect, while Tauri v2 capabilities and Rust commands provide narrowly scoped native file output and fullscreen control.
HTMLCanvasElement.captureStream() video captureMediaRecorder codec negotiationnpm install
npm run dev
The project contains an explicit [workspace] boundary in src-tauri/Cargo.toml, preventing unrelated parent Cargo workspaces from absorbing it.
WebGL fragment shader
│
▼
HTMLCanvasElement
│ captureStream(FPS)
▼
Canvas video track ──────────────┐
├──► MediaStream ─► MediaRecorder
Optional getUserMedia audio ─────┘ │
▼
Blob chunks
│
▼
Tauri v2 save dialog
│
▼
Rust write_binary command
The CSS preview scales to fit the stage, but the canvas backing store remains at the selected recording resolution. A 3840 × 2160 selection therefore renders and records a true 4K canvas even when the application window is smaller.
Higher resolutions increase GPU, encoder, memory, and IPC costs. Confirm 1080p first, then move to 4K.
The example asks MediaRecorder.isTypeSupported() about multiple candidate formats.
Without microphone audio it prefers:
video/webm;codecs=vp9
video/webm;codecs=vp8
video/mp4;codecs=h264
video/mp4
video/webm
With microphone audio enabled it prefers audio-bearing variants such as VP9/Opus, VP8/Opus, or H.264/AAC. The selected MIME type appears in the Codec field before recording.
Codec availability belongs to the operating system WebView, not Tauri. A format available on one machine may be unavailable on another.
Microphone audio is optional and disabled by default.
When enabled:
For macOS bundles, Info.plist contains NSMicrophoneUsageDescription, and entitlements.plist enables audio input.
The official dialog plugin provides the save dialog:
tauri-plugin-dialog = "2"
The capability file grants only:
[
"core:default",
"dialog:allow-save"
]
After the user chooses a destination, PNG snapshots use a direct write_binary command. Larger video recordings are divided into 1 MiB chunks and sent sequentially through create_binary and append_binary, avoiding one enormous IPC payload. The Save button reports chunk progress while the file is written.
This educational example retains MediaRecorder chunks in memory until the recording is discarded or replaced. Long, high-resolution, or high-bitrate captures may use substantial memory.
A production recorder would normally consider:
Four procedural shader modes are included so recording can be tested without external media:
Controls include speed, scale, detail, warp, pulse, hue, saturation, brightness, and contrast.
14-tauri-v2-canvas-recorder/
├── package.json
├── README.md
├── src/
│ ├── index.html
│ ├── styles.css
│ └── app.js
└── src-tauri/
├── Cargo.toml
├── Info.plist
├── entitlements.plist
├── tauri.conf.json
├── capabilities/default.json
├── build.rs
├── icons/
└── src/main.rs
The WebView must expose both canvas.captureStream() and MediaRecorder. The example checks both APIs at startup.
The platform WebView rejected every tested MIME string. Upgrade the operating system/WebView or test another target platform.
Some systems hide device labels before microphone permission. Enable microphone audio and begin a recording attempt; the device list refreshes after permission succeeds.
Reduce the resolution, frame rate, bitrate, or shader detail. The preview FPS is a rendering-loop measurement, not a guaranteed encoded-frame count.
The completed video is transferred to Rust only after recording stops and a path is chosen. Large captures can take time and temporarily require another in-memory byte representation.
A visual application needs a clear path from live GPU output to a persistent media file. This example isolates the browser capture APIs, optional audio mixing, codec negotiation, desktop save workflow, and Tauri v2 permission model in one reusable project.