diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index c84da7f9..cf0aaa84 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -84,23 +84,23 @@ if you don't.
### Web
+Perform all commands unless otherwise noted from the `web` directory.
+
1. Install `wasm-pack`:
```shell
cargo install wasm-pack
```
-2. Build the NPM package of the core:
+2. Build the WASM package for the core:
```shell
-cd common
-wasm-pack build --target web ferrostar --no-default-features --features wasm_js
+npm run prepare:core
```
3. Install dependencies:
```shell
-cd ../web
npm install
```
diff --git a/guide/src/SUMMARY.md b/guide/src/SUMMARY.md
index b1ed973e..c574b133 100644
--- a/guide/src/SUMMARY.md
+++ b/guide/src/SUMMARY.md
@@ -7,6 +7,7 @@
- [iOS](./ios-getting-started.md)
- [Android](./android-getting-started.md)
+- [Web](./web-getting-started.md)
- [Rust](./rust-getting-started.md)
# Customization
@@ -15,6 +16,7 @@
- [Navigation Behavior](./configuring-the-navigation-controller.md)
- [SwiftUI](./swiftui-customization.md)
- [Jetpack Compose](./jetpack-compose-customization.md)
+- [Web](./web-customization.md)
# Architecture
diff --git a/guide/src/web-customization.md b/guide/src/web-customization.md
new file mode 100644
index 00000000..245bcae3
--- /dev/null
+++ b/guide/src/web-customization.md
@@ -0,0 +1,91 @@
+# Web
+
+The [web tutorial](./web-getting-started.md) gets you set up with a “batteries included” UI
+and sane defaults (if a bit customized for Stadia Maps at the moment).
+This document covers ways you can customize it to your needs.
+
+## Removing or replacing the integrated search box
+
+If you want to use your own code to handle navigation instead of the integrated search box, you can do so by the following steps:
+
+### Disable the integrated search box
+
+You can disable the integrated search box with the `useIntegratedSearchBox` attribute.
+
+```html
+
+```
+
+### Use your own search box/geocoding API
+
+The HTML, JS, and CSS for this is out of scope of this guide,
+but here’s an example (without UI)
+showing how to retrieve the latitude and longitude of a destination
+using the Nominatim API ([note their usage policy](https://operations.osmfoundation.org/policies/nominatim/) before deploying):
+
+```javascript
+const destination = "One Apple Park Way";
+
+const { lat, lon } = await fetch("https://nominatim.openstreetmap.org/search?q=" + destination + "&format=json")
+ .then((response) => response.json())
+ .then((data) => data[0]);
+```
+
+### Get routes manually
+
+Once you have your waypoint(s) geocoded,
+create a list of them like this:
+
+```javascript
+const waypoints = [{ coordinate: { lat: parseFloat(lat), lng: parseFloat(lon) }, kind: "Break" }];
+```
+
+The asynchronous `getRoutes` method on `FerrostarCore`
+will fetch routes from your route provider (ex: a Valhalla server).
+Here’s an example:
+
+```javascript
+const core = document.getElementById("core");
+const routes = await core.getRoutes(locationProvider.lastLocation, waypoints);
+const route = routes[0];
+```
+
+### Starting navigation manually
+
+Once you have a route,
+it’s time to start navigating!
+
+```javascript
+core.startNavigation(route, config);
+```
+
+## Location providers
+
+The “batteries include” defaults will use the web Geolocation API automatically.
+However, you can override this for simulation purposes.
+
+### `BrowserLocationProvider`
+
+`BrowserLocationProvider` is a location provider that uses the browser's geolocation API.
+
+```javascript
+// Request location permission and start location updates
+const locationProvider = new BrowserLocationProvider();
+locationProvider.requestPermission();
+locationProvider.start();
+
+// TODO: This approach is not ideal, any better way to wait for the locationProvider to acquire the first location?
+while (!locationProvider.lastLocation) {
+ await new Promise((resolve) => setTimeout(resolve, 100));
+}
+```
+
+### `SimulatedLocationProvider`
+
+TODO: Documentation
\ No newline at end of file
diff --git a/guide/src/web-getting-started.md b/guide/src/web-getting-started.md
new file mode 100644
index 00000000..6cfc9123
--- /dev/null
+++ b/guide/src/web-getting-started.md
@@ -0,0 +1,112 @@
+# Getting Started on the Web
+
+This section of the guide covers how to integrate Ferrostar into a web app.
+While there are limitations to the web [Geolocation API](https://developer.mozilla.org/en-US/docs/Web/API/Geolocation_API)
+(notably no background updates),
+PWAs and other mobile-optimized sites
+can be a great solution when a native iOS/Android app is impractical or prohibitively expensive.
+
+We'll cover the "batteries included" approach, but flag areas for customization and overrides along the way.
+
+## Add the package dependency
+
+### Installing with `npm`
+
+NOTE: Currently you need to build the package locally.
+We intend to publish to npmjs.com very soon.
+
+In your web app, you can add the Ferrostar NPM package as a dependency.
+You will need to install [Rust](https://www.rust-lang.org/) and `wasm-pack` to build the NPM package.
+
+```shell
+cargo install wasm-pack
+```
+
+Head to the local path where you have checked out Ferrostar,
+go to the `web` directory, and build the module:
+
+```shell
+npm install && npm run build
+```
+
+Then, in your project, install the Ferrostar package using the local path:
+
+```shell
+npm install /path/to/ferrostar/web
+```
+
+### Using unpkg
+
+TODO after publishing to npm.
+
+## Add Ferrostar web components to your web app
+
+The Ferrostar web SDK uses the [Web Components](https://developer.mozilla.org/en-US/docs/Web/API/Web_components)
+to ensure maximum compatibility across frontend frameworks.
+You can import the components just like other things you’re used to in JavaScript.
+
+```javascript
+import { FerrostarCore, BrowserLocationProvider } from "ferrostar-components";
+```
+
+## Configure the `` component
+
+Now you can use Ferrostar in your HTML like this:
+
+```html
+
+```
+
+Here we have used Stadia Maps URLs, which should work without authentication for local development.
+(Refer to the [authentication docs](https://docs.stadiamaps.com/authentication/)
+for network deployment details; you can start with a free account.)
+
+See the [vendors appendix](./vendors.md) for a list of other compatible vendors.
+
+`` additionally requires setting some CSS manually, or it will be invisible!
+
+```css
+ferrostar-core {
+ display: block;
+ width: 100%;
+ height: 100%;
+}
+```
+
+That’s all you need to get started!
+
+### Configuration explained
+
+`` provides a few properties to configure.
+Here are the most important ones:
+
+- `valhallaEndpointUrl`: The Valhalla routing endpoint to use. You can use any reasonably up-to-date Valhalla server, including your own. See [vendors](./vendor.md#routing) for a list of known compatible vendors.
+- `httpClient`: You can set your own fetch-compatible HTTP client to make requests to the routing API (ex: Valhalla).
+- `costingOptions`: You can set the costing options for the route provider (ex: Valhalla JSON options).
+- `useIntegratedSearchBox`: Ferrostar web includes a search box powered by Stadia Maps, but you can disable this and replace with your own.
+- `useVoiceGuidance`: Enable or disable voice guidance.
+
+NOTE: `useIntegratedSearchBox` and `useVoiceGuidance` are disabled by default. Set them to any value to enable them.
+
+NOTE: The JavaScript API is currently limited to Valhalla,
+but support for arbitrary providers (like we already have on iOS and Android)
+is [tracked in this issue](https://github.com/stadiamaps/ferrostar/issues/191).
+
+## Demo app
+
+We've put together a minimal demo app with an example integration.
+Check out the [source code](https://github.com/stadiamaps/ferrostar/tree/main/web/index.html)
+or try the [hosted demo](https://stadiamaps.github.io/ferrostar/web-demo)
+(works best from a phone if you want to use real geolocation).
+
+## Going deeper
+
+This covers the basic “batteries included” configuration and pre-built UI.
+But there’s a lot of room for customization!
+Skip on over to the customization chapters that interest you.
\ No newline at end of file
diff --git a/web/index.html b/web/index.html
index 81a261d4..c59bb7dc 100644
--- a/web/index.html
+++ b/web/index.html
@@ -5,8 +5,6 @@
Ferrostar Web Demo
-
-