> ## Documentation Index
> Fetch the complete documentation index at: https://hyperframes-codex-docs-human-first-refactor.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# @hyperframes/player

> Embeddable web component for playing HyperFrames compositions in any web page.

The player package provides a `<hyperframes-player>` custom element that embeds a
HyperFrames composition in plain HTML or a framework application.

```bash theme={null}
npm install @hyperframes/player
```

Use Player when an application needs to play and seek an HTML composition. Use
[Studio](/packages/studio) to edit it or the [CLI](/packages/cli) and
[Producer](/packages/producer) to render a video file.

## Embed a composition

### Via CDN

```html theme={null}
<script type="module" src="https://cdn.jsdelivr.net/npm/@hyperframes/player"></script>

<hyperframes-player
  src="./my-composition/index.html"
  controls
  style="width: 100%; max-width: 800px; aspect-ratio: 16/9"
></hyperframes-player>
```

With a package manager:

```js theme={null}
import "@hyperframes/player";
```

```html theme={null}
<hyperframes-player src="/compositions/intro.html" controls></hyperframes-player>
```

Set `autoplay muted` only when playback should start without a user gesture.

## Attributes

| Attribute       | Type    | Default | Description                                               |
| --------------- | ------- | ------- | --------------------------------------------------------- |
| `src`           | string  | —       | URL or relative path to composition HTML                  |
| `srcdoc`        | string  | —       | Composition HTML already available as a string            |
| `width`         | number  | 1920    | Native composition width used for aspect ratio            |
| `height`        | number  | 1080    | Native composition height used for aspect ratio           |
| `controls`      | boolean | false   | Show playback, scrub, speed, time, and volume controls    |
| `autoplay`      | boolean | false   | Start when the composition is ready                       |
| `loop`          | boolean | false   | Restart at the end                                        |
| `muted`         | boolean | false   | Mute audio                                                |
| `volume`        | number  | 1       | Playback volume from 0 to 1                               |
| `poster`        | string  | —       | Image URL to show before first play                       |
| `playback-rate` | number  | 1       | Playback speed multiplier                                 |
| `audio-src`     | string  | —       | Optional primary audio URL to preload in the parent frame |
| `audio-locked`  | boolean | false   | Force muted playback and hide volume controls             |

Player also accepts `shader-capture-scale` and `shader-loading` for previewing
projects that use shader transitions. These are preview controls, not composition
authoring attributes.

## JavaScript API

The main API follows familiar media-player behavior:

```js theme={null}
const player = document.querySelector("hyperframes-player");

player.play();
player.pause();
player.seek(2.5);

player.currentTime = 5;
player.playbackRate = 1.5;
player.muted = true;

console.log(player.duration, player.paused, player.ready);
```

## Events

```js theme={null}
const player = document.querySelector("hyperframes-player");

player.addEventListener("ready", (event) => {
  console.log("Duration:", event.detail.duration);
});

player.addEventListener("timeupdate", (event) => {
  console.log("Time:", event.detail.currentTime);
});
```

| Event        | Detail            | Description                                                  |
| ------------ | ----------------- | ------------------------------------------------------------ |
| `ready`      | `{ duration }`    | Composition loaded and timeline discovered                   |
| `timeupdate` | `{ currentTime }` | Playback position changed, approximately 10 times per second |
| `play`       | —                 | Playback started                                             |
| `pause`      | —                 | Playback paused                                              |
| `ended`      | —                 | Playback reached end                                         |
| `error`      | `{ message }`     | Load or runtime error                                        |

## Advanced: iframe access

The composition runs inside a sandboxed `<iframe>` in the player's Shadow DOM. For most use cases you don't need direct access — the JavaScript API and events above are sufficient. But if you're building an editor, recorder, or custom timeline on top of the player, you'll need to inspect the composition's DOM or read its `__player` / `__timelines` runtime objects. The `iframeElement` getter exposes the inner iframe for these consumers:

```js theme={null}
const player = document.querySelector("hyperframes-player");
const iframe = player.iframeElement;

// Reach into the composition's DOM
iframe.contentDocument.querySelectorAll("[data-composition-id]");

// Read the runtime (GSAP timelines, element registry, etc.)
iframe.contentWindow.__timelines;
```

This is the canonical way to bridge the player into editor tools like [`@hyperframes/studio`](/packages/studio). The studio exports a `resolveIframe` helper that handles both direct iframe refs and web-component refs:

```ts theme={null}
import { resolveIframe, useTimelinePlayer } from "@hyperframes/studio";

const { iframeRef } = useTimelinePlayer();
const player = document.createElement("hyperframes-player");
player.setAttribute("src", src);
container.appendChild(player);

// Forward the inner iframe so useTimelinePlayer can drive play/pause/seek.
iframeRef.current = resolveIframe(player);
```

### React: declarative ref pattern

If you prefer JSX over imperative element creation, attach a ref to the web component and resolve the iframe inside an effect:

```tsx theme={null}
import "@hyperframes/player";
import type { HyperframesPlayer } from "@hyperframes/player";
import { resolveIframe, useTimelinePlayer } from "@hyperframes/studio";
import { useEffect, useRef } from "react";

function StudioPreview({ src }: { src: string }) {
  const { iframeRef, onIframeLoad } = useTimelinePlayer();
  const playerRef = useRef<HyperframesPlayer>(null);

  useEffect(() => {
    iframeRef.current = resolveIframe(playerRef.current);
  });

  return (
    <hyperframes-player
      ref={playerRef}
      src={src}
      onLoad={onIframeLoad}
    />
  );
}
```

<Warning>
  A hook that expects an iframe cannot use the `<hyperframes-player>` host
  directly. Pass `iframeElement`, or use `resolveIframe`.
</Warning>

## How it works

The composition runs in a sandboxed iframe inside the player's Shadow DOM. This
isolates its styles, scales it to the player container, and lets the player
communicate with the HyperFrames runtime through `postMessage`.
