> ## Documentation Index
> Fetch the complete documentation index at: https://docs.trycarhub.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Player SDK

> Embed and control an interactive 360° vehicle player in your application.

The CarHub Player SDK renders an interactive 360° vehicle player directly in your page. It accepts the same report data as the legacy Carviz player, but gives you typed events, imperative controls, custom theming and framework bindings.

It does not use an iframe.

## Install

Install the framework-independent package:

```bash theme={null}
pnpm add @carhub/player
```

The SDK supports modern evergreen browsers. It uses Shadow DOM, `ResizeObserver`, pointer events and `AbortController`.

## Create a player

Give the player a container. It fills the container width and derives its height from the displayed media.

```html theme={null}
<div id="player" style="max-width: 960px"></div>
```

```ts theme={null}
import { CarhubPlayer } from "@carhub/player";

const player = new CarhubPlayer("#player", {
  token: "5da89076-bdbc-47ba-9f61-a55878c06786",
  apiKey: "pk_live_…",
  tabview: "360",
});

player.on("hotspotclick", ({ damage }) => {
  console.log(damage.location, damage.type);
});
```

<Warning>
  `apiKey` is visible to everyone who loads your page. Use only a publishable, read-only key scoped to report reads. Never place a secret API key in browser code.
</Warning>

## Load report data

Choose one data-loading route. `report` takes precedence over `token`, and `fetchReport` takes precedence over the built-in request.

| Option               | Use when                                                                      |
| -------------------- | ----------------------------------------------------------------------------- |
| `report`             | You already have the report JSON. No request or browser credential is needed. |
| `fetchReport`        | Your backend loads the report. This keeps credentials server-side.            |
| `token` and `apiKey` | You use a publishable, read-only report key in the browser.                   |

```ts theme={null}
// Use a report you already fetched.
new CarhubPlayer("#player", { report });

// Load through your own backend.
new CarhubPlayer("#player", {
  token,
  fetchReport: (reportToken, signal) =>
    fetch(`/api/reports/${reportToken}`, { signal }).then((response) => response.json()),
});
```

You can also supply `jwt` for reports that require a JWT. The built-in loader sends it as `X-JWT` and retries anonymously if the request returns `401`.

## Configure the player

Pass configuration when you create the player. These defaults match the Player 360 embed contract.

| Option           | Type                | Default      | Effect                                                      |
| ---------------- | ------------------- | ------------ | ----------------------------------------------------------- |
| `upscaled`       | `boolean`           | `true`       | Prefer enhanced images when available.                      |
| `upscaleToggle`  | `boolean`           | `true`       | Show the enhanced/original image toggle.                    |
| `photomode`      | `boolean`           | `false`      | Hide the 360° spin and show photos only.                    |
| `hotspot`        | `boolean`           | `true`       | Show damage markers on images and 360° frames.              |
| `sharable`       | `boolean`           | `true`       | Show the share button.                                      |
| `zoomable`       | `boolean`           | `true`       | Enable wheel and pinch zoom in the 360° view.               |
| `fullscreenable` | `boolean`           | `true`       | Enable the fullscreen image viewer.                         |
| `interior`       | `boolean`           | `false`      | Include the interior photo tab.                             |
| `damages`        | `boolean`           | `true`       | Include the damage gallery and damage detail cards.         |
| `carousel`       | `boolean`           | `true`       | Show the thumbnail carousel.                                |
| `selector`       | `boolean`           | `true`       | Show the view selector.                                     |
| `tabview`        | `TabId`             | `"exterior"` | Select the initial view.                                    |
| `excludeTab`     | `TabId[] \| string` | `[]`         | Hide specified views. A comma-separated string is accepted. |
| `autoplay360`    | `boolean`           | `true`       | Start the 360° spin automatically.                          |
| `color`          | `string`            | `null`       | Accent colour. Accepts `#RGB`, `#RRGGBB` or bare hex.       |

The available `TabId` values are `360`, `exterior`, `interior`, `interior360`, `others` and `damages`.

```ts theme={null}
new CarhubPlayer("#player", {
  report,
  interior: true,
  excludeTab: ["others"],
  color: "16A34A",
});
```

### Use iframe-compatible query parameters

Query parameters are off by default. Enable them when you want the SDK to follow the iframe embed contract. Query values then take precedence over supplied options, which take precedence over defaults.

```ts theme={null}
new CarhubPlayer("#player", {
  report,
  readQueryParams: true,
});
```

For example, `?tabview=360&hotspot=false&excludeTab=interior,damages` overrides the corresponding options.

## Control the player

The instance exposes methods for navigation and playback.

