Live preview · the widget at the bottom-right is the actual product
Live co-browsing — instead of click-here tours

Stop telling them where to click.
Be on the page with them.

Tooltip tours and modal "Step 3 of 7" walk-throughs can't react to what the user actually sees. Guidewell drops one script tag and lets your team join any visitor's session — shared cursors, click ripples, scroll & navigation sync. The conversation happens on the page, not in a popup.

See integration
Bottom-right ? tell the room who you are (name + role), copy the invite link, share it with one or more teammates — done.
The thesis

From canned tooltip tours to open, live guidance

Static product tours are a guess about what each user might need. They break the moment your UI changes and can't adapt to what the visitor is actually looking at. Guidewell flips it: a real teammate joins the session, sees the same screen, and points with the cursor in real time.

The old way

Pre-recorded tooltip tours

"Click Save → then Settings → then this icon" — one fixed path, no eye-contact, no follow-up question.

Step 3 of 7
Click the Save button at the top-right to continue. Then we'll show you Settings.
  • Static, pre-recorded path
  • Breaks when the UI changes
  • One-way — you can't see them
  • Same script for everyone
With Guidewell

A real teammate (or three) — live on the same page

Shared cursors, click ripples, scroll & navigation in sync. Free-form, multi-peer, contextual — like sitting next to them.

Anna and 2 others are here
Anna · guide
visitor
QA · guide
  • Live, free-form pointing
  • Adapts to whatever they see
  • Two-way — you see their cursor too
  • Contextual to this exact user
Try it on this page

Start a co-browse session in 4 steps

The widget at the bottom-right of this very page is a real install. Follow the steps below to launch a session and bring in one or more peers — another tab, an incognito window, or a few teammates at once.

1

Open the widget

Look at the bottom-right corner of this page — there's a round floating button. Click it to open the panel.

2

Tell the room who you are

A tiny form asks for your name and whether you're getting help or guiding someone. It's remembered locally — next visit there's no form at all.

3

Copy invite link

Press "Copy invite link" — a URL with ?session_id=… is now in your clipboard.

4

Bring in others

Paste the link into another browser, an incognito window, or send it to multiple teammates at once — every tab that opens it auto-joins. You'll see a separate cursor per peer.

Hint: for a quick solo test, paste the invite link into an incognito / private window — you'll see your own cursor mirrored in real time.
Capabilities

Everything you need for live co-browsing

Built for support, sales demos and onboarding flows. Real-time, low-latency, multi-page, and invisible to your end users until they need help.

Shared cursors

See exactly where each peer is pointing — every participant gets a unique color (deterministic from their pid). Throttled to 60 fps, normalized to viewport coordinates, gracefully degrading on touch.

Click ripples

Click anywhere outside the widget and a wave fires for every other peer in the room — perfect for "tap this button" instructions without a single voice call.

Scroll sync

Pages stay in lock-step even when each user has a different viewport size. Position is sent as a normalized ratio, applied without echoing back.

Navigation sync

Works with classic page loads and SPA routers. We patch history.pushState/replaceState so visitor navigation mirrors instantly.

Frictionless invite

No email, no password, no signup. One tiny form (name + role) on the first visit, then a session ID and a copy-able invite link. Every tab that opens the link joins the same room — bring in as many peers as you need.

Multi-peer rooms

N participants per session, no hard cap. Pure broadcast model — every event reaches every other peer. peers_snapshot on join, peer_join/peer_leave updates keep every UI honest about who's there.

Selective event log

Cursors and scroll are realtime-only — they'd flood any DB. We persist only the meaningful stuff: click with coords, navigate with the resolved URL, and any action you fire from your code — straight into gw_analytics_events, scoped to the project & owner.

Lightweight bundle

~7 KB gzipped, vanilla JS, no React/Vue runtime, no external CDN requests. Loads async, won't block your TTI, isolates styles in a scoped host.

WebSocket signaling

One persistent connection per peer, JWT-scoped per session. Server broadcasts a peer snapshot on join so the UI never lies about who's there.

Origin-aware

Sessions remember the page they started on. Cross-origin events are filtered server-side; loopback hosts get a developer-friendly bypass.

Themeable

Brand it in seconds: primaryColor, theme: dark, custom fabIcon (any inline SVG — Lucide, Heroicons, your own).

Under the hood

Anatomy of a session

