Benchmarks Desktop
Tauri vs Electron
One web frontend, byte for byte the same, dropped into a Tauri host and an Electron host. Then the questions teams actually fight about: what does it cost to move data across the webview boundary, does the same frontend behave the same in three webviews, what happens to memory after ten minutes of real work, and how big is the thing you ship. Two machines, one per OS. I ship Tauri apps. The runner's public; check my numbers.
IPC across the boundary
Round trips from the frontend to the host and back at 1 KB, 64 KB, 1 MB, 16 MB, on each shell's idiomatic JSON path and on its raw bytes path, plus host-to-frontend push at 1000 events per second. The chart is the idiomatic 1 MB round trip; the headline is the geometric mean over the idiomatic sizes.
Test 1 Idiomatic 1 KB
Median round trip of a 1 KB JSON payload of seeded rows, frontend to host and back, on the path each shell's docs recommend. (LOWER ms = FASTER)
Test 2 Idiomatic 64 KB
Median round trip of a 64 KB JSON payload of seeded rows, frontend to host and back, on the path each shell's docs recommend. (LOWER ms = FASTER)
Test 3 Idiomatic 1 MB
Median round trip of a 1 MB JSON payload of seeded rows, frontend to host and back, on the path each shell's docs recommend. (LOWER ms = FASTER)
Test 4 Idiomatic 16 MB
Median round trip of a 16 MB JSON payload of seeded rows, frontend to host and back, on the path each shell's docs recommend. (LOWER ms = FASTER)
Test 5 Raw 1 KB
Median round trip of 1 KB of seeded bytes on the shell's bytes path: Tauri raw request and response, Electron Uint8Array over invoke. (LOWER ms = FASTER)
Test 6 Raw 64 KB
Median round trip of 64 KB of seeded bytes on the shell's bytes path: Tauri raw request and response, Electron Uint8Array over invoke. (LOWER ms = FASTER)
Test 7 Raw 1 MB
Median round trip of 1 MB of seeded bytes on the shell's bytes path: Tauri raw request and response, Electron Uint8Array over invoke. (LOWER ms = FASTER)
Test 8 Raw 16 MB
Median round trip of 16 MB of seeded bytes on the shell's bytes path: Tauri raw request and response, Electron Uint8Array over invoke. (LOWER ms = FASTER)
Test 9 Push inter-arrival p99
Host emits 1024 B events at 1000 per second for 10 s over the idiomatic event path; the p99 gap between arrivals on the frontend. (LOWER ms = FASTER)
Tauri moved JSON across the boundary 1.44 times faster than Electron over 1 KB, 64 KB, 1 MB and 16 MB (geometric mean of medians)
Webview parity
The same frontend, three engines: a 100,000-row virtualized table scrolled by script, 100,000 particles on Canvas 2D, and a 16-octave noise shader on WebGL2, each 600 frames after 60 warmup frames. Frame time is vsync-capped at 59 Hz so the tail and the main-thread step time carry the signal; the chart is step time per frame pooled over the three tests.
Test 1 Table frame p99
99th percentile interval between animation frames during the table test, mean over passes. (LOWER ms = FASTER)
Test 2 Table step time
Mean main-thread time inside the table test's per-frame step, which vsync cannot hide; a mean because WKWebView reports time in whole milliseconds. (LOWER ms = FASTER)
Test 3 Particles frame p99
99th percentile interval between animation frames during the particles test, mean over passes. (LOWER ms = FASTER)
Test 4 Particles step time
Mean main-thread time inside the particles test's per-frame step, which vsync cannot hide; a mean because WKWebView reports time in whole milliseconds. (LOWER ms = FASTER)
Test 5 Raster frame p99
99th percentile interval between animation frames during the raster test, mean over passes. (LOWER ms = FASTER)
Test 6 Raster step time
Mean main-thread time inside the raster test's per-frame step, which vsync cannot hide; a mean because WKWebView reports time in whole milliseconds. (LOWER ms = FASTER)
WKWebView spent 3.4 ms of main-thread time per frame to Chromium's 5.8, pooled over table, particles and raster
Lifecycle
Cold start after the OS file cache was purged: from the launch command to the frontend's first animation frame, on one wall clock, 20 launches each. Warm start immediately after a prior launch, 10 each. Then a 10-minute soak cycling the three parity tests with a 1 MB round trip every 5 s, footprint sampled every second, 3 soaks each.
Test 1 Cold start
Launch command to first animation frame after the OS file cache was purged, median of 20. (LOWER ms = FASTER)
Test 2 Warm start
Same measurement immediately after a prior launch, median of 10. (LOWER ms = FASTER)
Test 3 Footprint over soak
Attributed memory averaged over the ten-minute soak, after the first five seconds, averaged over soaks. (LOWER MB = FASTER)
Test 4 Footprint peak
Highest attributed memory sampled during the soak, mean over soaks. (LOWER MB = FASTER)
Cold start is a tie at about 1.1 s with the file cache purged; over ten minutes of work Electron averaged 349 MB to Tauri's 561
Size
The installer each shell's default build produced, unsigned, and the bytes on disk once installed. Nothing tuned on either side.
Test 1 Installer
Bytes of the default build's installer. (LOWER MB = FASTER)
Test 2 Installed
Bytes on disk after install and first launch. (LOWER MB = FASTER)
Tauri's installer is 2.5 MB to Electron's 121
Test conditions: rig, corpus, protocol
- Machine
- Mac14,6
- Chip
- Apple M2 Max
- Cores
- 12
- Memory
- 96 GB
- OS
- macOS 26.6.2 (25G83) (arm64)
- Runtime
- bun 1.4.0
- Browser
- Tauri: 21624.5.1.11.3; Electron: Chromium 152.0.7977.78
- Display
- Resolution: 3840 x 2160 (2160p/4K UHD 1 - Ultra High Definition); UI Looks like: 3840 x 2160 @ 60.00Hz; Resolution: 3456 x 2234 Retina
- Warmups
- 5 unmeasured passes
- Measured
- 4 passes per tool
- Process
- Apps launched open -n -W (LaunchServices) so the app is its own responsible process, with --disable-backgrounding-occluded-windows --disable-renderer-backgrounding passed to both shells (Chromium honors them, WKWebView has no equivalent and keeps rendering when covered); candidates interleaved A B / B A pass by pass; 20 cold and 10 warm starts, 4 IPC passes of 50 samples per cell after 5 warmups with small payloads batched to cover the webview's timer resolution, 5 parity passes of 600 frames, 3 ten-minute soaks; a conformance gate with byte-identical digests on both hosts precedes every run
- Cache
- OS file cache purged before every cold start; nothing purged before warm starts
- Output
- FIRST_FRAME marker on the host's stdout timed against the launcher's wall clock; frontend performance.now() for round trips and frames; macOS: physical footprint (phys_footprint, as Activity Monitor's Memory column) summed over every process whose responsible process is the app, which includes WKWebView's WebContent, Networking and GPU helpers for the Tauri candidate and every Electron helper for the Electron candidate; read with /usr/bin/footprint.
What did we learn?
Tauri moved JSON across the boundary 1.44 times faster than Electron on the M2 Max and 1.81 times slower on the Windows box: 'Tauri' is two different shells, and the webview decides which one you get.
| Candidate | IPC across the boundary (ms, lower is better) | Webview parity (ms, lower is better) | Lifecycle (ms, lower is better) | Size (MB, lower is better) |
|---|---|---|---|---|
| Tauri | 15.2 ms | 3.4 ms | 1.133 s | 2.5 MB |
| Electron | 31.2 ms | 5.8 ms | 1.099 s | 121.4 MB |
The headline is a rule I fixed before a single number existed: the geometric mean over four JSON payload sizes on the path each shell's own docs recommend. On macOS Tauri wins every size from 64 KB up, and Electron wins the 1 KB cell (0.09 ms to Tauri's 0.2, and if your app makes ten thousand tiny calls, that's the cell you care about). On Windows it flips: Electron takes every idiomatic size but 16 MB, and Tauri's raw bytes path through WebView2 runs about five times slower than Electron's, the exact reverse of the Mac. Same Rust, same frontend, different webview. The parity section says the same thing from the other side: the system webviews spend less main-thread time per frame than Electron's Chromium, almost all of it in the Canvas 2D particles, while the WebGL2 raster and the virtualized table are within a frame everywhere. Startup is a tie on the Mac with the file cache purged (about 1.1 s either way) and Electron's on Windows, where I had no purge, so read that row as warm. Memory flips too: over ten minutes Electron averaged 349 MB to Tauri's 561 on macOS, where WKWebView's content process climbed past 2 GB at the end of the table segments, and on Windows Tauri averaged 232 to Electron's 241. Installers are 2.5 and 1.8 MB against 121 and 106. The scorecards below carry the section means. The limitations say what none of this means.
What this does not prove
- Windows cold starts are not cold: no file-cache purge was available on that rig, so its cold row is a launch after a settle, and the Windows lifecycle verdict reads warm start against warm start.
- Two operating systems, one machine each, and no Linux: Tauri's webkit2gtk leg is the one most often complained about and it is not here yet.
- Start is the launch command to the frontend's first animation frame on one wall clock, not click to pixel; screen capture on both rigs is future work.
- Frame time is vsync-capped inside every webview, so the parity charts read from the tail and from main-thread step time, not from the mean.
- Both shells are built with their scaffolded defaults, unsigned, secure defaults on; nothing is tuned for size or speed on either side.
- The Tauri candidate's webview is the operating system's and changes without a rebuild; the version measured is in each run file.
- Memory is attributed by responsible process on macOS and by process tree on Windows; those are different statistics and are never compared across rigs.
- The Windows leg runs in a logged-on interactive desktop session reached through a scheduled task, because desktop shells cannot run headless.
- On the M2 Max the cold, warm and IPC passes and the first parity passes ran before the harness disabled Chromium's occluded-window backgrounding; one Electron and one Tauri parity pass hung under a covering window and were discarded, and every remaining pass ran with the switches. The run file's notes carry the exact sequence.
- Tauri's raw bytes path on Windows is measured as WebView2 delivers it; nothing was done to find out why it is five times slower there than on macOS, and that is a finding, not a fault in the runner.
Rerun it yourself: the runner and configuration are in the repository, and the methodology page covers what every run holds constant. Think a result is wrong? Open an issue with your rig and your samples.
Tauri is a trademark of the Tauri Programme within The Commons Conservancy; the tauri crates and CLI are MIT or Apache-2.0. Electron is a trademark of the OpenJS Foundation; Electron is MIT-licensed. Chromium is a Google project, BSD-3-Clause.