```ts theme={null}
player.setView("damages");
player.showSlide(2);
player.setFrame(18);
player.play();
player.pause();
player.setUpscaled(true);
player.openFullscreen();
player.closeFullscreen();
player.share();
player.setReport(nextReport);
player.destroy();
```

`showSlide()` uses one global slide list, ordered exterior, interior, interior 360°, others and damages. The player automatically switches to the matching view.

Read the active state with `player.currentView`, `player.tabs`, `player.frame` and `player.currentReport`.

## Subscribe to events

Attach listeners immediately after creating the player. Initial events are emitted in a microtask, so the listener receives `ready`.

```ts theme={null}
player.on("ready", ({ report, tabs }) => {
  console.log("Loaded", report, tabs);
});

player.on("viewchange", ({ view, previous }) => {
  console.log(`${previous} → ${view}`);
});

player.on("error", ({ error, phase }) => {
  console.error(phase, error);
});
```

| Event              | Payload                                         |
| ------------------ | ----------------------------------------------- |
| `ready`            | `report`, `tabs`                                |
| `viewchange`       | `view`, `previous`                              |
| `framechange`      | `frame`, `total`                                |
| `playstatechange`  | `playing`                                       |
| `hotspotclick`     | `hotspot`, `damage`                             |
| `slidechange`      | `index`, `slide`                                |
| `fullscreenchange` | `open`, `index`                                 |
| `upscalechange`    | `upscaled`                                      |
| `share`            | `url`, `preventDefault()`                       |
| `error`            | `error`, `phase` (`fetch`, `render` or `asset`) |
| `destroy`          | No payload                                      |

`on`, `off` and `once` are chainable. `framechange` can fire about 30 times a second during autoplay, so sample it before rendering UI from it.

To replace sharing entirely, prevent the default behaviour:

```ts theme={null}
player.on("share", ({ url, preventDefault }) => {
  preventDefault();
  openYourShareSheet(url);
});
```

## Use a framework binding

The core player has bindings for React, Vue and Angular. They all expose the same options and events as `@carhub/player`.

```bash theme={null}
pnpm add @carhub/player-react
# or: @carhub/player-vue, @carhub/player-angular
```

```tsx theme={null}
import { CarhubPlayerView } from "@carhub/player-react";

<CarhubPlayerView
  token="5da89076-bdbc-47ba-9f61-a55878c06786"
  apiKey="pk_live_…"
  tabview="360"
  interior
  onHotspotclick={({ damage }) => console.log(damage.location)}
/>;
```

You can also use the custom element without a framework:

```html theme={null}
<script type="module" src="https://unpkg.com/@carhub/player/dist/element.js"></script>

<carhub-player
  token="5da89076-bdbc-47ba-9f61-a55878c06786"
  api-key="pk_live_…"
  tabview="360"
  autoplay360
></carhub-player>
```

Its attributes mirror player options in kebab case. A bare boolean attribute is `true`; set `attribute="false"` to disable it.

## Theme and translate labels

Set CSS custom properties on the host element. They cross the Shadow DOM boundary.

```css theme={null}
#player {
  --chp-accent: #16a34a;
  --chp-radius: 4px;
  --chp-surface: #f8fafc;
}
```

Set `data-theme="light"` or `data-theme="dark"` on the host element to fix the theme. Without it, the player follows the user’s colour-scheme preference.

The player humanises damage and vehicle-part keys by default. Install the optional i18n export for the complete five-language dictionary:

```ts theme={null}
import { CarhubPlayer, toMessages } from "@carhub/player";
import { vehicleParts, damageTypes } from "@carhub/player/i18n";

new CarhubPlayer("#player", {
  report,
  locale: "fr",
  messages: toMessages(vehicleParts, damageTypes),
});
```

## Add interior panoramas

RICOH THETA equirectangular images use Photo Sphere Viewer. Install its optional peer dependencies to render them as interactive panoramas:

```bash theme={null}
pnpm add @photo-sphere-viewer/core @photo-sphere-viewer/gyroscope-plugin
```

Without these packages, a panorama falls back to a flat image.

## Migrate from the iframe

Replace the iframe with a container and SDK instance. Configuration names and defaults are compatible. Enable `readQueryParams` if you want existing URL query parameters to continue controlling the player.

```html theme={null}
<!-- Before -->
<iframe src="https://app.carviz.com/view/{token}/360"></iframe>

<!-- After -->
<div id="player"></div>
```

```ts theme={null}
new CarhubPlayer("#player", {
  token,
  apiKey: "pk_live_…",
  readQueryParams: true,
});
```
