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

# Parallax Device Dive

> A canonical phone rises into view before the camera pushes through its screen and layered app UI expands to fill the frame.

export const InstallCommand = ({command}) => {
  const [copied, setCopied] = React.useState(false);
  const copy = async () => {
    try {
      if (navigator.clipboard && window.isSecureContext) {
        await navigator.clipboard.writeText(command);
      } else {
        const previous = document.activeElement;
        const scratch = document.createElement("textarea");
        scratch.value = command;
        scratch.setAttribute("readonly", "");
        scratch.style.position = "fixed";
        scratch.style.opacity = "0";
        document.body.appendChild(scratch);
        scratch.select();
        document.execCommand("copy");
        document.body.removeChild(scratch);
        previous?.focus?.();
      }
      setCopied(true);
      setTimeout(() => setCopied(false), 2000);
    } catch {}
  };
  return <div className="hf-install-command not-prose my-4 flex items-stretch overflow-hidden rounded-xl border border-zinc-200 bg-zinc-50 dark:border-zinc-800 dark:bg-zinc-900">
      <code className="flex-1 overflow-x-auto whitespace-nowrap border-r border-zinc-200 px-4 py-3 font-mono text-sm text-zinc-800 dark:border-zinc-800 dark:text-zinc-100">
        {command}
      </code>
      <button type="button" onClick={copy} data-copied={copied ? "true" : "false"} aria-label={`Copy ${command} to the clipboard`} className="hf-install-copy">
        <svg className="hf-install-copy-clipboard" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 18 18" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
          <path d="M14.25 5.25H7.25C6.14543 5.25 5.25 6.14543 5.25 7.25V14.25C5.25 15.3546 6.14543 16.25 7.25 16.25H14.25C15.3546 16.25 16.25 15.3546 16.25 14.25V7.25C16.25 6.14543 15.3546 5.25 14.25 5.25Z" />
          <path d="M2.80103 11.998L1.77203 5.07397C1.61003 3.98097 2.36403 2.96397 3.45603 2.80197L10.38 1.77297C11.313 1.63397 12.19 2.16297 12.528 3.00097" />
        </svg>
        <svg className="hf-install-copy-check" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 18 18" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
          <path d="M2.75 9.5L6.5 13.25L15.25 4.5" />
        </svg>
      </button>
      <span className="hf-install-copy-status" role="status" aria-live="polite">
        {copied ? "Copied" : ""}
      </span>
    </div>;
};

<iframe className="w-full aspect-video rounded-xl border-0 bg-zinc-100 dark:bg-zinc-800" title="parallax-device-dive preview" loading="lazy" srcDoc={`<!doctype html><html><head><meta charset="utf-8"><style>html,body{margin:0;height:100%;overflow:hidden;background:transparent}hyperframes-player{display:block;width:100%;height:100%}</style><script src="https://cdn.jsdelivr.net/npm/@hyperframes/player@0.7/dist/hyperframes-player.global.js"><\/script></head><body><script>fetch("/public/catalog/components/parallax-device-dive.json").then(function(r){return r.json()}).then(function(d){var p=document.createElement("hyperframes-player");p.setAttribute("srcdoc",d.html);p.setAttribute("controls","");p.setAttribute("autoplay","");p.setAttribute("loop","");p.setAttribute("muted","");p.setAttribute("poster","https://static.heygen.ai/hyperframes-oss/docs/images/catalog/components/parallax-device-dive.png");document.body.appendChild(p)});<\/script></body></html>`} />

## Install

<InstallCommand command="npx hyperframes add parallax-device-dive" />

That writes one file: `compositions/components/parallax-device-dive.html`.

## Paste it into your composition

Open `compositions/components/parallax-device-dive.html` and copy what is inside into your own composition.

A component has no size or duration of its own. It takes both from the composition
you paste it into.

## Variables

Every one of these has a default, so the piece works untouched. Set the ones you
want to change on the element:

| Variable    | Default     | Accepts                   | What it does                                |
| ----------- | ----------- | ------------------------- | ------------------------------------------- |
| `accent`    | `blue`      | `green`, `blue`, `violet` | Highlight color used throughout the app UI. |
| `app_title` | `Dashboard` | string                    | Title displayed in the app header.          |

Set them with `data-variable-values` on the element that mounts it. These are the
defaults, so this behaves exactly like the preview above until you change one:

```html wrap theme={null}
<div
  data-composition-id="parallax-device-dive"
  data-composition-src="compositions/components/parallax-device-dive.html"
  data-variable-values='{"accent":"blue","app_title":"Dashboard"}'
></div>
```

## Source

