A standalone Tauri v2 + raw WebGL 1 example demonstrating a reusable multipass post-processing architecture with reorderable shader passes, reusable ping-pong work buffers, persistent previous-frame history, native PNG saving, and fullscreen preview.
The renderer first draws a procedural source into an offscreen framebuffer. Enabled effects then execute in user-defined order through two alternating work targets. The completed frame is displayed and optionally copied into a separate A/B history pair for feedback on the next frame.
Procedural source shader
│
▼
Source framebuffer
│
▼
Pass 1 ──► Work A
│
Pass 2 ──► Work B
│
Pass 3 ──► Work A
│
Pass 4 ──► Work B
│
Pass 5 ──► Work A
│
├──────────────► Screen
│
└──────────────► History write buffer
│
swap A/B next frame
Only two work framebuffers are required regardless of pass count. The history buffers are separate because the work targets are overwritten several times during the current frame.
| Effect | Purpose |
|---|---|
| Feedback trails | Mixes the current image with the previous completed frame |
| Kaleidoscope | Mirrored polar segmentation |
| Edge isolate | Four-tap luminance-gradient extraction |
| RGB split | Chromatic channel displacement |
| Pixelate | Quantized texture coordinates |
| Wave displace | Animated two-axis UV deformation |
| Directional blur | Nine-tap blur along an adjustable direction |
| Color grade | Hue rotation and contrast adjustment |
| Solarize | Threshold-based channel inversion |
Each of the five stack slots can use any effect. The arrows change actual execution order, so moving an effect earlier or later changes which processed texture it receives.
The source remains procedural so the project is self-contained. The compositor can later accept image, video, webcam, FFT, or scene-render textures.
npm install
npm run dev
An explicit [workspace] boundary in src-tauri/Cargo.toml prevents unrelated parent Cargo workspaces from absorbing the project.
Select a pass to edit:
The parameter labels change to match the selected effect.
Presets replace the complete stack and clear stale history.
The official dialog plugin supplies the PNG save dialog:
tauri-plugin-dialog = "2"
The capability grants only core IPC and save-dialog access:
[
"core:default",
"dialog:allow-save"
]
After the user chooses a destination, the PNG bytes are sent to the narrowly scoped Rust write_binary command. toggle_fullscreen addresses only the main WebView window.
The offscreen targets use RGBA8 textures for broad WebGL 1 compatibility. Repeated passes therefore clamp values to the normalized color range. HDR processing would require compatible float-texture/color-buffer extensions or a WebGL 2 implementation.
PNG capture enables preserveDrawingBuffer for reliable snapshot export. A production compositor focused only on maximum presentation performance may choose a dedicated export target instead.
15-tauri-v2-multipass-compositor/
├── package.json
├── README.md
├── V2-FOLDER-MAP.md
├── src/
│ ├── index.html
│ ├── styles.css
│ └── app.js
└── src-tauri/
├── Cargo.toml
├── build.rs
├── tauri.conf.json
├── capabilities/default.json
├── icons/
└── src/main.rs