# Video Playback Feature - E2E Documentation
## Overview
The Tour Builder Platform implements a comprehensive video playback system supporting forward and reverse playback modes. The system handles background videos, video player elements, and transition animations between pages in both Constructor (editing) and Runtime (presentation) modes.
---
## Architecture
```
┌─────────────────────────────────────────────────────────────────┐
│ Video Types │
│ Background Videos │ Video Player Elements │ Transition Videos │
└─────────────────────────────────────────────────────────────────┘
│
┌─────────────────────────────────────────────────────────────────┐
│ Playback Hooks │
│ useTransitionPlayback │ useReversePlayback │ usePageSwitch │
│ useBackgroundVideoPlayback (background video time control) │
└─────────────────────────────────────────────────────────────────┘
│
┌─────────────────────────────────────────────────────────────────┐
│ Preloading Layer │
│ usePreloadOrchestrator │ getReadyBlobUrl (O(1)) │ S3 Presign │
└─────────────────────────────────────────────────────────────────┘
│
┌─────────────────────────────────────────────────────────────────┐
│ Utilities │
│ mediaDuration.ts │ assetUrl.ts │ StorageManager (Cache API) │
└─────────────────────────────────────────────────────────────────┘
│
┌─────────────────────────────────────────────────────────────────┐
│ Rendering │
│ Constructor Page │ RuntimePresentation │ Transition Overlay │
└─────────────────────────────────────────────────────────────────┘
```
---
## Video Types
### 1. Background Videos
Full-screen videos that play behind page content with configurable playback settings.
```tsx
// Using useBackgroundVideoPlayback hook for custom time control
const { videoRef, shouldBlockAutoplay } = useBackgroundVideoPlayback({
videoUrl: page.background_video_url,
autoplay: page.background_video_autoplay ?? true,
loop: page.background_video_loop ?? true,
muted: page.background_video_muted ?? true,
startTime: page.background_video_start_time ?? null,
endTime: page.background_video_end_time ?? null,
});
// Block autoplay if video already played this session (when loop=false)
const effectiveAutoplay = (page.background_video_autoplay ?? true) && !shouldBlockAutoplay;
```
**Configurable Properties (stored in `tour_pages`):**
| Property | Type | Default | Description |
|----------|------|---------|-------------|
| `background_video_autoplay` | boolean | true | Start playback automatically |
| `background_video_loop` | boolean | true | Loop continuously |
| `background_video_muted` | boolean | true | Mute audio (required for autoplay policy) |
| `background_video_start_time` | DECIMAL(10,1) \| null | null | Start playback at this time (seconds, 0.1s precision) |
| `background_video_end_time` | DECIMAL(10,1) \| null | null | Stop/loop at this time (seconds, 0.1s precision) |
**DECIMAL Parsing (Critical):** Sequelize DECIMAL fields return strings from the database (e.g., `"2.5"` not `2.5`). In runtime code, these must be parsed with `parseFloat(String(value))` before passing to the hook.
**Note:** When `background_video_end_time` is set, native HTML5 `loop` attribute is disabled and looping is handled via JavaScript (`timeupdate` event) to properly seek back to `startTime`.
**Session-Scoped Play Once Behavior (when loop=false):**
When loop is disabled, videos only play once per browser session:
1. Video plays normally on first navigation to the page
2. When video ends, the video URL is added to a session-scoped Set (`playedVideos`)
3. On subsequent visits within the same session, autoplay is blocked and video shows last frame
4. Session tracking resets on browser refresh (module-level state, not persisted)
#### Global Sound Control and Autoplay Compatibility
Browsers block unmuted autoplay until the user interacts with the page. iOS WebKit is especially strict and can show native play overlays for unmuted videos. The platform handles this with a shared global mute state:
**Problem:** unmuted background audio/video, hover/click effects, and media-player sound cannot reliably autoplay before user interaction.
**Solution:**
1. **CSS Layer** - Hide native iOS video controls via `-webkit-media-controls-*` selectors in `main.css`
2. **Always Start Muted** - Presentation audio starts muted for autoplay compatibility
3. **Global Sound Control** - `backgroundAudioController` stores shared mute state; `useGlobalAudioMute` exposes it to React
4. **Custom Sound Button** - `useVideoSoundControl` drives the RuntimeControls sound button and decides when it should be visible
5. **webkit-playsinline** - Legacy attribute added to `