# Map Rendering Performance on Linux WebKitGTK in GeoLibre: Bottlenecks and Mitigations

> Discover WebKitGTK map rendering performance bottlenecks in GeoLibre on Linux. Learn how slow WebGL tile uploads cause frame rate drops and explore potential mitigations.

- Repository: [Open Geospatial Solutions/GeoLibre](https://github.com/opengeos/GeoLibre)
- Tags: performance
- Published: 2026-08-15

---

**GeoLibre's Linux desktop build suffers severe frame rate drops (to 5–10 FPS) during map panning due to WebKitGTK's slow WebGL tile upload pipeline, with each tile integration taking ~125 ms compared to a few milliseconds in Chromium.**

The opengeos/GeoLibre project provides a cross-platform mapping application built on MapLibre GL. On Linux, the desktop application relies on the system WebKitGTK WebView rather than a Chromium-based engine. This architectural choice creates a significant **map rendering performance** gap between the Linux desktop build and browser-based deployments.

## What Causes the WebKitGTK Performance Degradation

The performance degradation manifests specifically when **tile layers are active and loading**. According to the GeoLibre source analysis in [[`docs/architecture.md`](https://github.com/opengeos/GeoLibre/blob/main/docs/architecture.md)](https://github.com/opengeos/GeoLibre/blob/main/docs/architecture.md#L53), the bottleneck resides entirely within WebKitGTK's graphics pipeline—not in JavaScript execution or network latency.

### Observed Frame Rate Behavior

| Scenario | Measured FPS |
|----------|--------------|
| Blank map (no tile layer) | Steady **≈ 60 FPS** at any zoom level |
| Tile layer loading | **Single-digit FPS** (5–10 FPS) |
| After tiles finish loading | Immediate recovery to **≈ 60 FPS** |

This pattern directly implicates **per-tile WebGL processing** as the culprit. Each newly loaded tile triggers a synchronous main-thread operation sequence: WebGL texture upload for raster tiles (or vertex-buffer upload for vector tiles), followed by a fade-in repaint cycle.

### The 125 ms Tile Integration Problem

WebKitGTK's tile integration cycle has been measured at **~125 ms per tile**. Chromium completes identical work in **a few milliseconds**—a 25–40× performance differential.

The heavy operations occur in WebKitGTK's WebGL implementation and compositor pipeline:

- Driver command-stream flush
- Texture-mapper surface composition

Notably, the JavaScript side-chain remains efficient. Vector tile parsing and bucket building execute in MapLibre's Web Workers without blocking. This confirms the bottleneck is **browser-engine-specific**, not algorithmic.

## Ruled-Out Causes

The GeoLibre team systematically eliminated alternative explanations through instrumentation:

- **Software rendering**: GPU acceleration confirmed active (Intel i915)
- **GPU saturation**: Render engine idle ~20%
- **Tauri IPC overhead**: File reads (~126 ms for 22 MB GeoJSON) occur off critical path
- **JSON parsing**: ~36 ms for large payloads, negligible for tiles
- **KWin compositor latency**: No measurable impact
- **MapLibre configuration**: `renderWorldCopies`, projection settings, `preserveDrawingBuffer` all ruled out

## Source Code Locations

Key implementation files document the WebKitGTK behavior and detection:

| File | Purpose |
|------|---------|
| [[`docs/architecture.md`](https://github.com/opengeos/GeoLibre/blob/main/docs/architecture.md)](https://github.com/opengeos/GeoLibre/blob/main/docs/architecture.md#L53) | Primary performance documentation with timing measurements |
| [[`packages/plugins/src/plugins/maplibre-raster.ts`](https://github.com/opengeos/GeoLibre/blob/main/packages/plugins/src/plugins/maplibre-raster.ts)](https://github.com/opengeos/GeoLibre/blob/main/packages/plugins/src/plugins/maplibre-raster.ts#L1060) | WebKitGTK fallback comments, GPU upload cost notes |
| [[`packages/map/src/map-controller.ts`](https://github.com/opengeos/GeoLibre/blob/main/packages/map/src/map-controller.ts)](https://github.com/opengeos/GeoLibre/blob/main/packages/map/src/map-controller.ts#L2458) | WebKit-specific container resize handling |
| [[`tests/is-mobile.test.ts`](https://github.com/opengeos/GeoLibre/blob/main/tests/is-mobile.test.ts)](https://github.com/opengeos/GeoLibre/blob/main/tests/is-mobile.test.ts) | User-agent detection patterns for Linux WebKitGTK |
| [[`apps/geolibre-desktop/src/lib/diagnostics.ts`](https://github.com/opengeos/GeoLibre/blob/main/apps/geolibre-desktop/src/lib/diagnostics.ts)](https://github.com/opengeos/GeoLibre/blob/main/apps/geolibre-desktop/src/lib/diagnostics.ts#L42) | WebKit error classification for debugging |

## Measuring Performance Yourself

Reproduce the FPS measurements using this console script injected into the WebView developer tools:

```javascript
let frameCount = 0;
let start = performance.now();
(function loop(now) {
  frameCount++;
  if (now - start >= 1000) {
    console.log('FPS', frameCount);
    frameCount = 0;
    start = now;
  }
  requestAnimationFrame(loop);
})(performance.now());

```

Execute while panning the map. The console output will show **FPS collapse during tile fetches** and immediate recovery once the tile queue empties.

## Proposed Mitigations for WebKitGTK

Because the bottleneck is engine-inherent, optimizations must **reduce tiles processed per pan**. The GeoLibre repository identifies three conditional mitigations (not yet implemented):

1. **`maxTileCacheSize` increase** — Retain more tiles in memory to reduce new upload frequency
2. **512 px raster tiles** — Larger tiles mean fewer fetches per viewport at equivalent resolution
3. **`fadeDuration: 0`** — Eliminate fade-in repaint overhead

All three should be **gated behind WebKitGTK detection** to preserve full visual fidelity on Chromium builds. Detection logic can leverage the user-agent patterns in [[`tests/is-mobile.test.ts`](https://github.com/opengeos/GeoLibre/blob/main/tests/is-mobile.test.ts)](https://github.com/opengeos/GeoLibre/blob/main/tests/is-mobile.test.ts).

## Platform Scope

This **map rendering performance** limitation affects:

- ✅ Linux desktop builds using WebKitGTK
- ❌ Browser builds (Chromium-based, optimal performance)
- ❓ macOS and Windows WebViews (untested in the current analysis)

## Summary

- **Map rendering performance on Linux WebKitGTK** degrades to 5–10 FPS during tile loading due to ~125 ms synchronous WebGL upload cycles
- **Root cause**: WebKitGTK's main-thread GPU pipeline, not JavaScript execution
- **Impact**: Isolated to Linux desktop builds; browser deployments unaffected
- **Mitigation strategy**: Reduce tile processing volume through caching, larger tiles, and disabled fade-in—conditionally applied via user-agent detection

## Frequently Asked Questions

### How can I detect if my GeoLibre build uses WebKitGTK?

Check the user agent string in [[`tests/is-mobile.test.ts`](https://github.com/opengeos/GeoLibre/blob/main/tests/is-mobile.test.ts)](https://github.com/opengeos/GeoLibre/blob/main/tests/is-mobile.test.ts). Linux WebKitGTK emits distinct identifiers containing "WebKitGTK" or compatible version strings that differ from Chromium's "Chrome" token.

### Does this affect vector tiles or only raster tiles?

Both tile types trigger the same bottleneck. Raster tiles incur WebGL texture upload overhead; vector tiles require vertex-buffer uploads. The ~125 ms integration time applies to both pipeline paths in WebKitGTK.

### Why not switch GeoLibre Linux to a Chromium WebView?

The analysis does not address this architectural decision. Current implementation uses system-provided WebViews for minimal bundle size and native integration. A Chromium Embedded Framework would increase distribution overhead.

### Will `fadeDuration: 0` cause visual flickering?

Eliminating fade-in produces immediate tile visibility changes. On WebKitGTK this trade-off is necessary for usable panning performance. On Chromium builds, the default fade should be preserved for smooth visual transitions.