# Maximum Zoom Level for PMTiles Conversion in GeoLibre: How It Syncs with the WASM Binary

> Discover the maximum zoom level for PMTiles conversion in GeoLibre, zoom level 18, and how it syncs with the WASM binary for consistent UI, validation, and testing.

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

---

**The maximum zoom level for PMTiles conversion in GeoLibre is **zoom level 18**, enforced by a single source-of-truth constant that synchronizes the UI, validation logic, and test suite with the underlying WebAssembly binary.**

In `opengeos/GeoLibre`, client-side PMTiles conversion relies on a Rust-based WASM tool that hardcodes its own zoom ceiling. Rather than scattering magic numbers across the codebase, the project exports one constant—`MAX_VECTOR_PMTILES_ZOOM`—to keep every layer of the stack aligned. This article breaks down where that limit lives, how it propagates through the system, and what happens when you try to exceed it.

---

## Where the Limit Is Defined

The authoritative definition sits in [`packages/processing/src/wasm-convert.ts`](https://github.com/opengeos/GeoLibre/blob/main/packages/processing/src/wasm-convert.ts), the TypeScript wrapper around the `vector_to_pmtiles` WASM tool:

```typescript
// packages/processing/src/wasm-convert.ts
/** The deepest zoom `vector_to_pmtiles` accepts; past it the tool exits with
 * `validation error: max_zoom must be <= 18`. */
export const MAX_VECTOR_PMTILES_ZOOM = 18;

```

This constant serves two critical purposes:

- **It documents the binary's behavior**—the underlying Rust tool aborts with a validation error if `max_zoom` exceeds 18.
- **It acts as the single export** that every consumer (UI, validation, tests) imports to stay synchronized.

The WASM binary itself does not expose this limit programmatically; it simply fails at runtime. By hoisting the constant into TypeScript, GeoLibre creates a compile-time contract that prevents silent mismatches.

---

## UI Synchronization: Parsing Zoom Inputs

The desktop application's conversion dialog enforces the limit at the point of user input. In [`apps/geolibre-desktop/src/components/processing/ConversionDialog.tsx`](https://github.com/opengeos/GeoLibre/blob/main/apps/geolibre-desktop/src/components/processing/ConversionDialog.tsx), the `parseZoomRange` utility receives `MAX_VECTOR_PMTILES_ZOOM` as an explicit ceiling:

```tsx
// apps/geolibre-desktop/src/components/processing/ConversionDialog.tsx
// Parse user-entered zoom range, capping it at the WASM engine limit.
const zooms = parseZoomRange(minZoom, maxZoom, MAX_VECTOR_PMTILES_ZOOM);

```

This call on **line 1115** ensures that:

- Values above 18 are rejected or clamped before reaching the WASM layer.
- The error message shown to users matches the actual binary limitation.

By threading the constant through the parsing function, the UI never promises a zoom level the engine cannot deliver.

---

## Test-Level Safeguards

The test suite in [`tests/wasm-convert.test.ts`](https://github.com/opengeos/GeoLibre/blob/main/tests/wasm-convert.test.ts) provides the final synchronization checkpoint. It verifies both that zoom 18 succeeds and that zoom 19 triggers the expected runtime error:

```typescript
// tests/wasm-convert.test.ts
it("accepts the documented maximum zoom and rejects one deeper", async () => {
  const atCap = await tileVectorToPmtiles(
    { name: "points.geojson", data: pointsGeoJson },
    "cap.pmtiles",
    { minZoom: MAX_VECTOR_PMTILES_ZOOM, maxZoom: MAX_VECTOR_PMTILES_ZOOM },
  );
  assert.ok(hasMagic(atCap.data, PMTILES_MAGIC));

  await assert.rejects(
    tileVectorToPmtiles(
      { name: "points.geojson", data: pointsGeoJson },
      "over.pmtiles",
      { maxZoom: MAX_VECTOR_PMTILES_ZOOM + 1 },
    ),
    /max_zoom must be <= 18/i,
  );
});

```

This test on **line 33** creates a hard link between the constant and the binary's actual behavior. If the WASM tool's limit changes (e.g., in a future Rust upgrade), the test fails—forcing developers to update `MAX_VECTOR_PMTILES_ZOOM` rather than letting the UI and binary drift out of sync.

---

## How the Synchronization Chain Works

| Component | Mechanism | File Path |
|-----------|-----------|-----------|
| **WASM binary** | Hardcoded runtime validation | Bundled Rust tool (source in `vector_to_pmtiles`) |
| **TypeScript constant** | `MAX_VECTOR_PMTILES_ZOOM = 18` | [`packages/processing/src/wasm-convert.ts`](https://github.com/opengeos/GeoLibre/blob/main/packages/processing/src/wasm-convert.ts) |
| **UI validation** | `parseZoomRange` consumes the constant | [`apps/geolibre-desktop/src/components/processing/ConversionDialog.tsx`](https://github.com/opengeos/GeoLibre/blob/main/apps/geolibre-desktop/src/components/processing/ConversionDialog.tsx) |
| **Test verification** | Asserts success at 18, rejection at 19 | [`tests/wasm-convert.test.ts`](https://github.com/opengeos/GeoLibre/blob/main/tests/wasm-convert.test.ts) |

When the WASM binary is updated, developers modify **one location**: the constant in [`wasm-convert.ts`](https://github.com/opengeos/GeoLibre/blob/main/wasm-convert.ts). The change propagates automatically:

1. UI validation adjusts its ceiling.
2. Tests validate the new boundary (or fail until aligned).
3. Documentation comments in the source reflect the current limit.

This pattern eliminates the "works in UI, fails in engine" class of bugs common in WASM-frontend integrations.

---

## What Happens If You Bypass the Constant

Attempting to pass `maxZoom: 19` directly to `tileVectorToPmtiles` triggers a Rust-side assertion before any tiles are generated:

```

validation error: max_zoom must be <= 18

```

The WASM tool exits non-gracefully, returning no output and logging the error through the standard error channel. GeoLibre's wrapper catches this and rejects the promise, but no partial PMTiles archive is created.

This rigid behavior explains why the constant exists: the binary offers no negotiation, only enforcement. The TypeScript layer adds user-friendly handling and early validation to avoid wasting computation on doomed conversion attempts.

---

## Summary

- **Maximum zoom level for PMTiles conversion** is **18**, hardcoded in the `vector_to_pmtiles` WASM tool.
- **Synchronization** relies on `MAX_VECTOR_PMTILES_ZOOM`, a single exported constant from [`packages/processing/src/wasm-convert.ts`](https://github.com/opengeos/GeoLibre/blob/main/packages/processing/src/wasm-convert.ts).
- **UI enforcement** occurs in [`ConversionDialog.tsx`](https://github.com/opengeos/GeoLibre/blob/main/ConversionDialog.tsx) via `parseZoomRange`, which accepts the constant as its ceiling parameter.
- **Test coverage** in [`tests/wasm-convert.test.ts`](https://github.com/opengeos/GeoLibre/blob/main/tests/wasm-convert.test.ts) ensures the constant matches runtime behavior, catching WASM updates that change the limit.
- **Maintenance** requires editing only the constant; all consumers update automatically.

---

## Frequently Asked Questions

### Why is the PMTiles zoom limit 18 and not higher?

The underlying Rust tool `vector_to_pmtiles` enforces this ceiling to control memory usage and processing time during client-side conversion. According to the GeoLibre source code, higher zoom levels would exponentially increase tile count, exceeding the WASM memory constraints of browser environments. The limit balances detail coverage against practical runtime performance on typical hardware.

### How do I change the maximum zoom level if my use case requires deeper tiles?

You must rebuild the WASM binary itself after modifying the Rust source, then update `MAX_VECTOR_PMTILES_ZOOM` in [`packages/processing/src/wasm-convert.ts`](https://github.com/opengeos/GeoLibre/blob/main/packages/processing/src/wasm-convert.ts) to match. The test suite will confirm whether your rebuilt binary actually supports the new limit. Note that GeoLibre does not support runtime configuration of this parameter—the constant and binary must stay manually aligned.

### What error message appears when requesting zoom 19?

The WASM binary returns: `validation error: max_zoom must be <= 18`. This string is regex-matched in [`tests/wasm-convert.test.ts`](https://github.com/opengeos/GeoLibre/blob/main/tests/wasm-convert.test.ts) to verify correct rejection. In the desktop UI, users see a friendlier validation error before the conversion even attempts to start, since `parseZoomRange` intercepts out-of-range values.

### Does GeoLibre support different zoom limits for raster versus vector PMTiles?

The source code references `MAX_VECTOR_PMTILES_ZOOM` specifically, indicating the constant governs **vector** tile conversion. Raster PMTiles conversion—if implemented—would likely use a separate constant or inherit from the WASM tool's raster-specific limits. As of the current codebase, vector conversion is the primary WASM-powered path with this documented ceiling.