> ## Documentation Index
> Fetch the complete documentation index at: https://hyperframes-fix-nested-composition-media-inpoint.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# iOS 26 Liquid Glass Home Screen

> 3D iPhone with a normal iOS 26 home screen, liquid glass app icons, shader wallpaper, dock, and fluid glass notifications that drop from the status area onto a GLTF device model.

export const InstallCommand = ({command, item}) => {
  const [copied, setCopied] = React.useState(false);
  const [tuned, setTuned] = React.useState("");
  React.useEffect(() => {
    if (!item) return;
    const read = () => {
      try {
        const raw = new URLSearchParams(window.location.search).get(`vars-${item}`);
        if (!raw) return setTuned("");
        const parsed = JSON.parse(raw);
        if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return setTuned("");
        if (Object.keys(parsed).length === 0) return setTuned("");
        setTuned(` --vars '${JSON.stringify(parsed)}'`);
      } catch {
        setTuned("");
      }
    };
    read();
    window.addEventListener("hf-vars-changed", read);
    window.addEventListener("popstate", read);
    return () => {
      window.removeEventListener("hf-vars-changed", read);
      window.removeEventListener("popstate", read);
    };
  }, [item]);
  const fullCommand = `${command}${tuned}`;
  const copy = async () => {
    try {
      if (navigator.clipboard && window.isSecureContext) {
        await navigator.clipboard.writeText(fullCommand);
      } else {
        const previous = document.activeElement;
        const scratch = document.createElement("textarea");
        scratch.value = fullCommand;
        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">
        {fullCommand}
      </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>;
};

<video className="w-full aspect-video rounded-xl object-cover bg-zinc-100 dark:bg-zinc-800" src="https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/ios26-liquid-glass.mp4" poster="https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/ios26-liquid-glass.png" autoPlay muted loop playsInline />

## Install

<InstallCommand command="npx hyperframes add ios26-liquid-glass" item="ios26-liquid-glass" />

That writes `compositions/ios26-liquid-glass.html`, plus 28 supporting files under `models/`, `lib/`, and `assets/icons/`.

<Danger>
  Live preview needs the `chrome://flags/#canvas-draw-element` flag switched on.
  Without it this item's screen renders black. Rendering from the CLI switches
  it on for you. [How it works](/guides/html-in-canvas)
</Danger>

## Source

<Accordion title={`ios26-liquid-glass.html`}>
  ```html theme={null}
  <!doctype html>
  <html lang="en">
    <head>
      <meta charset="utf-8" />
      <meta name="viewport" content="width=1920, height=1080" />
      <title>Devices Canvas — HTML-in-Canvas API Showcase</title>
      <script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
      <script src="https://cdn.jsdelivr.net/npm/three@0.147.0/build/three.min.js"></script>
      <script src="https://cdn.jsdelivr.net/npm/three@0.147.0/examples/js/loaders/DRACOLoader.js"></script>
      <script src="https://cdn.jsdelivr.net/npm/three@0.147.0/examples/js/loaders/GLTFLoader.js"></script>
      <style>
        * {
          margin: 0;
          padding: 0;
          box-sizing: border-box;
        }
        body {
          background: #050508;
          overflow: hidden;
        }
        #root {
          position: relative;
          width: 1920px;
          height: 1080px;
          overflow: hidden;
          background: #050508;
        }
        .ios-home {
          position: relative;
          height: 844px;
          overflow: hidden;
          background: #010101;
        }
        .ios-wallpaper {
          position: absolute;
          inset: 0;
          background:
            radial-gradient(ellipse at 50% 50%, rgba(12, 16, 24, 0.96), transparent 31%),
            radial-gradient(circle at 50% 54%, rgba(255, 255, 255, 0.06), transparent 8%),
            linear-gradient(180deg, #020202, #000);
        }
        .ios-wallpaper::before,
        .ios-wallpaper::after {
          content: "";
          position: absolute;
          left: 92px;
          top: 210px;
          width: 210px;
          height: 360px;
          border-radius: 50% 50% 42% 46%;
          border: 4px solid transparent;
          background:
            linear-gradient(#020202, #020202) padding-box,
            conic-gradient(
                from 215deg,
                rgba(255, 255, 255, 0),
                rgba(255, 255, 255, 0.92),
                rgba(53, 131, 255, 0.92),
                rgba(38, 255, 168, 0.8),
                rgba(255, 63, 54, 0.72),
                rgba(255, 255, 255, 0)
              )
              border-box;
          filter: blur(0.35px) saturate(1.25);
          opacity: 0.82;
          transform: rotate(7deg);
        }
        .ios-wallpaper::after {
          left: 118px;
          top: 400px;
          width: 140px;
          height: 190px;
          border-width: 3px;
          opacity: 0.62;
          transform: rotate(-27deg);
        }
        .ios-status {
          position: relative;
          z-index: 2;
          display: flex;
          align-items: center;
          justify-content: space-between;
          padding: 27px 30px 10px;
          color: #f6f6f6;
          font-size: 22px;
          font-weight: 760;
          letter-spacing: 0;
          text-shadow: 0 2px 8px rgba(0, 0, 0, 0.55);
        }
        .ios-status-left,
        .ios-status-right {
          display: flex;
          align-items: center;
          gap: 8px;
        }
        .ios-location {
          display: grid;
          place-items: center;
          width: 18px;
          height: 18px;
          border-radius: 999px;
          background: #2f80ed;
          color: #fff;
          font-size: 12px;
          line-height: 1;
        }
        .ios-signal {
          display: flex;
          align-items: end;
          gap: 3px;
          height: 18px;
        }
        .ios-signal span {
          width: 5px;
          border-radius: 3px;
          background: rgba(255, 255, 255, 0.92);
        }
        .ios-signal span:nth-child(1) {
          height: 7px;
        }
        .ios-signal span:nth-child(2) {
          height: 10px;
        }
        .ios-signal span:nth-child(3) {
          height: 13px;
        }
        .ios-signal span:nth-child(4) {
          height: 17px;
          background: rgba(255, 255, 255, 0.48);
        }
        .ios-wifi {
          position: relative;
          width: 23px;
          height: 17px;
        }
        .ios-wifi span {
          position: absolute;
          left: 50%;
          border: 4px solid rgba(255, 255, 255, 0.94);
          border-color: rgba(255, 255, 255, 0.94) transparent transparent transparent;
          border-radius: 50%;
          transform: translateX(-50%);
        }
        .ios-wifi span:nth-child(1) {
          top: 0;
          width: 28px;
          height: 28px;
        }
        .ios-wifi span:nth-child(2) {
          top: 6px;
          width: 18px;
          height: 18px;
        }
        .ios-wifi span:nth-child(3) {
          top: 13px;
          width: 7px;
          height: 7px;
          border-radius: 999px;
          background: #fff;
          border: 0;
        }
        .ios-battery {
          position: relative;
          min-width: 34px;
          height: 21px;
          padding: 1px 5px 0;
          border-radius: 6px;
          background: #59d768;
          color: #fff;
          font-size: 15px;
          font-weight: 850;
          line-height: 20px;
        }
        .ios-battery::after {
          content: "";
          position: absolute;
          right: -3px;
          top: 7px;
          width: 3px;
          height: 7px;
          border-radius: 0 3px 3px 0;
          background: rgba(255, 255, 255, 0.5);
        }
        .ios-app-grid {
          position: relative;
          z-index: 2;
          display: grid;
          grid-template-columns: repeat(4, 1fr);
          gap: 14px 22px;
          padding: 20px 28px 0;
        }
        .ios-app {
          position: relative;
          text-align: center;
        }
        .ios-app-icon,
        .ios-dock-icon {
          display: block;
          object-fit: cover;
          box-shadow:
            0 8px 20px rgba(0, 0, 0, 0.34),
            inset 0 1px 0 rgba(255, 255, 255, 0.24);
          filter: grayscale(1) saturate(0.08) contrast(1.2) brightness(0.86);
        }
        .ios-app-icon {
          width: 62px;
          height: 62px;
          margin: 0 auto 7px;
          border-radius: 18px;
        }
        .ios-app-label {
          color: #fff;
          font-size: 13px;
          font-weight: 640;
          line-height: 1.1;
          text-shadow: 0 1px 5px rgba(0, 0, 0, 0.65);
          white-space: nowrap;
        }
        .ios-badge {
          position: absolute;
          top: -8px;
          right: -8px;
          min-width: 24px;
          padding: 3px 7px;
          border-radius: 999px;
          background: #f7f7f7;
          color: #303030;
          font-size: 15px;
          font-weight: 700;
          font-variant-numeric: tabular-nums;
          box-shadow: 0 1px 4px rgba(0, 0, 0, 0.32);
        }
        .ios-search-pill {
          position: absolute;
          z-index: 2;
          left: 50%;
          bottom: 105px;
          display: flex;
          align-items: center;
          justify-content: center;
          gap: 7px;
          width: 116px;
          padding: 9px 0;
          border-radius: 19px;
          background: rgba(255, 255, 255, 0.11);
          border: 1px solid rgba(255, 255, 255, 0.1);
          color: rgba(255, 255, 255, 0.72);
          font-size: 15px;
          font-weight: 650;
          text-align: center;
          transform: translateX(-50%);
          box-shadow:
            inset 0 1px 0 rgba(255, 255, 255, 0.17),
            0 10px 22px rgba(0, 0, 0, 0.34);
        }
        .ios-search-pill::before {
          content: "";
          width: 12px;
          height: 12px;
          border: 3px solid rgba(255, 255, 255, 0.72);
          border-radius: 999px;
          box-sizing: border-box;
        }
        .ios-search-pill::after {
          content: "";
          width: 7px;
          height: 3px;
          border-radius: 999px;
          background: rgba(255, 255, 255, 0.72);
          transform: rotate(45deg) translate(-6px, 6px);
          margin-left: -10px;
        }
        .ios-notification-layer {
          display: none;
          position: absolute;
          z-index: 5;
          top: 82px;
          left: 15px;
          right: 15px;
          height: 222px;
          pointer-events: none;
        }
        .ios-notification-card {
          position: absolute;
          left: 0;
          right: 0;
          display: flex;
          align-items: center;
          gap: 10px;
          min-height: 60px;
          padding: 9px 12px;
          border-radius: 23px;
          border: 1px solid rgba(255, 255, 255, 0.27);
          background:
            linear-gradient(135deg, rgba(255, 255, 255, 0.42), rgba(255, 255, 255, 0.13)),
            rgba(140, 150, 164, 0.52);
          -webkit-backdrop-filter: blur(22px) saturate(1.35);
          backdrop-filter: blur(22px) saturate(1.35);
          box-shadow:
            inset 0 1px 0 rgba(255, 255, 255, 0.46),
            0 14px 34px rgba(0, 0, 0, 0.38);
          opacity: 0;
          transform: translateY(-120px) scale(0.98);
        }
        .ios-notification-card:nth-child(2) {
          top: 68px;
        }
        .ios-notification-card:nth-child(3) {
          top: 136px;
        }
        .ios-notification-icon {
          width: 38px;
          height: 38px;
          border-radius: 11px;
          background-position: center;
          background-size: cover;
          flex: 0 0 auto;
        }
        .ios-notification-messages {
          background-image: url("assets/icons/notify-messages.jpg");
        }
        .ios-notification-hyperframes {
          background-image: url("assets/icons/slack.jpg");
        }
        .ios-notification-gmail {
          background-image: url("assets/icons/gmail.jpg");
        }
        .ios-icon-preload {
          position: absolute;
          width: 1px;
          height: 1px;
          opacity: 0;
          pointer-events: none;
        }
        .ios-notification-copy {
          min-width: 0;
          flex: 1;
        }
        .ios-notification-title {
          display: flex;
          justify-content: space-between;
          gap: 8px;
          color: #fff;
          font-size: 12px;
          font-weight: 750;
        }
        .ios-notification-body {
          margin-top: 2px;
          color: rgba(255, 255, 255, 0.72);
          font-size: 11px;
          line-height: 1.25;
        }
        .ios-dock {
          position: absolute;
          z-index: 2;
          left: 14px;
          right: 14px;
          bottom: 13px;
          display: flex;
          justify-content: space-around;
          padding: 10px 16px;
          border-radius: 37px;
          background: rgba(255, 255, 255, 0.08);
          border: 1px solid rgba(255, 255, 255, 0.16);
          -webkit-backdrop-filter: blur(18px) saturate(1.2);
          backdrop-filter: blur(18px) saturate(1.2);
          box-shadow:
            inset 0 1px 0 rgba(255, 255, 255, 0.16),
            0 14px 36px rgba(0, 0, 0, 0.24);
        }
        .ios-dock-item {
          position: relative;
          width: 62px;
          height: 62px;
        }
        .ios-dock-icon {
          width: 62px;
          height: 62px;
          border-radius: 18px;
          background-position: center;
          background-size: cover;
        }
        .ios-dock-phone {
          background-image: url("assets/icons/phone.jpg");
        }
        .ios-dock-brave {
          background-image: url("assets/icons/brave.jpg");
        }
        .ios-dock-whatsapp {
          background-image: url("assets/icons/whatsapp.jpg");
        }
        .ios-dock-music {
          background-image: url("assets/icons/music.jpg");
        }
      </style>
    </head>
    <body>
      <div
        id="root"
        data-composition-id="ios26-liquid-glass"
        data-width="1920"
        data-height="1080"
        data-start="0"
        data-duration="15"
        data-root="true"
      >
        <!-- LIVE HTML captured every frame via drawElementImage  -->
        <canvas
          id="cap-phone"
          layoutsubtree
          width="390"
          height="844"
          style="
            position: absolute;
            top: 0;
            left: 0;
            width: 390px;
            height: 844px;
            z-index: -1;
            pointer-events: none;
          "
        >
          <div
            id="phone-html"
            style="
              width: 390px;
              height: 844px;
              overflow: hidden;
              font-family: Inter, system-ui, sans-serif;
              color: #e2e8f0;
            "
          >
            <div id="phone-scroll" class="ios-home" style="color: #fff; transform: translateY(0)">
              <div id="phone-title" style="display: none"></div>
              <span id="phone-count" style="display: none"></span>
              <div id="phone-cards" style="display: none"></div>
              <img
                id="ios-notify-messages-preload"
                class="ios-icon-preload"
                src="assets/icons/notify-messages.jpg"
                alt=""
              />
              <div class="ios-wallpaper"></div>
              <div class="ios-status">
                <div class="ios-status-left">
                  <span style="font-variant-numeric: tabular-nums">15:16</span>
                  <span class="ios-location">▲</span>
                </div>
                <div class="ios-status-right">
                  <div class="ios-signal"><span></span><span></span><span></span><span></span></div>
                  <div class="ios-wifi"><span></span><span></span><span></span></div>
                  <div class="ios-battery">92</div>
                </div>
              </div>
              <div id="ios-app-grid" class="ios-app-grid">
                <div class="ios-app">
                  <img class="ios-app-icon" src="assets/icons/weather.jpg" alt="" />
                  <div class="ios-app-label">Weather</div>
                </div>
                <div class="ios-app">
                  <img class="ios-app-icon" src="assets/icons/stocks.jpg" alt="" />
                  <div class="ios-app-label">Stocks</div>
                </div>
                <div class="ios-app">
                  <img class="ios-app-icon" src="assets/icons/find-my.jpg" alt="" />
                  <div class="ios-app-label">Find My</div>
                </div>
                <div class="ios-app">
                  <img class="ios-app-icon" src="assets/icons/home.jpg" alt="" />
                  <div class="ios-app-label">Home</div>
                </div>
                <div class="ios-app">
                  <img class="ios-app-icon" src="assets/icons/books.jpg" alt="" />
                  <div class="ios-app-label">Books</div>
                </div>
                <div class="ios-app">
                  <img class="ios-app-icon" src="assets/icons/itunes-store.jpg" alt="" />
                  <div class="ios-app-label">iTunes Store</div>
                </div>
                <div class="ios-app">
                  <img class="ios-app-icon" src="assets/icons/fitness.jpg" alt="" />
                  <div class="ios-app-label">Fitness</div>
                </div>
                <div class="ios-app">
                  <img class="ios-app-icon" src="assets/icons/watch.jpg" alt="" />
                  <div class="ios-app-label">Watch</div>
                </div>
                <div class="ios-app">
                  <img class="ios-app-icon" src="assets/icons/contacts.jpg" alt="" />
                  <div class="ios-app-label">Contacts</div>
                </div>
                <div class="ios-app">
                  <img class="ios-app-icon" src="assets/icons/files.jpg" alt="" />
                  <div class="ios-app-label">Files</div>
                </div>
                <div class="ios-app">
                  <img class="ios-app-icon" src="assets/icons/translate.jpg" alt="" />
                  <div class="ios-app-label">Translate</div>
                </div>
                <div class="ios-app">
                  <img class="ios-app-icon" src="assets/icons/calendar.jpg" alt="" />
                  <div class="ios-app-label">Calendar</div>
                </div>
                <div class="ios-app">
                  <img class="ios-app-icon" src="assets/icons/photos.jpg" alt="" />
                  <div class="ios-app-label">Photos</div>
                </div>
                <div class="ios-app">
                  <img class="ios-app-icon" src="assets/icons/chatgpt.jpg" alt="" />
                  <div class="ios-app-label">ChatGPT</div>
                </div>
                <div class="ios-app">
                  <img class="ios-app-icon" src="assets/icons/slack.jpg" alt="" />
                  <div class="ios-app-label">Slack</div>
                </div>
                <div class="ios-app">
                  <img class="ios-app-icon" src="assets/icons/instagram.jpg" alt="" />
                  <span class="ios-badge">1</span>
                  <div class="ios-app-label">Instagram</div>
                </div>
                <div class="ios-app">
                  <img class="ios-app-icon" src="assets/icons/x.jpg" alt="" />
                  <span class="ios-badge">4</span>
                  <div class="ios-app-label">X</div>
                </div>
                <div class="ios-app">
                  <img class="ios-app-icon" src="assets/icons/proton-mail.jpg" alt="" />
                  <span class="ios-badge">797</span>
                  <div class="ios-app-label">Proton Mail</div>
                </div>
                <div class="ios-app">
                  <img class="ios-app-icon" src="assets/icons/gmail.jpg" alt="" />
                  <span class="ios-badge">31.8K</span>
                  <div class="ios-app-label">Gmail</div>
                </div>
                <div class="ios-app">
                  <img class="ios-app-icon" src="assets/icons/revolut.jpg" alt="" />
                  <div class="ios-app-label">Revolut</div>
                </div>
                <div class="ios-app">
                  <img class="ios-app-icon" src="assets/icons/phone.jpg" alt="" />
                  <span class="ios-badge">9+</span>
                  <div class="ios-app-label">Phone</div>
                </div>
                <div class="ios-app">
                  <img class="ios-app-icon" src="assets/icons/brave.jpg" alt="" />
                  <div class="ios-app-label">Brave</div>
                </div>
                <div class="ios-app">
                  <img class="ios-app-icon" src="assets/icons/whatsapp.jpg" alt="" />
                  <span class="ios-badge">282</span>
                  <div class="ios-app-label">WhatsApp</div>
                </div>
                <div class="ios-app">
                  <img class="ios-app-icon" src="assets/icons/music.jpg" alt="" />
                  <div class="ios-app-label">Music</div>
                </div>
              </div>

              <div class="ios-notification-layer">
                <div id="ios-notification-a" class="ios-notification-card">
                  <div class="ios-notification-icon ios-notification-messages"></div>
                  <div class="ios-notification-copy">
                    <div class="ios-notification-title"><span>Messages</span><span>now</span></div>
                    <div class="ios-notification-body">Liquid Glass preview is ready for review.</div>
                  </div>
                </div>
                <div id="ios-notification-b" class="ios-notification-card">
                  <div class="ios-notification-icon ios-notification-hyperframes"></div>
                  <div class="ios-notification-copy">
                    <div class="ios-notification-title"><span>HyperFrames</span><span>1m</span></div>
                    <div class="ios-notification-body">One-worker render is ready.</div>
                  </div>
                </div>
                <div id="ios-notification-c" class="ios-notification-card">
                  <div class="ios-notification-icon ios-notification-gmail"></div>
                  <div class="ios-notification-copy">
                    <div class="ios-notification-title"><span>Gmail</span><span>2m</span></div>
                    <div class="ios-notification-body">Preview videos are live on CDN.</div>
                  </div>
                </div>
              </div>

              <div class="ios-search-pill">Search</div>

              <div id="ios-real-dock" class="ios-dock">
                <div class="ios-dock-item">
                  <div class="ios-dock-icon ios-dock-phone"></div>
                  <span class="ios-badge">9+</span>
                </div>
                <div class="ios-dock-item">
                  <div class="ios-dock-icon ios-dock-brave"></div>
                </div>
                <div class="ios-dock-item">
                  <div class="ios-dock-icon ios-dock-whatsapp"></div>
                  <span class="ios-badge">282</span>
                </div>
                <div class="ios-dock-item">
                  <div class="ios-dock-icon ios-dock-music"></div>
                </div>
              </div>
            </div>
          </div>
        </canvas>

        <canvas
          id="cap-laptop"
          layoutsubtree
          width="1440"
          height="900"
          style="
            position: absolute;
            top: 0;
            left: 0;
            width: 1440px;
            height: 900px;
            z-index: -1;
            pointer-events: none;
          "
        >
          <div
            id="laptop-html"
            style="
              width: 1440px;
              height: 900px;
              overflow: hidden;
              font-family: Inter, system-ui, sans-serif;
              color: #e2e8f0;
            "
          >
            <div
              id="laptop-scroll"
              style="background: linear-gradient(180deg, #0a0a12, #0f0f1a); transform: translateY(0)"
            >
              <div
                style="
                  display: flex;
                  align-items: center;
                  justify-content: space-between;
                  padding: 18px 36px;
                  border-bottom: 1px solid rgba(255, 255, 255, 0.06);
                "
              >
                <span
                  style="
                    font-weight: 800;
                    font-size: 17px;
                    background: linear-gradient(135deg, #34d399, #3b82f6);
                    -webkit-background-clip: text;
                    -webkit-text-fill-color: transparent;
                  "
                  >HyperFrames</span
                >
                <div
                  style="display: flex; gap: 20px; font-size: 12px; color: rgba(255, 255, 255, 0.5)"
                >
                  <span>Docs</span><span>Playground</span><span>Pricing</span>
                </div>
                <span
                  style="
                    font-size: 11px;
                    font-weight: 600;
                    padding: 6px 14px;
                    border-radius: 6px;
                    background: #10b981;
                    color: #fff;
                  "
                  >Log in</span
                >
              </div>
              <div
                id="laptop-title"
                style="
                  font-size: 40px;
                  font-weight: 800;
                  letter-spacing: -1px;
                  padding: 28px 36px 10px;
                  opacity: 0;
                "
              >
                Community Playground
              </div>
              <div style="font-size: 13px; color: rgba(255, 255, 255, 0.4); padding: 0 36px 18px">
                Create videos with HTML — powered by HyperFrames
              </div>
              <div style="display: flex; gap: 8px; padding: 0 36px 20px">
                <span
                  style="
                    font-size: 10px;
                    font-weight: 600;
                    padding: 5px 12px;
                    border-radius: 16px;
                    background: rgba(16, 185, 129, 0.15);
                    color: #10b981;
                    border: 1px solid rgba(16, 185, 129, 0.3);
                  "
                  >Examples</span
                ><span
                  id="laptop-count"
                  style="
                    font-size: 10px;
                    font-weight: 600;
                    padding: 5px 12px;
                    border-radius: 16px;
                    border: 1px solid rgba(255, 255, 255, 0.1);
                    color: rgba(255, 255, 255, 0.5);
                  "
                  >Catalog 0</span
                >
              </div>
              <div
                id="laptop-cards"
                style="
                  display: grid;
                  grid-template-columns: repeat(3, 1fr);
                  gap: 14px;
                  padding: 0 36px 36px;
                "
              ></div>
            </div>
          </div>
        </canvas>

        <canvas
          id="theater"
          width="1920"
          height="1080"
          style="position: absolute; top: 0; left: 0; width: 1920px; height: 1080px"
        ></canvas>
        <div
          id="driver"
          class="clip"
          data-start="0"
          data-duration="15"
          data-track-index="0"
          style="position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none"
        ></div>
      </div>
      <script>
        var W = 1920,
          H = 1080,
          PI = Math.PI;

        // ── Renderer ───────────────────────────────────────────────────────
        var renderer = new THREE.WebGLRenderer({
          canvas: document.getElementById("theater"),
          antialias: true,
          preserveDrawingBuffer: true,
        });
        renderer.setSize(W, H, false);
        renderer.setPixelRatio(1);
        renderer.toneMapping = THREE.ACESFilmicToneMapping;
        renderer.toneMappingExposure = 1.15;
        renderer.outputEncoding = THREE.sRGBEncoding;
        renderer.setClearColor(0x050508, 1);

        var scene = new THREE.Scene();
        scene.background = new THREE.Color(0x050508);
        var camera = new THREE.PerspectiveCamera(34, W / H, 0.1, 100);

        // ── Environment Map ────────────────────────────────────────────────
        var eC = document.createElement("canvas");
        eC.width = 1024;
        eC.height = 512;
        var ec = eC.getContext("2d");
        var eg = ec.createLinearGradient(0, 0, 1024, 512);
        eg.addColorStop(0, "#020307");
        eg.addColorStop(0.15, "#1a1a2e");
        eg.addColorStop(0.3, "#ffffff");
        eg.addColorStop(0.45, "#0a0a1e");
        eg.addColorStop(0.6, "#ffffff");
        eg.addColorStop(0.75, "#1e1e2e");
        eg.addColorStop(1, "#020307");
        ec.fillStyle = eg;
        ec.fillRect(0, 0, 1024, 512);
        ec.globalCompositeOperation = "screen";
        for (var ei = 0; ei < 7; ei++) {
          ec.fillStyle = "rgba(255,255,255," + (ei % 2 === 0 ? 0.15 : 0.08) + ")";
          ec.fillRect((ei % 3) * 100, 60 + ei * 56, 700, 3 + (ei % 2) * 2);
        }
        var eT = new THREE.CanvasTexture(eC);
        eT.mapping = THREE.EquirectangularReflectionMapping;
        var pm = new THREE.PMREMGenerator(renderer);
        scene.environment = pm.fromEquirectangular(eT).texture;
        eT.dispose();
        pm.dispose();

        // ── Three.js shader backdrop ────────────────────────────────────────
        var bgUniforms = { uTime: { value: 0 }, uAspect: { value: W / H } };
        var bgPlane = new THREE.Mesh(
          new THREE.PlaneGeometry(28, 16),
          new THREE.ShaderMaterial({
            uniforms: bgUniforms,
            vertexShader:
              "varying vec2 v;void main(){v=uv;gl_Position=projectionMatrix*modelViewMatrix*vec4(position,1.0);}",
            fragmentShader:
              "uniform float uTime;uniform float uAspect;varying vec2 v;float wave(vec2 p,float s,float t){return sin(p.x*s+p.y*s*.57+uTime*t);}void main(){vec2 p=v*2.0-1.0;p.x*=uAspect;float w=wave(p,3.0,.22)+.55*wave(p.yx+vec2(.7,-.2),5.1,.16)+.32*wave(p+vec2(-.4,.5),8.0,-.12);float a=smoothstep(-.75,1.05,w);vec3 navy=vec3(.015,.013,.055);vec3 cyan=vec3(.02,.42,.72);vec3 violet=vec3(.30,.08,.55);vec3 amber=vec3(.95,.42,.04);float ribbon=smoothstep(.22,.95,sin(p.x*2.2-p.y*3.1+uTime*.18));vec3 c=mix(navy,cyan,a);c=mix(c,violet,smoothstep(.16,1.1,wave(p+vec2(.8,.1),2.4,.1)));c+=amber*ribbon*.33;float glow=exp(-dot(p+vec2(.62,-.08),p+vec2(.62,-.08))*1.7);c+=vec3(.10,.45,.55)*glow;float vign=smoothstep(1.85,.25,length(p));gl_FragColor=vec4(c*vign,1.0);}",
            depthTest: false,
            depthWrite: false,
          }),
        );
        bgPlane.position.z = -7.5;
        scene.add(bgPlane);

        // Vibrant aurora-colored floor
        var floor = new THREE.Mesh(
          new THREE.PlaneGeometry(30, 30),
          new THREE.MeshStandardMaterial({ color: 0x081427, metalness: 0.86, roughness: 0.22 }),
        );
        floor.rotation.x = -PI / 2;
        floor.position.y = -2.5;
        scene.add(floor);

        scene.add(new THREE.AmbientLight(0x222233, 0.5));
        var kL = new THREE.DirectionalLight(0xffffff, 2.2);
        kL.position.set(-3, 4, 5);
        scene.add(kL);
        var rL = new THREE.PointLight(0x6366f1, 3, 15);
        rL.position.set(4, 2, 3);
        scene.add(rL);
        var wL = new THREE.PointLight(0xffc8a8, 1.5, 12);
        wL.position.set(-3, -1, 4);
        scene.add(wL);

        // ── Live HTML → WebGL Texture  ─
        var capPhoneCvs = document.getElementById("cap-phone");
        var capPhoneCtx = capPhoneCvs.getContext("2d");
        var phoneHtmlEl = document.getElementById("phone-html");

        var capLapCvs = document.getElementById("cap-laptop");
        var capLapCtx = capLapCvs.getContext("2d");
        var lapHtmlEl = document.getElementById("laptop-html");

        var apiOk = (function () {
          var tc = document.createElement("canvas");
          if (!("layoutSubtree" in tc)) return false;
          tc.setAttribute("layoutsubtree", "");
          var ctx = tc.getContext("2d");
          return ctx && typeof ctx.drawElementImage === "function";
        })();
        var paintReady = false;

        function waitForImages() {
          var images = Array.prototype.slice.call(document.querySelectorAll("img"));
          return Promise.all(
            images.map(function (image) {
              if (image.complete) return Promise.resolve();
              return new Promise(function (resolveImage) {
                image.onload = resolveImage;
                image.onerror = resolveImage;
              });
            }),
          );
        }

        function waitForPaint(canvas) {
          return new Promise(function (resolvePaint) {
            if (!canvas || typeof canvas.requestPaint !== "function") {
              resolvePaint();
              return;
            }
            var resolved = false;
            function done() {
              if (resolved) return;
              resolved = true;
              canvas.onpaint = null;
              resolvePaint();
            }
            canvas.onpaint = done;
            canvas.requestPaint();
            setTimeout(done, 100);
          });
        }

        // Generate card HTML for both screens
        var cardData = [
          {
            bg: "linear-gradient(135deg,#1e3a5f,#2563eb)",
            text: "100M+ AI team",
            title: "Notion Showcase",
            meta: "15s · 1920×1080",
          },
          {
            bg: "linear-gradient(135deg,#4a1942,#db2777)",
            text: "Design System v4",
            title: "Dribbble Showcase",
            meta: "12s · 1920×1080",
          },
          {
            bg: "linear-gradient(135deg,#451a03,#f59e0b)",
            text: "$1.9T Market Cap",
            title: "Stripe Showcase",
            meta: "10s · 1920×1080",
          },
          {
            bg: "linear-gradient(135deg,#064e3b,#10b981)",
            text: "Ship faster with AI",
            title: "Vercel Promo",
            meta: "20s · 1080×1920",
          },
          {
            bg: "linear-gradient(135deg,#1e3a5f,#3b82f6)",
            text: "Privacy App",
            title: "iPhone App Store",
            meta: "8s · 1080×1920",
          },
          {
            bg: "linear-gradient(135deg,#4a1942,#a855f7)",
            text: "Audio Visualizer",
            title: "Spotify Remix",
            meta: "15s · 1920×1080",
          },
          {
            bg: "linear-gradient(135deg,#064e3b,#059669)",
            text: "Code Animation",
            title: "Terminal Demo",
            meta: "12s · 1920×1080",
          },
          {
            bg: "linear-gradient(135deg,#451a03,#d97706)",
            text: "Launch Video",
            title: "Product Hunt",
            meta: "30s · 1920×1080",
          },
          {
            bg: "linear-gradient(135deg,#1e3a5f,#6366f1)",
            text: "3D Devices",
            title: "Device Mockups",
            meta: "15s · 1920×1080",
          },
        ];
        function makeCardHTML(cd, isMobile) {
          var fs = isMobile ? 12 : 16;
          return (
            '<div style="background:rgba(255,255,255,0.03);border:1px solid rgba(255,255,255,0.06);border-radius:' +
            (isMobile ? 10 : 12) +
            'px;overflow:hidden;opacity:0" class="anim-card">' +
            '<div style="width:100%;aspect-ratio:16/9;background:' +
            cd.bg +
            ';display:flex;align-items:center;justify-content:center"><span style="font-size:' +
            fs +
            'px;font-weight:700;color:rgba(255,255,255,0.7)">' +
            cd.text +
            "</span></div>" +
            '<div style="padding:' +
            (isMobile ? "8px 10px" : "10px 14px") +
            '"><div style="font-size:' +
            (isMobile ? 11 : 13) +
            'px;font-weight:600;margin-bottom:2px">' +
            cd.title +
            '</div><div style="font-size:' +
            (isMobile ? 9 : 10) +
            'px;color:rgba(255,255,255,0.3)">' +
            cd.meta +
            "</div></div></div>"
          );
        }
        var phoneCardsEl = document.getElementById("phone-cards");
        var laptopCardsEl = document.getElementById("laptop-cards");
        for (var ci = 0; ci < cardData.length; ci++) {
          if (ci < 6) phoneCardsEl.innerHTML += makeCardHTML(cardData[ci], true);
          laptopCardsEl.innerHTML += makeCardHTML(cardData[ci], false);
        }

        var phoneTex = new THREE.CanvasTexture(capPhoneCvs);
        phoneTex.minFilter = THREE.LinearFilter;
        phoneTex.magFilter = THREE.LinearFilter;
        phoneTex.generateMipmaps = false;
        var lapTex = new THREE.CanvasTexture(capLapCvs);
        lapTex.minFilter = THREE.LinearFilter;
        lapTex.magFilter = THREE.LinearFilter;
        lapTex.generateMipmaps = false;
        var glassBuffer = document.createElement("canvas");
        glassBuffer.width = 390;
        glassBuffer.height = 844;
        var glassBufferCtx = glassBuffer.getContext("2d");

        function pathRoundRect(ctx, x, y, w, h, r) {
          var rr = Math.min(r, w / 2, h / 2);
          ctx.beginPath();
          ctx.moveTo(x + rr, y);
          ctx.lineTo(x + w - rr, y);
          ctx.quadraticCurveTo(x + w, y, x + w, y + rr);
          ctx.lineTo(x + w, y + h - rr);
          ctx.quadraticCurveTo(x + w, y + h, x + w - rr, y + h);
          ctx.lineTo(x + rr, y + h);
          ctx.quadraticCurveTo(x, y + h, x, y + h - rr);
          ctx.lineTo(x, y + rr);
          ctx.quadraticCurveTo(x, y, x + rr, y);
          ctx.closePath();
        }

        function drawNotificationCard(ctx, progress, top, iconSelector, title, time, body) {
          var p = Math.max(0, Math.min(1, progress));
          if (p <= 0.001) return;
          var q = notificationEase(p);
          var lift = Math.sin(q * PI);
          var y = 106 + top + (1 - q) * -58 + lift * 6;
          var x = 13;
          var w = 364;
          var h = 66 + lift * 2;
          ctx.save();
          ctx.globalAlpha = q * 0.98;
          ctx.shadowColor = "rgba(0,0,0,0.54)";
          ctx.shadowBlur = 22;
          ctx.shadowOffsetY = 14;
          pathRoundRect(ctx, x, y, w, h, 23);
          ctx.clip();
          ctx.filter = "blur(12px) saturate(1.55) brightness(1.24)";
          ctx.drawImage(glassBuffer, -4, -4, 398, 852);
          ctx.filter = "none";
          pathRoundRect(ctx, x, y, w, h, 23);
          var fill = ctx.createLinearGradient(x, y, x + w, y + h);
          fill.addColorStop(0, "rgba(255,255,255,0.54)");
          fill.addColorStop(0.45, "rgba(188,202,219,0.46)");
          fill.addColorStop(1, "rgba(94,108,130,0.36)");
          ctx.fillStyle = fill;
          ctx.fill();
          ctx.globalCompositeOperation = "screen";
          var caustic = ctx.createLinearGradient(x + 40, y, x + w - 20, y + h);
          caustic.addColorStop(0, "rgba(255,255,255,0.18)");
          caustic.addColorStop(0.36, "rgba(117,230,255,0.15)");
          caustic.addColorStop(0.64, "rgba(255,143,220,0.12)");
          caustic.addColorStop(1, "rgba(255,255,255,0.08)");
          ctx.fillStyle = caustic;
          ctx.fillRect(x, y, w, h);
          ctx.globalCompositeOperation = "source-over";
          ctx.shadowColor = "transparent";
          pathRoundRect(ctx, x + 0.5, y + 0.5, w - 1, h - 1, 22.5);
          ctx.strokeStyle = "rgba(255,255,255,0.72)";
          ctx.lineWidth = 1;
          ctx.stroke();
          ctx.beginPath();
          ctx.moveTo(x + 24, y + 11);
          ctx.bezierCurveTo(x + 105, y - 2, x + 220, y + 7, x + w - 42, y + 12);
          ctx.strokeStyle = "rgba(255,255,255,0.38)";
          ctx.lineWidth = 2;
          ctx.stroke();
          pathRoundRect(ctx, x + 10, y + 14, 38, 38, 11);
          ctx.fillStyle = "rgba(255,255,255,0.58)";
          ctx.fill();
          var icon = document.querySelector(iconSelector);
          if (icon && icon.complete) {
            ctx.save();
            pathRoundRect(ctx, x + 10, y + 14, 38, 38, 11);
            ctx.clip();
            ctx.drawImage(icon, x + 10, y + 14, 38, 38);
            ctx.restore();
          }
          ctx.fillStyle = "#fff";
          ctx.shadowColor = "rgba(0,0,0,0.32)";
          ctx.shadowBlur = 3;
          ctx.shadowOffsetY = 1;
          ctx.font = "800 13px Inter, system-ui, sans-serif";
          ctx.textBaseline = "top";
          ctx.fillText(title, x + 60, y + 13);
          ctx.globalAlpha = q * 0.92;
          ctx.font = "800 12px Inter, system-ui, sans-serif";
          ctx.fillText(time, x + w - ctx.measureText(time).width - 14, y + 13);
          ctx.globalAlpha = q * 0.95;
          ctx.font = "650 12px Inter, system-ui, sans-serif";
          ctx.fillText(body, x + 60, y + 34);
          ctx.restore();
        }

        function drawCanvasNotifications(ctx) {
          glassBufferCtx.clearRect(0, 0, 390, 844);
          glassBufferCtx.drawImage(ctx.canvas, 0, 0);
          drawNotificationCard(
            ctx,
            S.iosN1,
            0,
            "#ios-notify-messages-preload",
            "Messages",
            "now",
            "Liquid Glass preview is ready for review.",
          );
          drawNotificationCard(
            ctx,
            S.iosN2,
            68,
            'img[src="assets/icons/slack.jpg"]',
            "HyperFrames",
            "1m",
            "One-worker render is ready.",
          );
          drawNotificationCard(
            ctx,
            S.iosN3,
            136,
            'img[src="assets/icons/gmail.jpg"]',
            "Gmail",
            "2m",
            "Preview videos are live on CDN.",
          );
        }

        function captureScreens() {
          if (!apiOk || !paintReady) return;
          try {
            capPhoneCtx.clearRect(0, 0, 390, 844);
            capPhoneCtx.drawElementImage(phoneHtmlEl, 0, 0, 390, 844);
            drawCanvasNotifications(capPhoneCtx);
            capLapCtx.clearRect(0, 0, 1440, 900);
            capLapCtx.drawElementImage(lapHtmlEl, 0, 0, 1440, 900);
            phoneTex.needsUpdate = true;
            lapTex.needsUpdate = true;
          } catch (e) {}
        }

        // ── Turntable Group (both devices rotate together for 360° orbit) ──
        var turntable = new THREE.Group();
        scene.add(turntable);

        // ── Model Groups ───────────────────────────────────────────────────
        var phoneGrp = new THREE.Group();
        turntable.add(phoneGrp);
        var lapGrp = new THREE.Group();
        turntable.add(lapGrp);
        var ready = 0;

        var draco = new THREE.DRACOLoader();
        draco.setDecoderPath("https://www.gstatic.com/draco/versioned/decoders/1.5.6/");
        var loader = new THREE.GLTFLoader();
        loader.setDRACOLoader(draco);

        // iPhone
        // The replacement device models carry UVs authored for a tiling material
        // (the laptop panel runs u -6.3..6.3), so mapping one screen image onto
        // them clamps at the edges and smears the last row of pixels across the
        // whole panel. Both screens are flat planes, so derive planar UVs from
        // the mesh's own bounds instead of trusting what shipped.
        function planarScreenUv(mesh) {
          var g = mesh.geometry;
          g.computeBoundingBox();
          var bb = g.boundingBox;
          var pos = g.attributes.position;
          var w = bb.max.x - bb.min.x || 1;
          var h = bb.max.y - bb.min.y || 1;
          var uv = new Float32Array(pos.count * 2);
          for (var i = 0; i < pos.count; i++) {
            uv[i * 2] = (pos.getX(i) - bb.min.x) / w;
            uv[i * 2 + 1] = (pos.getY(i) - bb.min.y) / h;
          }
          g.setAttribute("uv", new THREE.BufferAttribute(uv, 2));
        }

        // These compositions were authored around a model whose screen faced -Z
        // (the old asset's display plane sat at its minimum Z) and every camera
        // move and spin in the timeline assumes it. The replacement faces +Z, so
        // align the model rather than re-time the animation. Reads the model's
        // own parts, so a re-export facing the other way still lands right.
        function alignScreenToMinusZ(root, frontName, backName) {
          var front = root.getObjectByName(frontName);
          var back = root.getObjectByName(backName);
          if (!front || !back) return;
          // matrixWorld is only refreshed during a render, and this runs at load,
          // so without this the two parts both read as the origin and the test
          // silently never fires.
          root.updateWorldMatrix(true, true);
          var fp = new THREE.Vector3();
          var bp = new THREE.Vector3();
          front.getWorldPosition(fp);
          back.getWorldPosition(bp);
          if (fp.z > bp.z) root.rotation.y += Math.PI;
        }

        loader.load("models/iphone.glb", function (gltf) {
          var m = gltf.scene;
          var box = new THREE.Box3().setFromObject(m);
          var sz = box.getSize(new THREE.Vector3());
          var s = 3.2 / sz.y;
          m.scale.setScalar(s);
          box.setFromObject(m);
          m.position.sub(box.getCenter(new THREE.Vector3()));
          m.traverse(function (child) {
            if (!child.isMesh) return;
            // `front-glass` is the display face; Blender's exporter suffixes the
            // mesh name and leaves the node name bare, so match the prefix.
            if (child.name.indexOf("front-glass") === 0) {
              planarScreenUv(child);
              child.material = new THREE.MeshBasicMaterial({ map: phoneTex });
            }
          });
          alignScreenToMinusZ(m, "front-glass", "rear-window");
          phoneGrp.add(m);
          ready++;
          if (ready === 1) onReady();
        });

        function onReady() {
          waitForImages()
            .then(function () {
              return waitForPaint(capPhoneCvs);
            })
            .then(function () {
              paintReady = true;
              captureScreens();
              render();
            });
        }

        // ── Cubic Bezier Implementation ────────────────────────────────────
        function cbz(p1x, p1y, p2x, p2y) {
          var ax = 3 * p1x - 3 * p2x + 1,
            bx = 3 * p2x - 6 * p1x,
            cx = 3 * p1x;
          var ay = 3 * p1y - 3 * p2y + 1,
            by = 3 * p2y - 6 * p1y,
            cy = 3 * p1y;
          return function (t) {
            var s = t;
            for (var i = 0; i < 12; i++) {
              var x = ((ax * s + bx) * s + cx) * s - t;
              var dx = (3 * ax * s + 2 * bx) * s + cx;
              if (Math.abs(dx) < 1e-10) break;
              s -= x / dx;
            }
            s = Math.max(0, Math.min(1, s));
            return ((ay * s + by) * s + cy) * s;
          };
        }

        var enterEase = cbz(0.16, 1.0, 0.3, 1.0);
        var notificationEase = cbz(0.22, 0.0, 0.16, 1.0);
        var spinEase = cbz(0.76, 0.0, 0.18, 1.0);
        var driftEase = cbz(0.45, 0.0, 0.15, 1.0);
        var snapEase = cbz(0.22, 1.15, 0.36, 1.0);
        var breatheEase = cbz(0.37, 0.0, 0.63, 1.0);

        // ── Animation State ────────────────────────────────────────────────
        var S = {
          phX: 0,
          phY: 0.1,
          phZ: 0.5,
          phRX: 0,
          phRY: PI,
          phRZ: 0,
          lpX: 10,
          lpY: -0.2,
          lpZ: -0.6,
          lpRX: 0,
          lpRY: 0,
          lpRZ: 0,
          cX: 0,
          cY: 0.18,
          cZ: 6.15,
          clX: 0,
          clY: 0.1,
          clZ: 0,
          drift: 0,
          turn: 0,
          cardReveal: 0,
          titleFade: 0,
          counter: 0,
          iosN1: 0,
          iosN2: 0,
          iosN3: 0,
          p: 0,
        };

        function render() {
          var d = S.drift;

          // ── Animate live HTML content (captured to texture every frame) ──
          // Title fade-in
          document.getElementById("phone-title").style.opacity = S.titleFade;
          document.getElementById("laptop-title").style.opacity = S.titleFade;
          // Counter
          var cnt = Math.round(S.counter);
          document.getElementById("phone-count").textContent = "Catalog " + cnt;
          document.getElementById("laptop-count").textContent = "Catalog " + cnt;
          // Staggered card reveal
          var allCards = document.querySelectorAll(".anim-card");
          for (var ci2 = 0; ci2 < allCards.length; ci2++) {
            var cardProgress = Math.max(0, Math.min(1, (S.cardReveal * allCards.length - ci2) * 1.5));
            allCards[ci2].style.opacity = cardProgress;
            allCards[ci2].style.transform = "translateY(" + (1 - cardProgress) * 15 + "px)";
          }
          function dropNotification(id, progress) {
            var el = document.getElementById(id);
            if (!el) return;
            var p = Math.max(0, Math.min(1, progress));
            var q = notificationEase(p);
            var overshoot = Math.sin(q * PI) * 5;
            el.style.opacity = q;
            el.style.transform =
              "translateY(" + ((1 - q) * -125 + overshoot) + "px) scale(" + (0.98 + q * 0.02) + ")";
          }
          dropNotification("ios-notification-a", S.iosN1);
          dropNotification("ios-notification-b", S.iosN2);
          dropNotification("ios-notification-c", S.iosN3);
          // Re-capture HTML to texture
          captureScreens();

          phoneGrp.position.set(
            S.phX + Math.sin(d * 0.5) * 0.015,
            S.phY + Math.sin(d * 0.7) * 0.008,
            S.phZ + Math.cos(d * 0.4) * 0.008,
          );
          phoneGrp.rotation.set(S.phRX, S.phRY, S.phRZ);
          lapGrp.position.set(
            S.lpX - Math.sin(d * 0.4) * 0.012,
            S.lpY + Math.sin(d * 0.6) * 0.006,
            S.lpZ + Math.cos(d * 0.35) * 0.008,
          );
          lapGrp.rotation.set(S.lpRX, S.lpRY, S.lpRZ);
          turntable.rotation.y = S.turn;
          bgUniforms.uTime.value = d;
          camera.position.set(S.cX, S.cY, S.cZ);
          camera.lookAt(S.clX, S.clY, S.clZ);
          renderer.render(scene, camera);
        }

        // ── GSAP Timeline — Product Review Edit ────────────────────────────
        var tl = gsap.timeline({ paused: true });
        window.__timelines = window.__timelines || {};
        window.__timelines["ios26-liquid-glass"] = tl;

        tl.to(S, { p: 1, drift: 15, duration: 15, ease: "none" }, 0);

        // ── iOS 26 Showcase: full-device hero with a subtle drift-in ──
        tl.to(S, { cZ: 6.45, duration: 12, ease: driftEase }, 0.5);
        tl.to(S, { cY: 0.16, duration: 12, ease: driftEase }, 0.5);

        // Very gentle tilt — shows the glass icon refraction from slightly different angles
        tl.to(S, { phRY: PI + 0.04, phRX: 0.01, duration: 4, ease: breatheEase }, 0.5);
        tl.to(S, { phRY: PI - 0.03, phRX: -0.008, duration: 4, ease: breatheEase }, 4.5);
        tl.to(S, { phRY: PI + 0.02, phRX: 0.005, duration: 4, ease: breatheEase }, 8.5);

        // No scroll — keep the home screen static
        // No 360° turntable — just the gentle zoom out

        // Move MacBook off screen so it doesn't appear
        tl.set(S, { lpX: 10 }, 0);
        tl.to(S, { iosN1: 1, duration: 0.72, ease: "none" }, 0.85);
        tl.to(S, { iosN2: 1, duration: 0.74, ease: "none" }, 1.7);
        tl.to(S, { iosN3: 1, duration: 0.76, ease: "none" }, 2.55);
        tl.to({}, { duration: 15, ease: "none", onUpdate: render }, 0);
      </script>
    </body>
  </html>
  ```
</Accordion>

## Usage

After installing, add the block to your host composition:

```html theme={null}
<div data-composition-id="ios26-liquid-glass" data-composition-src="compositions/ios26-liquid-glass.html" data-start="0" data-duration="15" data-track-index="1" data-width="1920" data-height="1080"></div>
```

Tagged `liquid-glass-html-in-canvas` `webgpu` `3d` `iphone` `ios26` `notifications` `gltf` `html-in-canvas`.

## Related topics

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