# @hyperframes/player
Embeddable web component for playing HyperFrames compositions. Zero dependencies, works with any framework.
## Install
```bash
npm install @hyperframes/player
```
Or load directly via CDN:
```html
```
## Usage
```html
```
The player loads the composition in a sandboxed iframe, auto-detects its dimensions and duration, and scales it responsively to fit the container.
### With a framework
```typescript
import "@hyperframes/player";
// The custom element is now registered — use it in your markup
// React:
// Vue:
```
### Poster image
Show a static image before playback starts:
```html
```
## Attributes
| Attribute | Type | Default | Description |
| --------------- | ------- | ------- | -------------------------------------------- |
| `src` | string | — | URL to the composition HTML file |
| `audio-src` | string | — | Audio URL for parent-frame playback (mobile) |
| `width` | number | 1920 | Composition width in pixels (aspect ratio) |
| `height` | number | 1080 | Composition height in pixels (aspect ratio) |
| `controls` | boolean | false | Show play/pause, scrubber, and time display |
| `muted` | boolean | false | Mute audio playback |
| `poster` | string | — | Image URL shown before playback starts |
| `playback-rate` | number | 1 | Speed multiplier (0.5 = half, 2 = double) |
| `autoplay` | boolean | false | Start playing when ready |
| `loop` | boolean | false | Restart when the composition ends |
### Mobile audio
Mobile browsers block `audio.play()` inside iframes when the user gesture happened in the parent frame (the [User Activation spec](https://html.spec.whatwg.org/multipage/interaction.html#tracking-user-activation) does not propagate activation across frame boundaries via `postMessage`).
The player handles this automatically for same-origin iframes (the default — `sandbox` includes `allow-same-origin`):
1. When the composition is ready, the player extracts all timed media (`audio[data-start]`, `video[data-start]`) from the iframe DOM and creates parent-frame copies.
2. The iframe originals are disabled (`src` and `data-start` removed) so the runtime doesn't try to play them.
3. When `play()` is called (from a user gesture), parent media `.play()` runs synchronously in the gesture call stack, satisfying mobile autoplay policy.
4. Both parent media and the GSAP timeline start simultaneously and free-run — no active sync needed since both are real-time systems.
No changes are required by consumers — this works out of the box.
The optional `audio-src` attribute can be used to start preloading a primary audio track before the iframe loads (useful on slow connections), but is not required for mobile playback.
## JavaScript API
```js
const player = document.querySelector("hyperframes-player");
// Playback
player.play();
player.pause();
player.seek(2.5); // jump to 2.5 seconds
// Properties
player.currentTime; // number (read/write)
player.duration; // number (read-only)
player.paused; // boolean (read-only)
player.ready; // boolean (read-only)
player.playbackRate; // number (read/write)
player.muted; // boolean (read/write)
player.loop; // boolean (read/write)
// Inner iframe access (for advanced consumers — see "Advanced: iframe access" below)
player.iframeElement; // HTMLIFrameElement (read-only)
```
## Advanced: iframe access
The composition runs inside a sandboxed `