---
title: "3D buyer view"
description: "Offer an interactive venue view, cinematic seat navigation, and 360° sightlines with a reliable 2D fallback."
---

The SeatPicker can move between a fast 2D map and an interactive 3D venue. Buyers can orbit the venue, inspect live seat state, fly to a seat, and open its 360° view. For a walkthrough of what buyers see, see [the 3D seat map experience](https://seatlayer.io/3d-seat-map/).

The feature is progressive enhancement:

- enabled by default;
- shown only when the browser supports WebGL2;
- lazy-loaded only when the buyer enters 3D; and
- always paired with the standard 2D map.

## Basic setup

New to this feature? The step-by-step
[3D seat view tutorial](/buyer-sdk/add-3d-seat-view) walks from install through
fallback behaviour; this page is the reference for everything it touches.

```js title="buyer-picker.js" {5,6}
const picker = new SeatPicker({
  container: "#picker",
  event: "ev_9f3a",
  publicKey: "pk_test_…",
  enable3D: true,
  seatView: true,
});

await picker.render();
```

For a public Platform event, use a publishable key that matches the event mode
(`pk_test_…` for test or `pk_live_…` for live) and register the page's exact
Embed origin. The SDK obtains Public-only access directly and keeps the grant in
memory, so your server does not mint a buyer token when the chart loads. For a
login, presale, partner, or channel audience, replace `publicKey` with an async
`buyerAccessTokenProvider` backed by your authenticated server; an explicit
provider or token takes precedence.

Set `enable3D: false` when an embed must remain strictly 2D. Set `seatView: false` to hide 360° view-from-seat actions in both 2D and 3D.

## Control the view

The buyer can use the built-in **Map | 3D** switch. Your application can drive the same state:

```js title="venue-tour.js"
picker.setBuyerView("venue3d");
console.log(picker.getBuyerView()); // "venue3d"

// Enter 3D and move directly to a known seat.
picker.setBuyerView("venue3d", {
  flyToSeatId: "A-12",
});

picker.setBuyerView("map");
```

Calling `setBuyerView("venue3d", { flyToSeatId })` while already in 3D moves the existing camera without rebuilding the scene.

<Aside type="note" title="Use a seat id">
  `flyToSeatId` must identify a seat that exists in the loaded chart. Store the seat id from the SDK selection; do not derive it from display text in your UI.
</Aside>

## View-from-seat sources

SeatLayer resolves the closest available source:

1. a seat or row-level authored image;
2. a floor-level fallback;
3. a venue-wide fallback; or
4. a geometry-generated preview.

This lets a venue start with one 360° image and progressively add more precise sightlines later. Buyer UI distinguishes an authored image from a generated preview.

The public template demos may attach AI-generated, venue-representative panoramas so teams can evaluate the complete control flow across theatre, arena, stadium and other venue types. Those assets are labeled as representative demo media. They are not real venue photographs, exact-seat captures, obstruction evidence or certified sightlines.

Organizer-authored media remains authoritative. Use buyer-safe metadata such as coverage, capture date and source label so the viewer can disclose whether a panorama represents an exact seat, row, section or the wider venue. See the [virtual seat-view source model](https://seatlayer.io/view-from-seat/) for examples and limits.

For fast mobile delivery, keep `viewFromSeatUrl` as the sharp equirectangular source and add an optional lightweight source to its metadata:

```json title="progressive-panorama.json"
{
  "viewFromSeatUrl": "https://cdn.example.com/venue/main-8192.webp",
  "viewFromSeatMeta": {
    "projection": "equirectangular",
    "previewUrl": "https://cdn.example.com/venue/main-512.webp",
    "previewWidth": 512,
    "previewHeight": 256,
    "sourceWidth": 8192,
    "sourceHeight": 4096
  }
}
```

The preview paints first. On capable devices the viewer decodes the sharp source after that first frame and replaces the existing texture. Save-Data connections and devices that cannot safely allocate the full texture keep the preview. Charts without preview metadata continue to load `viewFromSeatUrl` directly.

SeatLayer-hosted view media is delivered through the Event, not through a raw
Chart URL. `SeatPicker` handles this automatically: it fetches the image bytes
with the same in-memory buyer-access bearer used for inventory, displays a blob
URL, and revokes that URL when the picker is destroyed. The bearer is never
placed in `src`, CSS, query parameters, browser history, or logs. A custom
`PickerTransport` that serves SeatLayer-authored private media must implement
its optional `asset(eventKey, assetName)` byte loader; without it, the picker
fails closed for those Event-scoped images. External organizer/CDN image URLs
continue to load as authored.

## Finding an exact seat in a large venue

The buyer does not need to hunt for a tiny seat in the full bowl. The built-in 3D locator drills through **Section → Row → Seat**, frames each chosen level, highlights the exact seat and then offers the cinematic seat view. For venues above 6,000 seats, seat-number labels are created only for the focused section instead of allocating thousands of DOM labels for the entire venue. Returning to Overview restores the locator for another search.

## Live state and theming

The 3D scene receives the same availability and selection changes as the 2D picker. Available, held, sold, not-for-sale, accessible, table, booth, section, zone, label, and floor information remains consistent while the buyer changes view.

Chart branding and host theme overrides are applied to the 3D experience as well as the picker chrome.

## Venue architecture

Venue landmarks authored in the Designer are shared with the 3D scene rather than maintained as a separate model. Entrance and exit landmarks become open portal frames; screens become vertical display surfaces; walls, sound booths and service counters use physical extrusion; and the Safety rail, Suite and Obstruction presets add guardrails, open-front hospitality boxes and pillars.

Select one of these landmarks in the Designer to adjust its **3D physical height**. The stored `heightM` value is optional: when absent, the semantic role supplies a safe default, so existing charts remain compatible.

Reusable venues can scope architecture to an event layout. Set `ChartDoc.eventConfigurationId` to the active layout and add `eventConfigurationIds` to shapes that only exist for configurations such as `concert` or `basketball`. Unscoped objects remain visible in every layout; scoped objects remain hidden until a matching configuration is active. A 3D scene input can override the active id for previews without mutating the saved chart.

Use `onBuyerViewChange` to mirror `{ view, seatId }` into a host route. The public demo does this with `?tour=3d&seat=<seat-id>`, so a copied URL opens the same venue and flies to the same seat; browser Back and Forward restore the map, 3D overview, and targeted seat without rebuilding application state.

The buyer picker prepares merged venue geometry and per-seat GPU buffers in a module worker before mounting WebGL. Typed arrays transfer to the main thread without buffer copies. If workers are unavailable or blocked by a host policy, the same pure compiler runs on the main thread as a compatibility fallback.

All architectural pieces are merged into the existing venue solid mesh. Adding them does not create one draw call per wall, portal or screen.

## Analytics

Use `onAnalytics` to forward the 3D journey into your product analytics:

```js title="3d-analytics.js"
const picker = new SeatPicker({
  container: "#picker",
  event: "ev_9f3a",
  publicKey: "pk_test_…",
  onAnalytics: (event, properties) => {
    analytics.capture(event, properties);
  },
});
```

Current events include:

- `3d_opened`;
- `3d_orbit_engaged`;
- `3d_seat_picked`;
- `3d_cinematic_played`, `3d_cinematic_skipped`, `3d_cinematic_cancelled`;
- `3d_panorama_opened`; and
- `3d_panorama_closed`.

The callback receives `{ surface: "buyer" }` with event-specific properties. A failing analytics sink does not break the picker.

## UX guidance

- Keep the built-in toggle discoverable; do not force every buyer into 3D.
- Use a direct flight for a “show my seat” or order-history experience.
- Keep controls outside the picker synchronized with `getBuyerView()`.
- Test low-power devices and browser WebGL restrictions.
- Do not make 3D a requirement for accessibility or checkout.
- Provide meaningful authored 360° images where obstructed views affect purchase decisions.

## Test matrix

- WebGL2 available and unavailable.
- Single and multi-floor venues.
- A seat with an authored image and one using the generated fallback.
- Live availability changing while 3D is open.
- Keyboard exit and return to the 2D selection.
- Mobile portrait and reduced container heights.
- Host theming with sufficient text and control contrast.

For the complete purchase flow, continue to the [checkout example](/examples/complete-checkout).