# Build with RouteWasm

RouteWasm runs Valhalla routing in a Web Worker using WebAssembly. The SDK is free. The hosted service packages routing graph tiles and Protomaps vector map data so applications can reuse downloaded data instead of paying for each local route calculation.

## Start here

1. Read https://routewasm.dev/llms.txt and this guide.
2. Create a project at https://routewasm.dev/#start. Signup redirects to https://app.routewasm.dev/quickstart/ with your public project ID. Signed-in quickstarts use Amsterdam with worldwide hosted maps and routing; the account-free SDK default remains Frankfurt.
3. Download the MapLibre starter or integrate the SDK. The website field is optional while developing: blank allows any valid browser origin. Localhost and the hosted quickstart stay allowed when restrictions are set.
4. **Before publishing, add the deployed HTTPS origin(s) in the account under "Where your app runs".** The public project ID is visible in browser code; without a restriction any other website can use it and spend the project's monthly allowance, and new sessions pause at 125% of it (no overage charges). Origin restrictions bind browser tokens but do not authenticate non-browser clients; native apps and servers should use a private server API key with `/v1/tile-auth`.
4. Run a cold route, repeat it, then try nearby endpoints. Measure downloaded bytes as well as elapsed time.

The public Frankfurt demo does not require an account. Worldwide map and routing delivery is hosted on Cloudflare; use the quickstart and account descriptor for the current release URLs. Do not assume a dataset is ready simply because its hostname resolves. Hosted hybrid routing is planned. The general preview is free and uncapped. Stripe test-mode checkout is available at /app/plans when the operator configures a test key: no real charges. Plan activation follows a signature-verified Stripe webhook, not the redirect. Test-paid projects can enable delayed calendar-month allowance enforcement; general preview projects remain uncapped.

## Working public demo

This small Frankfurt example works without an account. Use routewasm 0.3.1 or later, or the downloadable starter.

```js
import { RouteWasmClient } from 'routewasm';
const client = new RouteWasmClient();
await client.init();
const route = JSON.parse(await client.call('route', JSON.stringify({
  locations: [{ lat: 50.1069, lon: 8.6638 }, { lat: 50.1214, lon: 8.6567 }],
  costing: 'bicycle',
})));
console.log(route.trip?.summary || route.error);
```

## Hosted browser integration

Use this after the account descriptor reports `routingReady: true`.

Install `routewasm`. For maps also install `maplibre-gl`, `pmtiles` and `@protomaps/basemaps`. Serve your app over HTTPS or localhost; workers and persistent browser storage need the appropriate browser environment. The SDK is for browsers and web-based app shells, not a supported native iOS/Android binding.

```js
import { RouteWasmClient } from 'routewasm';

const projectId = 'YOUR_PUBLIC_PROJECT_ID';
const installationKey = 'routewasm-installation';
let installation = localStorage.getItem(installationKey);
if (!/^[a-f0-9]{64}$/.test(installation || '')) {
  installation = Array.from(crypto.getRandomValues(new Uint8Array(32)),
    b => b.toString(16).padStart(2, '0')).join('');
  localStorage.setItem(installationKey, installation);
}

async function mint() {
  const response = await fetch('https://app.routewasm.dev/v1/device-session', {
    method: 'POST', headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ projectId, installation }),
  });
  if (!response.ok) throw new Error(`Hosted access failed: ${response.status}`);
  return response.json();
}

const client = new RouteWasmClient();
await client.init({ tileAuth: await mint(), refreshTileAuth: mint });
const result = JSON.parse(await client.call('route', JSON.stringify({
  locations: [{ lat: 50.1069, lon: 8.6638 }, { lat: 50.1214, lon: 8.6567 }],
  costing: 'bicycle',
})));
if (!result.trip) throw new Error(result.error || 'No route returned');
console.log(result.trip.summary);
```

Blank website settings allow any valid browser origin; a configured allowlist restricts which browser origins can obtain sessions. Each browser token binds the requesting origin. Use the descriptor returned by the session endpoint. It supplies configuration, locale and tile URLs, credentials, expiry and dataset identity. Tokens last at most 15 minutes and refresh without changing the project ID. The public project ID is safe in browser code; a private server API key is not. Origin allowlists control browser sessions but do not make a public identifier a secret.

You may specify `routingUrl` from the account's `routingUrls` descriptor to select a dataset. Do not invent paths or use a signing secret. Dataset identity must change when tile contents change; token refresh must not replace the active dataset. Shut down and initialize a new client when switching datasets.

## Maps and offline behavior

Routing graph tiles and visual map tiles are separate. Render the map with MapLibre. Preserve OpenStreetMap attribution and the Protomaps/MapLibre/Valhalla notices. Valhalla shapes use **polyline6**, not Google's polyline5.

`routewasm/basemap` supports small PMTiles archives and explicit archive caching. Never download the whole planet archive: it is about 138 GB. Use ranges for hosted planet maps. A complete offline application needs its app shell, worker/WASM, configuration, locale, visual tiles, styles, fonts and sprites as well as its routing graph. Browser storage can be evicted; request durable storage with user understanding and provide removal controls.

The router defaults to a 400 km geographic-span guard. This is not a byte or memory cap. Dense regions and complex actions can still require large downloads. Surface progress and errors; do not promise every cold route is instant. Hosted server fallback is planned, not an existing SDK feature.

## Production assets

Ship `valhalla_wasm.js` and `valhalla_wasm.wasm` together under their original filenames. Copy the installed package's `dist/wasm/` into your static output and pass `wasmJsUrl` if needed. A Vite development build succeeding does not prove the production worker or WASM URL works. Test the built output and an actual route.

Use the README for the full SDK API, the performance guide for repeatable measurements, and the examples guide to choose a workload. Do not submit generated credentials, customer route coordinates or account exports to an agent prompt.

## Repeated actions and SDK version

Since 0.3.2 the engine resets per-request state between calls, including matrices and isochrones. Use one client for repeated calls and verify multiple requests, not only a single successful result.

For matrix demos, measure computation separately from first-use initialization and tile downloads. Matrix elements equal sources multiplied by targets. Rerouting in retained coverage adds no hosted route-request charge, but new coverage can trigger downloads.

## Plans and accounts

Account styling and the MapLibre quickstart follow the public demo. Use /app for project identity, origin restrictions and delayed delivered-byte totals, and /app/plans for billing details, checkout, invoices and cancellation. Switching plans must preserve project ID, dataset URLs and local cache identity. Paid cancellation stops renewals; retained data still works. Expired paid access falls back to the Developer allowance.
