Benchmarks · An open suite

Benchmarks, in the open.

One reproducible suite for desktop web runtimes. Same machine, same workloads, every run published — Kurogane measured against the alternatives, one runtime at a time.

Runtimes

  • Kurogane0.0.3

    subject

  • Electron42

    measured

  • Tauri2

    in progress

  • Wails2

    planned

  • NW.js

    planned

Want a runtime added? Open an issue

Suites

3 live · 3 coming

  • Startup time

    launch to first paint

    coming soon

  • Memory

    idle and under load

    coming soon

  • Bundle size

    installer, per platform

    coming soon

Latest run · Kurogane 0.0.3 · Chromium 147 · Windows 11 · Ryzen 7 5700X3D · median of 5 runs

01 — Latency

Round-trip latency

Median latency per test. The lighter extension on each bar runs from the median to the p95 — shorter extensions mean more consistent execution.

  • Kurogane — engraved
  • Electron 42 — hatched
  • tail to p95
Fig. 1Median per workload; the faint tail runs to p95. Engraved bars: Kurogane. Hatched bars: Electron. Rows are scaled to themselves.

Speedup, by weight

electron median ÷ kurogane median

Fig. 2Electron median ÷ Kurogane median; the dotted line is parity. The heavier the payload, the wider the margin.

02 — Throughput

Throughput that keeps climbing

Effective round-trip throughput against payload size, on a log scale — workloads span 1 KB to 20 MB. Past ~2 MB Kurogane moves to a shared-memory path; Electron flattens, then falls.

  • Kurogane
  • Electron 42
Fig. 3MB/s by payload size on a log axis. The engraved field is Kurogane; the dashed line is Electron. Values use each test’s median run.

03 — Stability

Run-to-run variance

Five measured runs per test, plotted as deviation from each runtime’s own median so every workload shares one axis. Boxes span Q1–Q3, whiskers min–max. Tighter is steadier.

  • Kurogane
  • Electron 42
Fig. 4Five runs, each against its own median. Hatched boxes: Kurogane. Open boxes: Electron. Whiskers span min–max.

04 — Data

Every number, in full

All six tests: median, p95 and derived throughput for both runtimes — plus every measured run, so you can check our math.

Every workload: median, p95 and throughput for Kurogane 0.0.3 and Electron 42, and Kurogane’s speedup (Electron median divided by Kurogane median)
Kurogane 0.0.3Electron 42
WorkloadMedianp95MB/sMedianp95MB/sSpeedup
RPC1,000 JSON echo calls118.8 ms121.2 msnot applicable158.9 ms172.5 msnot applicable1.34×
1 KB1 KB × 1,000129.6 ms152.9 ms15.8169.9 ms172.7 ms12.11.31×
1 MB1 MB × 100580.0 ms587.9 ms344.8644.1 ms650.5 ms310.51.11×
5 MB5 MB × 1001991.5 ms2012.5 ms502.12790.8 ms3098.2 ms358.31.40×
10 MB10 MB × 501498.5 ms1509.6 ms667.32575.2 ms2583.9 ms388.31.72×
20 MB20 MB × 1004943.3 ms4996.4 ms809.214058.9 ms14526.2 ms284.52.84×
Runs (ms)KuroganeElectron 42
RPC117.7, 121.2, 118.6, 119.2, 118.8172.5, 165.9, 157.1, 157.3, 158.9
1 KB129.6, 126.8, 127.6, 129.7, 152.9168.3, 168.8, 170.7, 169.9, 172.7
1 MB578.4, 582.9, 577.8, 587.9, 580.0647.6, 641.9, 650.5, 644.1, 640.6
5 MB1962.1, 1970.3, 1991.8, 2012.5, 1991.53098.2, 2905.9, 2790.8, 2738.8, 2565.8
10 MB1509.6, 1495.9, 1498.5, 1506.1, 1496.02575.2, 2539.9, 2539.7, 2579.0, 2583.9
20 MB4996.4, 4969.4, 4937.0, 4911.4, 4943.314526.2, 14312.4, 14058.9, 13074.6, 12951.4

Raw dataset

Every run of every workload, for both runtimes, with the test machine — as JSON, for verification and independent analysis.

Download JSON

benchmarks.json · 5.1 KB

05 — Methodology

How this was measured

One round trip, taken apart. Follow the numbers — each one is explained below.

Annotated diagram of one measured round trip between the Renderer and the Host. Each numbered marker links to its note below.

per test

warmup
3 discarded
5 measured
median

Renderer

JavaScript

performance.now()t₀ → t₁

Uint8Array · allocated once, reused

GC · never forced

request · invokeBinary / ipcRenderer.invoke

×2

response — the echo

Kurogane · payloads over ~2 MB move through shared memory — no copies

Host

Rust · Kurogane / Node · Electron

echo handlerreturns the same bytes
shared memory region
(2 × payload × iterations) ÷ median time
  1. 1. Timed end to end

    A stopwatch starts when the renderer sends a request and stops when the full response is back — wall-clock time, via performance.now().

  2. 2. Warmed up, then measured

    An untimed warmup for the whole suite, then 3 thrown-away iterations per test, then 5 measured runs. We report the median.

  3. 3. Identical bytes

    One pre-allocated Uint8Array, reused every iteration, for both runtimes — so we time the IPC, not memory allocation.

  4. 4. Default transports only

    Large binary payloads go through Kurogane’s shared memory automatically. Electron uses its standard path. Nothing is tuned for the test.

  5. 5. Throughput counts both ways

    (2 × payload × iterations) ÷ median time. The ×2 is the payload going out plus the echo coming back.

  6. 6. Real-world garbage collection

    GC is never forced and responses aren’t kept, so collection runs whenever the runtime decides — just like in your app.

Measured on

OS
Windows 11 25H2
CPU
Ryzen 7 5700X3D
RAM
16 GB DDR4-3200
Chromium
147.0.10
Kurogane
0.0.3
Electron
42.0.1
Node
22.16.0

Both runtimes receive byte-for-byte identical payloads, on the same machine, with the same workload suite — only the IPC call differs.

Don’t take
our word for it.

Clone the repo, run the suite on your own machine, and tell us what you see.