# How to Run GeoPandas/Shapely Tools in the Browser via Pyodide

> Run GeoPandas and Shapely in your browser with GeoLibre and Pyodide. Experience identical geometry processing without network latency using pure-Python vector logic.

- Repository: [Open Geospatial Solutions/GeoLibre](https://github.com/opengeos/GeoLibre)
- Tags: how-to-guide
- Published: 2026-08-02

---

**GeoLibre executes GeoPandas and Shapely directly in the browser by loading a Pyodide Web Worker that runs the same pure-Python vector logic used by the server-side sidecar, ensuring identical geometry processing without network latency.**

The GeoLibre project provides a third vector engine that brings server-grade geospatial processing client-side using Pyodide—a WebAssembly port of CPython. By sharing the underlying Python implementation between the browser and server environments, you can run complex GeoPandas/Shapely operations locally while maintaining a single source of truth for your vector tools.

## How the Pyodide Vector Engine Works

GeoLibre’s browser-based engine relies on a multi-layer architecture that bridges TypeScript frontend code with a Python runtime running inside a Web Worker.

### Shared Python Logic

The core geometry functions reside in [`backend/geolibre_server/geolibre_server/vector_ops.py`](https://github.com/opengeos/GeoLibre/blob/main/backend/geolibre_server/geolibre_server/vector_ops.py). This file contains pure-Python implementations of vector operations using GeoPandas and Shapely, designed to run anywhere CPython is available. Both the server-side sidecar and the browser-based Pyodide engine import this module, guaranteeing that buffer operations, intersections, and unions produce identical coordinates regardless of execution environment.

### Build-Time Integration

A custom Vite plugin located at [`apps/geolibre-desktop/vite-plugins/copy-vector-ops.ts`](https://github.com/opengeos/GeoLibre/blob/main/apps/geolibre-desktop/vite-plugins/copy-vector-ops.ts) automatically copies [`vector_ops.py`](https://github.com/opengeos/GeoLibre/blob/main/vector_ops.py) into the frontend bundle as [`src/lib/pyodide/vector_ops.generated.py`](https://github.com/opengeos/GeoLibre/blob/main/src/lib/pyodide/vector_ops.generated.py) during the build process. This ensures the browser always executes the same code version deployed to the server, eliminating drift between client and server logic.

### Web Worker Runtime

The browser loads [`public/pyodide/pyodide-worker.js`](https://github.com/opengeos/GeoLibre/blob/main/public/pyodide/pyodide-worker.js), which initializes the Pyodide environment inside a dedicated Web Worker thread. This script fetches the Pyodide distribution from a CDN (or self-hosted mirror), installs the `geopandas` package, and exposes the `run_vector_tool` function that accepts JSON-serialized GeoJSON and returns processed results.

## Configuring the Pyodide Environment

Before executing Python code, you must configure the runtime source and initialize the console.

### CDN vs. Self-Hosted

Configuration is managed in [`apps/geolibre-desktop/src/lib/pyodide/pyodide-config.ts`](https://github.com/opengeos/GeoLibre/blob/main/apps/geolibre-desktop/src/lib/pyodide/pyodide-config.ts). By default, the system loads Pyodide from the public jsDelivr CDN at `https://cdn.jsdelivr.net/pyodide/v${PYODIDE_VERSION}/full/`. For offline deployments or security-hardened environments, set the `VITE_PYODIDE_INDEX_URL` environment variable to point to a local mirror (e.g., `https://your-host/pyodide/v0.27.7/full/`).

### Runtime Initialization

The [`apps/geolibre-desktop/src/lib/pyodide/pyodide-console.ts`](https://github.com/opengeos/GeoLibre/blob/main/apps/geolibre-desktop/src/lib/pyodide/pyodide-console.ts) module handles bootstrapping. It creates the Pyodide runtime, registers a JavaScript module named `_geolibre_js` that exposes helper functions for DOM and network access, and executes the console’s Python bootstrap code to warm up the interpreter for subsequent calls.

## Running Vector Tools in the Browser

Once configured, you can invoke GeoPandas operations from your TypeScript application using the provided loader functions.

### Initializing the Console

Import the initialization function to prepare the Pyodide environment. This typically runs once when the application starts:

```typescript
import { initConsoleRuntime } from '../lib/pyodide/pyodide-console';

// Warm up the interpreter (downloads ~tens of MB on first run)
await initConsoleRuntime();

```

### Executing GeoPandas Operations

Use `runVectorToolInPyodide` from [`apps/geolibre-desktop/src/lib/pyodide/pyodide-vector-loader.ts`](https://github.com/opengeos/GeoLibre/blob/main/apps/geolibre-desktop/src/lib/pyodide/pyodide-vector-loader.ts) to send requests to the worker. This function serializes your GeoJSON input, invokes `run_vector_tool` inside the Pyodide environment, and streams progress updates back to the UI:

```typescript
import { runVectorToolInPyodide } from '../lib/pyodide/pyodide-vector-loader';

async function runBufferTool(geojson: string, distance: number) {
  const result = await runVectorToolInPyodide({
    toolId: 'buffer',
    layerId: 'my-layer',
    inputGeoJSON: geojson,
    params: { distance },
    onProgress: (msg) => console.log(msg), // UI progress hook
  });
  return result;
}

```

Inside the worker, the following Python executes in [`vector_ops.generated.py`](https://github.com/opengeos/GeoLibre/blob/main/vector_ops.generated.py):

```python
from geopandas import GeoDataFrame
from shapely.geometry import shape

def run_vector_tool(tool_id: str, geojson_str: str, **params):
    gdf = GeoDataFrame.from_features(shape(geojson_str))
    if tool_id == 'buffer':
        gdf['geometry'] = gdf.buffer(params['distance'])
    # Additional tool implementations...

    return gdf.__geo_interface__

```

## UI Integration and User Experience

GeoLibre’s frontend provides a seamless interface for selecting the execution engine and handling runtime downloads.

### Engine Selection

The `VectorToolsDialog` component in [`apps/geolibre-desktop/src/components/processing/VectorToolsDialog.tsx`](https://github.com/opengeos/GeoLibre/blob/main/apps/geolibre-desktop/src/components/processing/VectorToolsDialog.tsx) presents users with three engine options: `client` (JavaScript), `sidecar` (server Python), or `pyodide` (browser Python). When users select **Pyodide**, the dialog invokes `runVectorToolInPyodide()` instead of making HTTP requests to the backend.

### First-Run Download Behavior

The UI displays a notification during the initial Pyodide execution, informing users that the Python runtime (approximately tens of megabytes) is downloading. Subsequent operations reuse the warmed-up runtime, providing near-instant execution for additional vector tools during the same session.

## Summary

- **Shared logic**: The [`vector_ops.py`](https://github.com/opengeos/GeoLibre/blob/main/vector_ops.py) module provides a single source of truth for geometry operations used by both server-side and browser-based engines.
- **Build integration**: The Vite plugin [`copy-vector-ops.ts`](https://github.com/opengeos/GeoLibre/blob/main/copy-vector-ops.ts) ensures the frontend bundle contains the latest Python code.
- **Web Worker execution**: Pyodide runs inside [`pyodide-worker.js`](https://github.com/opengeos/GeoLibre/blob/main/pyodide-worker.js), keeping the main thread responsive during heavy geospatial computations.
- **Flexible hosting**: Configure `VITE_PYODIDE_INDEX_URL` to use public CDNs or private mirrors for the Pyodide distribution.
- **Identical results**: Because both environments execute the same GeoPandas/Shapely code, buffer distances and intersection areas remain consistent across client and server.

## Frequently Asked Questions

### How does GeoLibre ensure results match between browser and server?

Both environments import the same [`vector_ops.py`](https://github.com/opengeos/GeoLibre/blob/main/vector_ops.py) file. The Vite build process copies this file into the frontend as [`vector_ops.generated.py`](https://github.com/opengeos/GeoLibre/blob/main/vector_ops.generated.py), ensuring the Pyodide engine and the sidecar execute identical logic when calling `run_vector_tool`.

### What is the performance cost of running GeoPandas in the browser?

The initial load requires downloading the Pyodide runtime and packages (tens of megabytes), which may take several seconds on slower connections. Once cached, vector operations execute at near-native speed inside the Web Worker, though very large datasets may still benefit from the server-side `sidecar` engine due to memory constraints in the browser.

### Can I deploy GeoLibre with an offline Pyodide mirror?

Yes. Set the `VITE_PYODIDE_INDEX_URL` environment variable to point to a self-hosted directory containing the Pyodide distribution files. The configuration in [`pyodide-config.ts`](https://github.com/opengeos/GeoLibre/blob/main/pyodide-config.ts) will use this URL instead of the public jsDelivr CDN, enabling offline or air-gapped deployments.

### Which vector tools are available in the Pyodide engine?

Any tool defined in the shared [`vector_ops.py`](https://github.com/opengeos/GeoLibre/blob/main/vector_ops.py) catalog is available, including buffering, intersection, union, and dissolving operations. The `VectorToolsDialog` filters the available tools based on the selected engine, but the underlying Python implementation remains consistent across all three execution modes.