<Accordion title={`parallax-device-dive.html`}>
  ```html theme={null}
  <!doctype html>
  <!--
    parallax-device-dive: HyperFrames video primitive (camera / device / transition)

    Concept: a canonical phone rises into view, then the camera pushes through
    its screen. The app header, hero card, and supporting cards separate at
    different apparent depths during the push, then settle into one coherent UI
    that fills the frame.

    Variables:
      - accent (green | blue | violet, default "blue"): app highlight color.
      - app_title (string, default "Dashboard"): title in the app header.

    Envelope (fixed IN/OUT, elastic HOLD only, never gsap.timeScale()):
      IN_BASE  = 1.05s  phone rises and settles on stage
      HOLD     = elastic subtle device drift before the camera move
      OUT_BASE = 1.65s  camera dives through the screen and the UI becomes stage
      If D is shorter than IN_BASE + OUT_BASE, IN and OUT compress together and
      HOLD becomes zero. OUT is a camera exit from the hardware, not a fade.

    Mount contract: the runtime clones only this template. #root fills the host
    box, establishes the container query basis, carries no data-width or
    data-height, and registers one paused timeline under the hardcoded
    parallax-device-dive id.
  -->
  <html
    lang="en"
    data-composition-id="parallax-device-dive"
    data-composition-duration="4.5"
    data-composition-variables='[
      { "id": "accent", "type": "enum", "role": "style", "label": "Accent", "description": "Highlight color used throughout the app UI.", "default": "blue", "options": [{ "value": "green", "label": "Green" }, { "value": "blue", "label": "Blue" }, { "value": "violet", "label": "Violet" }] },
      { "id": "app_title", "type": "string", "role": "content", "label": "App title", "description": "Title displayed in the app header.", "default": "Dashboard" }
    ]'
  >
    <head>
      <meta charset="UTF-8" />
      <title>Parallax Device Dive</title>
    </head>
    <body>
      <template>
        <div id="root" data-composition-id="parallax-device-dive" data-duration="4.5" data-fps="30">
          <style>
            *,
            *::before,
            *::after {
              box-sizing: border-box;
            }

            #root {
              position: absolute;
              inset: 0;
              overflow: hidden;
              container-type: size;
              isolation: isolate;
              background: #0b0c0e;
              color: #f8fafc;
              font-family: Inter, system-ui, sans-serif;
            }

            .pdd-clip,
            .pdd-camera {
              position: absolute;
              inset: 0;
            }

            .pdd-clip {
              overflow: hidden;
              background:
                radial-gradient(
                  circle at 50% 48%,
                  color-mix(in srgb, var(--pdd-accent) 17%, transparent),
                  transparent 34%
                ),
                #0b0c0e;
            }

            .pdd-camera {
              display: grid;
              place-items: center;
              perspective: 125cqw;
            }

            .pdd-phone {
              position: relative;
              height: 82cqh;
              aspect-ratio: 9 / 19.5;
              padding: 0.72cqh;
              border: 0.08cqw solid rgba(255, 255, 255, 0.28);
              border-radius: 4.4cqh;
              background: linear-gradient(145deg, #3c4049, #17191e 44%, #08090c);
              box-shadow:
                0 4.8cqh 10cqh rgba(0, 0, 0, 0.5),
                0 1.4cqh 3cqh rgba(0, 0, 0, 0.4),
                inset 0 0.12cqh 0 rgba(255, 255, 255, 0.3),
                inset 0 0 0 0.08cqw rgba(0, 0, 0, 0.72);
              transform-origin: 50% 50%;
              will-change: transform, opacity;
            }

            .pdd-screen {
              position: relative;
              width: 100%;
              height: 100%;
              overflow: hidden;
              border-radius: 3.72cqh;
              background: #f4f6fb;
              box-shadow: inset 0 0 0 0.08cqw rgba(15, 23, 42, 0.28);
              color: #111827;
              isolation: isolate;
            }

            .pdd-app-glow {
              position: absolute;
              inset: 0;
              background:
                radial-gradient(
                  circle at 76% 8%,
                  color-mix(in srgb, var(--pdd-accent) 28%, transparent),
                  transparent 31%
                ),
                linear-gradient(180deg, #ffffff, #eef2f8);
            }

            .pdd-cutout {
              position: absolute;
              z-index: 10;
              top: 1.2cqh;
              left: 50%;
              width: 7.2cqh;
              height: 2.1cqh;
              border-radius: 1.2cqh;
              background: #050607;
              transform: translateX(-50%);
            }

            .pdd-ui {
              position: absolute;
              inset: 0;
              padding: 6.2cqh 2.1cqh 2.2cqh;
              display: flex;
              flex-direction: column;
              gap: 1.55cqh;
            }

            .pdd-header,
            .pdd-hero,
            .pdd-stats {
              position: relative;
              z-index: 2;
              transform-origin: 50% 50%;
              will-change: transform;
            }

            .pdd-header {
              display: flex;
              align-items: center;
              justify-content: space-between;
            }

            .pdd-heading {
              min-width: 0;
            }

            .pdd-kicker {
              color: #788397;
              font-size: 0.75cqw;
              font-weight: 700;
              letter-spacing: 0.12em;
              line-height: 1;
              text-transform: uppercase;
            }

            .pdd-title {
              max-width: 13cqw;
              overflow: hidden;
              color: #111827;
              font-size: 1.5cqw;
              font-weight: 780;
              letter-spacing: -0.04em;
              line-height: 1.12;
              text-overflow: ellipsis;
              white-space: nowrap;
            }

            .pdd-avatar {
              flex: 0 0 auto;
              width: 3.7cqh;
              aspect-ratio: 1;
              display: grid;
              place-items: center;
              border-radius: 50%;
              background: var(--pdd-accent);
              color: #ffffff;
              font-size: 0.72cqw;
              font-weight: 800;
              box-shadow: 0 0.55cqh 1.4cqh color-mix(in srgb, var(--pdd-accent) 32%, transparent);
            }

            .pdd-hero {
              min-height: 24cqh;
              padding: 2.1cqh;
              overflow: hidden;
              border: 0.08cqw solid rgba(255, 255, 255, 0.12);
              border-radius: 2.1cqh;
              background: #101722;
              box-shadow: 0 1.7cqh 3.8cqh rgba(15, 23, 42, 0.2);
            }

            .pdd-hero-label {
              color: #a9b4c7;
              font-size: 0.72cqw;
              font-weight: 650;
            }

            .pdd-value {
              margin-top: 0.45cqh;
              color: #ffffff;
              font-size: 2.15cqw;
              font-weight: 790;
              letter-spacing: -0.055em;
              line-height: 1;
            }

            .pdd-value span {
              color: var(--pdd-accent);
              font-size: 0.76cqw;
              letter-spacing: 0;
            }

            .pdd-chart {
              position: absolute;
              left: 2.1cqh;
              right: 2.1cqh;
              bottom: 2.2cqh;
              height: 8.2cqh;
              display: flex;
              align-items: end;
              gap: 0.55cqh;
            }

            .pdd-bar {
              flex: 1 1 0;
              min-width: 0;
              border-radius: 0.55cqh 0.55cqh 0.18cqh 0.18cqh;
              background: color-mix(in srgb, var(--pdd-accent) 38%, #263244);
            }

            .pdd-bar:nth-child(1) {
              height: 38%;
            }

            .pdd-bar:nth-child(2) {
              height: 54%;
            }

            .pdd-bar:nth-child(3) {
              height: 46%;
            }

            .pdd-bar:nth-child(4) {
              height: 72%;
            }

            .pdd-bar:nth-child(5) {
              height: 64%;
            }

            .pdd-bar:nth-child(6) {
              height: 92%;
              background: var(--pdd-accent);
              box-shadow: 0 0.55cqh 1.5cqh color-mix(in srgb, var(--pdd-accent) 30%, transparent);
            }

            .pdd-stats {
              display: grid;
              grid-template-columns: 1fr 1fr;
              gap: 1.15cqh;
            }

            .pdd-stat {
              min-height: 11.5cqh;
              padding: 1.55cqh;
              border: 0.08cqw solid rgba(55, 65, 81, 0.08);
              border-radius: 1.7cqh;
              background: rgba(255, 255, 255, 0.94);
              box-shadow: 0 1.1cqh 2.6cqh rgba(71, 85, 105, 0.12);
            }

            .pdd-stat-label {
              color: #7b8799;
              font-size: 0.65cqw;
              font-weight: 650;
            }

            .pdd-stat-value {
              margin-top: 1cqh;
              color: #18202d;
              font-size: 1.25cqw;
              font-weight: 780;
              letter-spacing: -0.035em;
            }

            .pdd-stat-value span {
              color: var(--pdd-accent);
            }
          </style>

          <div
            id="parallax-device-dive-clip"
            class="pdd-clip clip"
            data-start="0"
            data-duration="4.5"
            data-track-index="0"
          >
            <div class="pdd-camera">
              <div class="pdd-phone">
                <div class="pdd-screen">
                  <div class="pdd-app-glow"></div>
                  <div class="pdd-ui">
                    <div class="pdd-header">
                      <div class="pdd-heading">
                        <div class="pdd-kicker">Overview</div>
                        <div class="pdd-title"></div>
                      </div>
                      <div class="pdd-avatar">HF</div>
                    </div>

                    <div class="pdd-hero">
                      <div class="pdd-hero-label">Weekly activity</div>
                      <div class="pdd-value">24.8k <span>+18%</span></div>
                      <div class="pdd-chart" aria-hidden="true">
                        <span class="pdd-bar"></span>
                        <span class="pdd-bar"></span>
                        <span class="pdd-bar"></span>
                        <span class="pdd-bar"></span>
                        <span class="pdd-bar"></span>
                        <span class="pdd-bar"></span>
                      </div>
                    </div>

                    <div class="pdd-stats">
                      <div class="pdd-stat">
                        <div class="pdd-stat-label">Conversion</div>
                        <div class="pdd-stat-value"><span>8.4%</span></div>
                      </div>
                      <div class="pdd-stat">
                        <div class="pdd-stat-label">Active users</div>
                        <div class="pdd-stat-value">12,604</div>
                      </div>
                    </div>
                  </div>
                </div>
                <div class="pdd-cutout" aria-hidden="true"></div>
              </div>
            </div>
          </div>

          <script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
          <script>
            (function () {
              "use strict";

              var root = document.getElementById("root");
              var phone = root.querySelector(".pdd-phone");
              var cutout = root.querySelector(".pdd-cutout");
              var header = root.querySelector(".pdd-header");
              var hero = root.querySelector(".pdd-hero");
              var stats = root.querySelector(".pdd-stats");
              var title = root.querySelector(".pdd-title");
              var vars =
                window.__hyperframes && window.__hyperframes.getVariables
                  ? window.__hyperframes.getVariables()
                  : {};

              var accent = vars.accent;
              var accentColors = {
                green: "var(--brand, #a1a1aa)",
                blue: "var(--accent, #a1a1aa)",
                violet: "var(--accent-2, #a1a1aa)",
              };
              var accentColor = accentColors[accent] || accentColors.blue;
              var appTitle = vars.app_title == null ? "Dashboard" : String(vars.app_title);

              root.style.setProperty("--pdd-accent", accentColor);
              title.textContent = appTitle;

              var IN_BASE = 1.05;
              var OUT_BASE = 1.65;
              var duration = Math.max(0.001, parseFloat(root.dataset.duration || "4.5"));
              var totalBase = IN_BASE + OUT_BASE;
              var envelopeScale = duration < totalBase ? duration / totalBase : 1;
              var IN = IN_BASE * envelopeScale;
              var OUT = OUT_BASE * envelopeScale;
              var HOLD = Math.max(0, duration - IN - OUT);
              var HOLD_START = IN;
              var OUT_START = IN + HOLD;
              var RISE = IN * 0.78;
              var SETTLE = IN - RISE;
              var DIVE = OUT * 0.72;
              var DEPTH_SETTLE = OUT - DIVE;

              var tl = gsap.timeline({ paused: true });

              tl.fromTo(
                phone,
                { opacity: 0, y: "18cqh", scale: 0.86 },
                { opacity: 1, y: "-1cqh", scale: 1.02, duration: RISE, ease: "power3.out" },
                0,
              );
              tl.to(phone, { y: 0, scale: 1, duration: SETTLE, ease: "power2.out" }, RISE);

              if (HOLD > 0) {
                tl.to(phone, { y: "-0.55cqh", duration: HOLD * 0.5, ease: "sine.inOut" }, HOLD_START);
                tl.to(
                  phone,
                  { y: 0, duration: HOLD * 0.5, ease: "sine.inOut" },
                  HOLD_START + HOLD * 0.5,
                );
              }

              tl.to(phone, { scale: 6.4, y: "2.4cqh", duration: OUT, ease: "power4.in" }, OUT_START);
              tl.to(cutout, { opacity: 0, duration: DIVE * 0.45, ease: "power2.out" }, OUT_START);
              tl.to(
                header,
                { y: "-1.2cqh", scale: 1.035, duration: DIVE, ease: "power4.in" },
                OUT_START,
              );
              tl.to(
                hero,
                { y: "-2.1cqh", scale: 1.09, duration: DIVE, ease: "power4.in" },
                OUT_START,
              );
              tl.to(
                stats,
                { y: "2.4cqh", scale: 1.16, duration: DIVE, ease: "power4.in" },
                OUT_START,
              );
              tl.to(
                [header, hero, stats],
                { y: 0, scale: 1, duration: DEPTH_SETTLE, ease: "power4.out" },
                OUT_START + DIVE,
              );
              tl.seek(0);
              window.__timelines = window.__timelines || {};
              window.__timelines["parallax-device-dive"] = tl;
            })();
          </script>
        </div>
      </template>
    </body>
  </html>
  ```
</Accordion>

Tagged `motion-primitive` `camera` `device` `phone` `parallax` `transition`.

## Related topics

* [Browse the complete Catalog](/catalog)
* [Add assets and Catalog items in Studio](/studio/assets-and-blocks)
* [Build a richer composition](/go-further)


## Related topics

- [Parallax Unzoom](/catalog/components/parallax-unzoom.md)
- [Parallax Zoom](/catalog/components/parallax-zoom.md)
- [Parallax Layers](/catalog/components/caption-parallax-layers.md)