REST to bootstrap, WebSocket to stream, Postgres to remember. A pure broadcast room with N participants, no hard cap and no master/slave roles — every event from one peer fans out to all the others, and only the meaningful ones land in the database.

REST: bootstrap & link

POST /v1/sessions with X-Guidewell-Key returns a fresh session_id, an identity_jwt and a signal_token. Server resolves the key into a project_id and owner_user_id and stores them on the row — that's how the admin shows you only your sessions.

WebSocket: open the signal channel

Each peer connects to /v1/sessions/{id}/signal?token=…. The server adds them to an in-memory SignalRoom and emits a peers_snapshot with everyone already inside, then a peer_join to all the existing members.

Broadcast: N peers, one room

Cursor / scroll / click / nav events from any peer are JSON-broadcast to all the others (sender excluded). peer_leave fires on disconnect. The room is just a list — no participant cap, no role hierarchy, no per-pair signaling.

Persist: only what matters

Cursors and scroll stay in-memory — they'd drown any database at 60 fps. click_wave, nav_change and explicit action events are written to gw_analytics_events with their full payload (coords, target href, custom props). The admin replay is built straight off that table.

Integration

Two lines of HTML. That's the whole install.

One script tag. Auto-mount.

Drop the snippet anywhere on your page. The bundle injects its own host element, scopes its styles, creates sessions on demand and tears itself down on unmount — no glue code, no virtual DOM runtime.

  • Two attributes do the work · data-api points at your backend, data-key binds the session to a project. Everything else has sensible defaults.
  • Brand it in HTML · add data-fab-icon (any inline SVG — paste from lucide.dev), data-fab-text, data-primary-color, data-theme="dark".
  • Or configure via JS · set window.GuidewellConfig = {…} before the script tag. Same options, useful when you build the config dynamically.
  • Open it from anywhere · put data-guidewell-toggle on any button on your page, or call Guidewell.open() / .close() / .toggle() from your own JS. The two CTAs on this page use exactly that.
index.html
<!-- That's the entire install. Same-origin: no data-api needed. -->
<script src="/widget.js"></script>

<!-- Cross-origin? Point at your API host. The data-key links every session
     this widget creates to your project + owner row, so the admin console
     shows you only the sessions you actually own. -->
<script src="https://api.your-domain.com/widget.js"
        data-api="https://api.your-domain.com"
        data-key="prj_pk_…"></script>
Admin console

A real control plane behind the widget

Guidewell ships with a Nuxt-based admin console for project owners and operators — manage projects, watch live sessions, run the API, handle billing & access.

Projects

Spin up a new project, get an API key, paste the embed snippet, lock to site_origin, tweak per-project widget defaults — all from the dashboard.

Sessions monitor

Every session you launched — scoped to your account via owner_user_id, filterable by project_id. Status, origin, mode, started/ended timestamps. Drill in to replay the persisted click / nav / action stream.

Billing

Per-project payments ledger with manual or provider-backed entries (Stripe/etc.), currency-aware, export-ready. Wire your own pricing on top.

Users & roles

Invite teammates, assign roles, review who has access to which projects. Admin-only view, JWT-backed, role-aware navigation in the sidebar.

Policies

RBAC primitives baked in: control which roles can manage projects, run sessions, see analytics or charge customers — Postgres rows, not config files.

API console & docs

Built-in OpenAPI playground — pick any endpoint, set path params and JSON body, fire it from the admin without touching curl. Live docs included.

Stack

Boring, batteries-included tech

No proprietary runtime, no vendor lock-in. Self-host on Fly.io, Render, your own Kubernetes — same Postgres your team already operates.

FastAPI

Python 3.11+, async REST & WebSocket. OpenAPI auto-generated.

Supabase & Postgres

Transactional persistence, RLS-ready schema, SQL migrations.

WebSockets

JWT-scoped per-session signaling rooms, peer snapshots on join.

Vanilla JS widget

~7 KB gzipped IIFE bundle. Vite build. No frontend runtime.

Docker

Single Dockerfile + compose for local dev, prod-ready images.

Nuxt 3 admin

Vue 3, Tailwind, lucide-vue-next. Built-in i18n & theme switcher.

Run it on your stack today

Spin up the backend, mount the widget, open the admin console. Everything you saw on this page is shipped in this repo — no SaaS sign-up required.

Open API docs