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

# AI Chat Reveal

> A question is typed on a rising keyboard, sent, and answered by an AI that streams its reply word by word on a measured token rhythm - thinking dot, uneven bursts, bulleted list - before the app cuts to a branded closing card

# AI Chat Reveal

A question is typed on a rising keyboard, sent, and answered by an AI that streams its reply word by word on a measured token rhythm - thinking dot, uneven bursts, bulleted list - before the app cuts to a branded closing card

**AI Chat Reveal**

```html ai-chat-reveal.html
<!doctype html>
<html
  lang="en"
  data-composition-variables='[
    { "id": "botName", "type": "string", "role": "content", "label": "Assistant name", "description": "Name of the assistant answering in this mock chat. It belongs to the host product, never to the brand this video promotes.", "portrays": ["host_identity"], "default": "Assistant" },
    { "id": "userMessage", "type": "string", "role": "content", "label": "User message", "description": "The question typed on the keyboard and sent. Around 40 characters keeps the measured pacing.", "default": "How do I turn my HTML into real video?" },
    { "id": "answer1", "type": "string", "role": "content", "label": "Answer paragraph 1", "description": "First streamed paragraph. It names the brand this video promotes and says what that brand does.", "default": "You do not need an editor. HyperFrames renders the HTML you already write into deterministic, pixel-perfect video." },
    { "id": "answer2", "type": "string", "role": "content", "label": "Answer paragraph 2", "description": "Short second beat.", "default": "It is markup, not magic." },
    { "id": "answer3", "type": "string", "role": "content", "label": "Answer paragraph 3", "description": "Lead-in to the list.", "default": "What you get out of the box:" },
    { "id": "bullet1", "type": "string", "role": "content", "label": "Bullet 1", "description": "First list item.", "default": "A catalog of motion primitives you install and own" },
    { "id": "bullet2", "type": "string", "role": "content", "label": "Bullet 2", "description": "Second list item.", "default": "GSAP timelines that seek to any frame" },
    { "id": "bullet3", "type": "string", "role": "content", "label": "Bullet 3", "description": "Third list item.", "default": "4K renders from a single CLI command" },
    { "id": "ecHeadline", "type": "string", "role": "content", "label": "End-card headline", "description": "Closing card headline. A | breaks the line.", "default": "It&#39;s not magic.|It&#39;s HTML." },
    { "id": "ecSub", "type": "string", "role": "content", "label": "End-card subline", "description": "Positioning sentence of the brand this video promotes, on the closing card.", "portrays": ["subject_tagline"], "default": "Open-source video rendering — write HTML, ship pixel-perfect video." },
    { "id": "ecCta", "type": "string", "role": "content", "label": "End-card CTA", "description": "Closing-card button label, which names the brand this video promotes. Use the real name of that brand.", "portrays": ["subject_name"], "default": "Try HyperFrames" },
    { "id": "ecFooter", "type": "string", "role": "content", "label": "End-card footer", "description": "Domain of the brand this video promotes, letter-spaced at the very bottom. Use the real domain.", "portrays": ["subject_domain"], "default": "HYPERFRAMES.HEYGEN.COM" },
    { "id": "brandLogo", "type": "image", "role": "content", "label": "Brand logo", "description": "Transparent mark of the brand this video promotes, in the closing-card header.", "portrays": ["subject_logo"], "default": "assets/hyperframes-logo-white.svg" }
  ]'
>
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=1080, height=1920" />
    <title>AI Chat Reveal</title>
    <script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
    <style>
      * {
        margin: 0;
        padding: 0;
        box-sizing: border-box;
      }
      html,
      body {
        width: 1080px;
        height: 1920px;
        overflow: hidden;
        background: #000;
      }
      body {
        font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", Arial, sans-serif;
      }
      /* The measured 750x1624 device UI is scaled uniformly into a standard
         1080x1920 portrait canvas so its keyboard geometry stays pixel-true. */
      #acr-root {
        position: relative;
        width: 1080px;
        height: 1920px;
        overflow: hidden;
      }
      .clip {
        position: absolute;
        inset: 0;
        overflow: hidden;
      }
      #acr-chat {
        background: #fdfdfd;
      }
      .acr-stage {
        position: absolute;
        left: 96.6502px;
        top: 0;
        width: 750px;
        height: 1624px;
        transform: scale(1.18226601);
        transform-origin: top left;
      }
      #acr-chat-bg {
        position: absolute;
        inset: 0;
        background: #fdfdfd;
      }

      /* status bar */
      #acr-statusbar {
        position: absolute;
        top: 14px;
        left: 0;
        right: 0;
        height: 44px;
      }
      #acr-clock {
        position: absolute;
        left: 39px;
        top: 9px;
        font-size: 22px;
        font-weight: 600;
        letter-spacing: 2.4px;
        font-variant-numeric: tabular-nums;
        color: #0d0d0d;
      }
      #acr-sicons {
        position: absolute;
        right: 26px;
        top: 8px;
        display: flex;
        gap: 9px;
        align-items: center;
      }

      /* header */
      #acr-header {
        position: absolute;
        top: 64px;
        left: 0;
        right: 0;
        height: 52px;
      }
      #acr-hleft {
        position: absolute;
        left: 28px;
        top: 12px;
      }
      #acr-htitle {
        position: absolute;
        left: 0;
        right: 48px;
        text-align: center;
        top: 13px;
        font-size: 22px;
        font-weight: 600;
        color: #0d0d0d;
      }
      .acr-hicons {
        position: absolute;
        right: 28px;
        top: 14px;
        display: flex;
        gap: 26px;
        align-items: center;
      }
      #acr-hicons-b {
        visibility: hidden;
        opacity: 0;
      }

      /* user bubble */
      #acr-bubble {
        position: absolute;
        top: 129px;
        right: 32px;
        max-width: 544px;
        background: #f3f3f3;
        border-radius: 34px;
        padding: 20px 30px;
        font-size: 27px;
        line-height: 38.6px;
        color: #111;
        letter-spacing: 1.25px;
        visibility: hidden;
        opacity: 0;
      }

      /* answer */
      #acr-answer {
        position: absolute;
        top: 267px;
        left: 33px;
        width: 662px;
        font-size: 27px;
        line-height: 38.6px;
        color: #141414;
        letter-spacing: 1.25px;
      }
      #acr-answer .acr-ap {
        margin: 0 0 15.5px 0;
      }
      #acr-answer .acr-ali {
        display: flex;
        margin: 0 0 6.5px 0;
      }
      #acr-answer .acr-mark {
        width: 30px;
        flex: 0 0 30px;
        text-align: center;
        font-size: 18px;
        line-height: 38.6px;
      }
      #acr-answer .acr-alitext {
        flex: 1;
      }
      .acr-w {
        opacity: 0;
      }
      #acr-dot {
        position: absolute;
        left: 32px;
        top: 279px;
        width: 15px;
        height: 15px;
        border-radius: 50%;
        background: #646464;
        visibility: hidden;
        opacity: 0;
      }

      /* composer (rest dock; rides keyboard via transform) */
      #acr-composer {
        position: absolute;
        left: 30px;
        right: 30px;
        bottom: 31px;
      }
      #acr-pill {
        position: relative;
        width: 100%;
        min-height: 81px;
        background: #fff;
        border: 2px solid #ececec;
        border-radius: 41px;
        box-shadow: 0 4px 14px rgba(0, 0, 0, 0.04);
        display: flex;
        align-items: center;
        padding: 13px 160px 13px 78px;
      }
      #acr-plusi {
        position: absolute;
        left: 30px;
        top: 50%;
        transform: translateY(-50%);
      }
      #acr-ph {
        position: absolute;
        left: 78px;
        top: 50%;
        transform: translateY(-50%);
        color: #767676;
        font-size: 28px;
        letter-spacing: 1.25px;
      }
      #acr-ctextwrap {
        width: 100%;
        min-height: 38px;
      }
      #acr-ctext {
        font-size: 29px;
        line-height: 38px;
        color: #0d0d0d;
        letter-spacing: 1.25px;
        visibility: hidden;
        opacity: 0;
      }
      #acr-caret {
        display: inline-block;
        width: 3px;
        height: 30px;
        margin-left: 2px;
        border-radius: 1.5px;
        background: #0d0d0d;
        vertical-align: -4px;
        visibility: hidden;
        opacity: 0;
      }
      #acr-cmic {
        position: absolute;
        right: 80px;
        top: 50%;
        transform: translateY(-50%);
      }
      .acr-cbtn {
        position: absolute;
        right: 14px;
        top: 50%;
        transform: translateY(-50%);
        width: 52px;
        height: 52px;
        border-radius: 50%;
        display: grid;
        place-items: center;
      }
      #acr-btn-send-grey {
        background: #ececec;
      }
      #acr-btn-send-black {
        background: #0d0d0d;
        visibility: hidden;
        opacity: 0;
      }
      #acr-btn-stop {
        background: #0d0d0d;
        visibility: hidden;
        opacity: 0;
      }
      #acr-btn-stop .acr-sq {
        width: 19px;
        height: 19px;
        border-radius: 5px;
        background: #fff;
      }

      /* keyboard: geometry and #d2d5e0 ground measured from the reference */
      #acr-keyboard {
        position: absolute;
        left: 0;
        bottom: 0;
        width: 750px;
        height: 482px;
        background: #d2d5e0;
      }
      #acr-suggest {
        position: absolute;
        top: 0;
        left: 0;
        right: 0;
        height: 74px;
        display: flex;
      }
      #acr-suggest .acr-sug {
        flex: 1;
        display: grid;
        place-items: center;
        font-size: 29px;
        color: #0d0d0d;
        position: relative;
      }
      #acr-suggest .acr-sug + .acr-sug::before {
        content: "";
        position: absolute;
        left: 0;
        top: 16px;
        bottom: 16px;
        width: 1.5px;
        background: #bcbabf;
      }
      .acr-key {
        position: absolute;
        height: 74px;
        background: #fdfdfd;
        border-radius: 10px;
        box-shadow: 0 2px 0 #8b8a8f;
        display: grid;
        place-items: center;
        font-size: 32px;
        color: #000;
      }
      .acr-gkey {
        background: #aeacaf;
      }
      .acr-num,
      .acr-spc {
        font-size: 27px;
      }
      .acr-kbstrip {
        position: absolute;
        bottom: 10px;
        left: 0;
        right: 0;
        height: 52px;
      }
      .acr-kglobe {
        position: absolute;
        left: 48px;
        top: 21px;
      }
      .acr-kmic {
        position: absolute;
        right: 52px;
        top: 23px;
      }

      /* end card: the HyperFrames closing card, on the family's dark ground */
      #acr-endcard-inner {
        position: absolute;
        inset: 0;
        background: linear-gradient(180deg, #14110e 0%, #221b13 100%);
        opacity: 0;
      }
      #acr-eclogo {
        position: absolute;
        left: 255px;
        top: 112px;
        width: 240px;
        height: 72px;
      }
      #acr-ecstar {
        position: absolute;
        left: 610px;
        top: 230px;
      }
      #acr-ecvideo {
        position: absolute;
        left: 222px;
        top: 336px;
        width: 283px;
        height: 454px;
        border-radius: 36px;
        background: linear-gradient(165deg, #0f3a2c 0%, #0a9c6b 78%, #3ce6ac 100%);
        box-shadow: 0 24px 60px rgba(0, 0, 0, 0.45);
      }
      #acr-ecvideo .acr-play {
        position: absolute;
        left: 50%;
        top: 50%;
        transform: translate(-50%, -50%);
        width: 108px;
        height: 108px;
        border-radius: 50%;
        background: rgba(253, 253, 253, 0.94);
        display: grid;
        place-items: center;
      }
      #acr-echead {
        position: absolute;
        top: 946px;
        left: 0;
        right: 0;
        text-align: center;
        font-weight: 700;
        font-size: 57px;
        line-height: 58px;
        color: #f7ece5;
        letter-spacing: 0.5px;
      }
      #acr-ecsub {
        position: absolute;
        top: 1084px;
        left: 65px;
        right: 65px;
        text-align: center;
        font-weight: 500;
        font-size: 31px;
        line-height: 40px;
        color: #cfc4b8;
      }
      #acr-eccta {
        position: absolute;
        left: 160px;
        top: 1406px;
        width: 429px;
        height: 87px;
        background: #3ce6ac;
        border-radius: 44px;
        display: flex;
        align-items: center;
        justify-content: center;
        gap: 22px;
      }
      #acr-eccta .acr-lbl {
        color: #0d0d0d;
        font-weight: 700;
        font-size: 33px;
        letter-spacing: 0.3px;
      }
      #acr-ecfoot {
        position: absolute;
        top: 1536px;
        left: 0;
        right: 0;
        text-align: center;
        font-weight: 700;
        font-size: 25px;
        letter-spacing: 4px;
        color: #3ce6ac;
      }
    </style>
  </head>
  <body>
    <div
      id="acr-root"
      data-composition-id="ai-chat-reveal"
      data-start="0"
      data-width="1080"
      data-height="1920"
      data-duration="19.3333"
    >
      <section id="acr-chat" class="clip" data-start="0" data-duration="16.9" data-track-index="1">
        <div class="acr-stage">
          <div id="acr-chat-bg"></div>

          <div id="acr-statusbar">
            <div id="acr-clock">11:42</div>
            <div id="acr-sicons">
              <svg width="30" height="20" viewBox="0 0 20 12" fill="#0d0d0d">
                <rect x="0" y="7" width="3" height="4" rx="1" />
                <rect x="5" y="5" width="3" height="6" rx="1" />
                <rect x="10" y="2.6" width="3" height="8.4" rx="1" />
                <rect x="15" y="0.5" width="3" height="10.5" rx="1" /></svg
              ><svg
                width="30"
                height="21"
                viewBox="0 0 22 15"
                fill="none"
                stroke="#0d0d0d"
                stroke-width="2.2"
                stroke-linecap="round"
              >
                <path d="M2 5 C7 -0.5 15 -0.5 20 5" fill="none" />
                <path d="M5.5 8.6 C9 5 13 5 16.5 8.6" />
                <path d="M9.2 12 C10.4 11 11.6 11 12.8 12" /></svg
              ><svg width="46" height="22" viewBox="0 0 30 14" fill="none">
                <rect
                  x="0.8"
                  y="0.8"
                  width="25"
                  height="12.4"
                  rx="3.6"
                  stroke="#0d0d0d"
                  stroke-opacity="0.32"
                  stroke-width="1.2"
                />
                <rect x="2.4" y="2.4" width="22.4" height="9.2" rx="2.4" fill="#0d0d0d" />
                <path d="M28 4.6 v4.8 a2.4 2.4 0 0 0 0 -4.8" fill="#0d0d0d" fill-opacity="0.45" />
              </svg>
            </div>
          </div>

          <div id="acr-header">
            <div id="acr-hleft">
              <svg
                width="29"
                height="29"
                viewBox="0 0 24 24"
                fill="none"
                stroke="#0d0d0d"
                stroke-width="1.9"
                stroke-linecap="round"
              >
                <path d="M4 8 H20 M4 15 H12" />
              </svg>
            </div>
            <div id="acr-htitle">Assistant</div>
            <div class="acr-hicons" id="acr-hicons-a">
              <svg
                width="28"
                height="28"
                viewBox="0 0 26 24"
                fill="none"
                stroke="#0d0d0d"
                stroke-width="1.7"
                stroke-linecap="round"
              >
                <circle cx="10" cy="8" r="4" />
                <path d="M3 21 C3 16 6.5 14 10 14 C13.5 14 17 16 17 21" />
                <path d="M20 6 V12 M17 9 H23" /></svg
              ><svg
                width="26"
                height="26"
                viewBox="0 0 24 24"
                fill="none"
                stroke="#0d0d0d"
                stroke-width="1.7"
                stroke-linecap="round"
                stroke-dasharray="2.4 3.4"
              >
                <circle cx="12" cy="12" r="9" />
              </svg>
            </div>
            <div class="acr-hicons" id="acr-hicons-b">
              <svg
                width="23"
                height="23"
                viewBox="0 0 24 24"
                fill="none"
                stroke="#0d0d0d"
                stroke-width="1.7"
                stroke-linecap="round"
                stroke-linejoin="round"
              >
                <path
                  d="M4 20 L5 15.5 L16.5 4 C17.6 2.9 19.4 2.9 20.5 4 C21.6 5.1 21.6 6.9 20.5 8 L9 19.5 L4 20 Z"
                /></svg
              ><svg width="34" height="34" viewBox="0 0 24 24" fill="#0d0d0d">
                <circle cx="5" cy="12" r="1.7" />
                <circle cx="12" cy="12" r="1.7" />
                <circle cx="19" cy="12" r="1.7" />
              </svg>
            </div>
          </div>

          <div id="acr-bubble">How do I turn my HTML into real video?</div>

          <div id="acr-dot"></div>
          <div id="acr-answer" data-layout-allow-occlusion></div>

          <div id="acr-composer">
            <div id="acr-pill">
              <div id="acr-plusi">
                <svg
                  width="22"
                  height="22"
                  viewBox="0 0 24 24"
                  fill="none"
                  stroke="#0d0d0d"
                  stroke-width="1.9"
                  stroke-linecap="round"
                >
                  <path d="M12 5 V19 M5 12 H19" />
                </svg>
              </div>
              <div id="acr-ph" data-layout-allow-overlap data-layout-allow-occlusion>
                Ask anything
              </div>
              <div id="acr-ctextwrap"><span id="acr-ctext"></span><span id="acr-caret"></span></div>
              <div id="acr-cmic">
                <svg
                  width="15"
                  height="22"
                  viewBox="0 0 24 34"
                  fill="none"
                  stroke="#0d0d0d"
                  stroke-width="2"
                  stroke-linecap="round"
                >
                  <rect x="8" y="2" width="8" height="16" rx="4" fill="#0d0d0d" stroke="none" />
                  <path d="M4 14 a8 8 0 0 0 16 0 M12 22 V28 M7 31 H17" stroke-width="2.2" />
                </svg>
              </div>
              <div class="acr-cbtn" id="acr-btn-send-grey">
                <svg
                  width="26"
                  height="26"
                  viewBox="0 0 24 24"
                  fill="none"
                  stroke="#b6b6b6"
                  stroke-width="2.6"
                  stroke-linecap="round"
                  stroke-linejoin="round"
                >
                  <path d="M12 19 V6 M6.5 11 L12 5.5 L17.5 11" />
                </svg>
              </div>
              <div class="acr-cbtn" id="acr-btn-send-black">
                <svg
                  width="26"
                  height="26"
                  viewBox="0 0 24 24"
                  fill="none"
                  stroke="#fff"
                  stroke-width="2.6"
                  stroke-linecap="round"
                  stroke-linejoin="round"
                >
                  <path d="M12 19 V6 M6.5 11 L12 5.5 L17.5 11" />
                </svg>
              </div>
              <div class="acr-cbtn" id="acr-btn-stop"><div class="acr-sq"></div></div>
            </div>
          </div>

          <div id="acr-keyboard">
            <div id="acr-suggest">
              <div class="acr-sug">How</div>
              <div class="acr-sug">Can</div>
              <div class="acr-sug">My</div>
            </div>
            <div id="acr-keys"></div>
            <div class="acr-kbstrip">
              <span class="acr-kglobe"
                ><svg
                  width="30"
                  height="30"
                  viewBox="0 0 24 24"
                  fill="none"
                  stroke="#000"
                  stroke-width="2.6"
                >
                  <circle cx="12" cy="12" r="9.6" />
                  <path
                    d="M2.4 12 H21.6 M12 2.4 C7.8 6.8 7.8 17.2 12 21.6 C16.2 17.2 16.2 6.8 12 2.4 M4 7 H20 M4 17 H20"
                    stroke-width="2.2"
                  /></svg></span
              ><span class="acr-kmic"
                ><svg
                  width="16"
                  height="25"
                  viewBox="0 0 24 34"
                  preserveAspectRatio="none"
                  fill="none"
                  stroke="#0d0d0d"
                  stroke-width="3.9"
                  stroke-linecap="round"
                >
                  <rect
                    x="5.5"
                    y="1"
                    width="13"
                    height="17.5"
                    rx="6.5"
                    fill="#0d0d0d"
                    stroke="none"
                  />
                  <path d="M2.5 14 a9.5 9.5 0 0 0 19 0 M12 23.5 V28.5 M5.5 32 H18.5" /></svg
              ></span>
            </div>
          </div>
        </div>
      </section>

      <section
        id="acr-endcard"
        class="clip"
        data-start="15.8"
        data-duration="3.5333"
        data-track-index="2"
      >
        <div id="acr-endcard-inner">
          <div class="acr-stage">
            <img
              id="acr-eclogo"
              data-var-src="brandLogo"
              src="assets/hyperframes-logo-white.svg"
              alt="Brand logo"
            />
            <svg id="acr-ecstar" width="80" height="80" viewBox="0 0 80 80">
              <g stroke="#2f6b57" stroke-width="8.5" stroke-linecap="round">
                <line x1="47" y1="40" x2="77" y2="40" />
                <line x1="46.5" y1="42.7" x2="74.2" y2="54.2" />
                <line x1="44.9" y1="44.9" x2="66.2" y2="66.2" />
                <line x1="42.7" y1="46.5" x2="54.2" y2="74.2" />
                <line x1="40" y1="47" x2="40" y2="77" />
                <line x1="37.3" y1="46.5" x2="25.8" y2="74.2" />
                <line x1="35.1" y1="44.9" x2="13.8" y2="66.2" />
                <line x1="33.5" y1="42.7" x2="5.8" y2="54.2" />
                <line x1="33" y1="40" x2="3" y2="40" />
                <line x1="33.5" y1="37.3" x2="5.8" y2="25.8" />
                <line x1="35.1" y1="35.1" x2="13.8" y2="13.8" />
                <line x1="37.3" y1="33.5" x2="25.8" y2="5.8" />
                <line x1="40" y1="33" x2="40" y2="3" />
                <line x1="42.7" y1="33.5" x2="54.2" y2="5.8" />
                <line x1="44.9" y1="35.1" x2="66.2" y2="13.8" />
                <line x1="46.5" y1="37.3" x2="74.2" y2="25.8" />
              </g>
            </svg>
            <div id="acr-ecvideo">
              <div class="acr-play">
                <svg width="40" height="46" viewBox="0 0 44 48" fill="none">
                  <path
                    d="M40.5 20.6 C43.2 22.1 43.2 25.9 40.5 27.4 L6.2 46.6 C3.5 48.1 0.2 46.2 0.2 43.2 L0.2 4.8 C0.2 1.8 3.5 -0.1 6.2 1.4 Z"
                    fill="#0d0d0d"
                  />
                </svg>
              </div>
            </div>
            <div id="acr-echead">It's not magic.<br />It's HTML.</div>
            <div id="acr-ecsub">
              Open-source video rendering — write HTML, ship pixel-perfect video.
            </div>
            <div id="acr-eccta">
              <span class="acr-lbl">Try HyperFrames</span
              ><svg
                width="30"
                height="34"
                viewBox="0 0 16 24"
                fill="none"
                stroke="#0d0d0d"
                stroke-width="2.6"
                stroke-linecap="round"
                stroke-linejoin="round"
              >
                <path d="M4 3 L13 12 L4 21" />
              </svg>
            </div>
            <div id="acr-ecfoot">HYPERFRAMES.HEYGEN.COM</div>
          </div>
        </div>
      </section>
    </div>

    <script>
      (function () {
        "use strict";

        /* -------- variables -------- */
        var vars =
          window.__hyperframes && typeof window.__hyperframes.getVariables === "function"
            ? window.__hyperframes.getVariables()
            : {};

        function text(value, fallback, max) {
          if (typeof value !== "string") return fallback;
          var t = value.trim();
          if (!t) return fallback;
          return t.length > max ? t.slice(0, max) : t;
        }

        var botName = text(vars.botName, "Assistant", 24);
        var userMessage = text(vars.userMessage, "How do I turn my HTML into real video?", 60);
        var answer1 = text(
          vars.answer1,
          "You do not need an editor. HyperFrames renders the HTML you already write into deterministic, pixel-perfect video.",
          220,
        );
        var answer2 = text(vars.answer2, "It is markup, not magic.", 120);
        var answer3 = text(vars.answer3, "What you get out of the box:", 120);
        var bullets = [
          text(vars.bullet1, "A catalog of motion primitives you install and own", 90),
          text(vars.bullet2, "GSAP timelines that seek to any frame", 90),
          text(vars.bullet3, "4K renders from a single CLI command", 90),
        ];

        document.getElementById("acr-htitle").textContent = botName;
        document.getElementById("acr-bubble").textContent = userMessage;
        document.getElementById("acr-echead").innerHTML = "";
        text(vars.ecHeadline, "It's not magic.|It's HTML.", 60)
          .split("|")
          .forEach(function (line, i) {
            var head = document.getElementById("acr-echead");
            if (i > 0) head.appendChild(document.createElement("br"));
            head.appendChild(document.createTextNode(line));
          });
        document.getElementById("acr-ecsub").textContent = text(
          vars.ecSub,
          "Open-source video rendering — write HTML, ship pixel-perfect video.",
          140,
        );
        document.querySelector("#acr-eccta .acr-lbl").textContent = text(
          vars.ecCta,
          "Try HyperFrames",
          30,
        );
        document.getElementById("acr-ecfoot").textContent = text(
          vars.ecFooter,
          "HYPERFRAMES.HEYGEN.COM",
          40,
        );

        /* -------- build the answer DOM: paragraphs of word spans, then bullets -------- */
        var answerEl = document.getElementById("acr-answer");
        var wordEls = [];
        function addPara(str) {
          var p = document.createElement("p");
          p.className = "acr-ap";
          str.split(/\s+/).forEach(function (word, i) {
            if (i > 0) p.appendChild(document.createTextNode(" "));
            var s = document.createElement("span");
            s.className = "acr-w";
            s.textContent = word;
            p.appendChild(s);
            wordEls.push(s);
          });
          answerEl.appendChild(p);
        }
        addPara(answer1);
        addPara(answer2);
        addPara(answer3);
        bullets.forEach(function (b) {
          var row = document.createElement("div");
          row.className = "acr-ali";
          var mark = document.createElement("span");
          mark.className = "acr-mark acr-w";
          mark.textContent = "•";
          row.appendChild(mark);
          wordEls.push(mark);
          var body = document.createElement("div");
          body.className = "acr-alitext";
          b.split(/\s+/).forEach(function (word, i) {
            if (i > 0) body.appendChild(document.createTextNode(" "));
            var s = document.createElement("span");
            s.className = "acr-w";
            s.textContent = word;
            body.appendChild(s);
            wordEls.push(s);
          });
          row.appendChild(body);
          answerEl.appendChild(row);
        });

        /* -------- build the keyboard: measured key geometry -------- */
        var keysEl = document.getElementById("acr-keys");
        var ROWS = [
          { top: 83, left: 13, letters: "qwertyuiop" },
          { top: 173, left: 48, letters: "asdfghjkl" },
          { top: 263, left: 123, letters: "zxcvbnm" },
        ];
        ROWS.forEach(function (row) {
          row.letters.split("").forEach(function (ch, i) {
            var k = document.createElement("div");
            k.className = "acr-key";
            k.style.left = row.left + i * 73.7 + "px";
            k.style.top = row.top + "px";
            k.style.width = "62px";
            k.textContent = ch;
            keysEl.appendChild(k);
          });
        });
        [
          {
            left: 13,
            top: 263,
            width: 97,
            cls: " acr-gkey",
            html: '<svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="#000" stroke-width="1.6" stroke-linejoin="round"><path d="M12 4 L4.5 12 H8.5 V17 H15.5 V12 H19.5 Z"/></svg>',
          },
          {
            left: 640,
            top: 263,
            width: 98,
            cls: " acr-gkey",
            html: '<svg width="31" height="24" viewBox="0 0 28 20" fill="none" stroke="#000" stroke-width="1.6" stroke-linejoin="round" stroke-linecap="round"><path d="M9 2 H26 V18 H9 L1.5 10 Z"/><path d="M13 6.5 L20 13.5 M20 6.5 L13 13.5"/></svg>',
          },
          { left: 13, top: 353, width: 97, cls: " acr-gkey acr-num", html: "123" },
          {
            left: 118,
            top: 353,
            width: 86,
            cls: "",
            html: '<svg width="27" height="27" viewBox="0 0 24 24" fill="none" stroke="#000" stroke-width="1.4" stroke-linecap="round"><circle cx="12" cy="12" r="9"/><circle cx="9" cy="10" r="0.6" fill="#000"/><circle cx="15" cy="10" r="0.6" fill="#000"/><path d="M8 14.5 C9.5 16.5 14.5 16.5 16 14.5"/></svg>',
          },
          { left: 217, top: 353, width: 389, cls: " acr-spc", html: "space" },
          {
            left: 614,
            top: 353,
            width: 124,
            cls: " acr-gkey",
            html: '<svg width="27" height="27" viewBox="0 0 24 24" fill="none" stroke="#000" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"><path d="M19 5 V11 H6"/><path d="M9.5 7.5 L6 11 L9.5 14.5"/></svg>',
          },
        ].forEach(function (spec) {
          var k = document.createElement("div");
          k.className = "acr-key" + spec.cls;
          k.style.left = spec.left + "px";
          k.style.top = spec.top + "px";
          k.style.width = spec.width + "px";
          k.innerHTML = spec.html;
          keysEl.appendChild(k);
        });

        /* -------- timeline -------- */
        window.__timelines = window.__timelines || {};
        var tl = gsap.timeline({ paused: true });

        /* keystroke and token rhythms, measured from the reference and cycled */
        var KEY_GAPS = [
          0.1, 0.033, 0.2, 0.133, 0.2, 0.033, 0.067, 0.033, 0.1, 0.067, 0.033, 0.067, 0.067, 0.067,
          0.133, 0.067, 0.033, 0.067, 0.1, 0.1, 0.067, 0.133, 0.1, 0.033, 0.1, 0.033, 0.167, 0.033,
          0.1, 0.1, 0.067, 0.133, 0.033, 0.067, 0.1, 0.033, 0.1, 0.067,
        ];
        var WORD_GAPS = [
          0.167, 0.1, 0.0, 0.367, 0.0, 0.167, 0.133, 0.1, 0.1, 0.133, 0.167, 0.133, 0.0, 0.233,
          0.167, 0.1, 0.0, 0.133, 0.133, 0.1, 0.167, 0.133, 0.1, 0.133, 0.167, 0.1, 0.133, 0.1,
          0.133,
        ];

        /* keyboard rises, composer rides it (verbatim anchors) */
        tl.fromTo("#acr-keyboard", { y: 482 }, { y: 0, duration: 0.2667, ease: "power2.out" }, 0.2);
        tl.fromTo(
          "#acr-composer",
          { y: 0 },
          { y: -497, duration: 0.2667, ease: "power2.out" },
          0.2,
        );

        /* typing: two characters land on the first beat, then the measured gaps */
        tl.set("#acr-ctext", { textContent: "", autoAlpha: 0 }, 0.001);
        var TYPE_START = 0.8637;
        tl.set("#acr-ctext", { autoAlpha: 1 }, TYPE_START);
        tl.set("#acr-ph", { autoAlpha: 0 }, TYPE_START);
        tl.set("#acr-btn-send-grey", { autoAlpha: 0 }, TYPE_START);
        tl.set("#acr-btn-send-black", { autoAlpha: 1 }, TYPE_START);
        var t = TYPE_START;
        for (var n = 2; n <= userMessage.length; n += 1) {
          tl.set("#acr-ctext", { textContent: userMessage.slice(0, n) }, t);
          t += KEY_GAPS[(n - 2) % KEY_GAPS.length];
        }
        var typeEnd = t;
        /* caret blinks on a steady half-second while typing */
        tl.set("#acr-caret", { autoAlpha: 0 }, 0.001);
        for (var c = TYPE_START, on = true; c < typeEnd; c += 0.5333, on = !on) {
          tl.set("#acr-caret", { autoAlpha: on ? 1 : 0 }, c);
        }

        /* send: bubble pops, keyboard drops, stream begins */
        var SEND = typeEnd + 0.1667;
        tl.set("#acr-caret", { autoAlpha: 0 }, SEND);
        tl.set("#acr-ctext", { textContent: "" }, SEND);
        tl.set("#acr-ph", { autoAlpha: 1 }, SEND);
        tl.set("#acr-btn-send-black", { autoAlpha: 0 }, SEND);
        tl.set("#acr-btn-stop", { autoAlpha: 1 }, SEND);
        tl.fromTo(
          "#acr-bubble",
          { autoAlpha: 0 },
          { autoAlpha: 1, duration: 0.1667, ease: "power1.out" },
          SEND - 0.03,
        );
        tl.set("#acr-hicons-a", { autoAlpha: 0 }, SEND);
        tl.set("#acr-hicons-b", { autoAlpha: 1 }, SEND);
        tl.to("#acr-keyboard", { y: 482, duration: 0.2667, ease: "power1.inOut" }, SEND - 0.03);
        tl.to("#acr-composer", { y: 0, duration: 0.2667, ease: "power1.inOut" }, SEND - 0.03);

        /* thinking dot: the measured alpha ramp, then it yields to the words */
        var DOT_RAMP = [
          0.488, 0.53, 0.53, 0.56, 0.608, 0.687, 0.729, 0.771, 0.813, 0.867, 0.904, 0.988, 1,
        ];
        var dotStart = SEND + 0.4333;
        tl.set("#acr-dot", { autoAlpha: 0 }, 0.001);
        DOT_RAMP.forEach(function (a, i) {
          tl.set("#acr-dot", { autoAlpha: a }, dotStart + i * 0.0333);
        });
        tl.set("#acr-dot", { autoAlpha: 0 }, dotStart + 0.5667);

        /* the stream: measured token rhythm, fresh grey inking to full colour */
        var w = dotStart + 0.6333;
        wordEls.forEach(function (el, i) {
          if (i > 0) w += WORD_GAPS[i % WORD_GAPS.length];
          tl.set(el, { opacity: 1 }, w);
          tl.fromTo(
            el,
            { color: "#767676" },
            { color: "#141414", duration: 0.2667, ease: "none" },
            w,
          );
        });

        /* stream ends: stop button returns to send */
        tl.set("#acr-btn-stop", { autoAlpha: 0 }, w + 0.4667);
        tl.set("#acr-btn-send-black", { autoAlpha: 1 }, w + 0.4667);

        /* end card */
        tl.set("#acr-endcard-inner", { opacity: 0 }, 0.001);
        tl.fromTo(
          "#acr-endcard-inner",
          { opacity: 0 },
          { opacity: 1, duration: 0.3167, ease: "none" },
          15.8333,
        );

        window.__timelines["ai-chat-reveal"] = tl;
      })();
    </script>
  </body>
</html>
```

Copy npx hyperframes add ai-chat-reveal to the clipboard

That writes `compositions/ai-chat-reveal.html`, plus 2 supporting files under `assets/`.

## Add it to your video

It runs for 19.3333 seconds at 1080×1920. Paste this into your composition:

```html index.html
<div
  data-composition-id="ai-chat-reveal"
  data-composition-src="compositions/ai-chat-reveal.html"
  data-start="0"
  data-duration="19.3333"
  data-track-index="1"
  data-width="1080"
  data-height="1920"
></div>
```

Move it in time with `data-start`. Put it on a different timeline row with
`data-track-index`. See [data attributes](/concepts/data-attributes.md) for the rest.

## 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 | | |
| - | - | - | - | - | - |
| `botName` | `Assistant` | string | Name of the assistant answering in this mock chat. It belongs to the host product, never to the brand this video promotes. | | |
| `userMessage` | `How do I turn my HTML into real video?` | string | The question typed on the keyboard and sent. Around 40 characters keeps the measured pacing. | | |
| `answer1` | `You do not need an editor. HyperFrames renders the HTML you already write into deterministic, pixel-perfect video.` | string | First streamed paragraph. It names the brand this video promotes and says what that brand does. | | |
| `answer2` | `It is markup, not magic.` | string | Short second beat. | | |
| `answer3` | `What you get out of the box:` | string | Lead-in to the list. | | |
| `bullet1` | `A catalog of motion primitives you install and own` | string | First list item. | | |
| `bullet2` | `GSAP timelines that seek to any frame` | string | Second list item. | | |
| `bullet3` | `4K renders from a single CLI command` | string | Third list item. | | |
| `ecHeadline` | \`It's not magic. | It's HTML.\` | string | Closing card headline. A | breaks the line. |
| `ecSub` | `Open-source video rendering — write HTML, ship pixel-perfect video.` | string | Positioning sentence of the brand this video promotes, on the closing card. | | |
| `ecCta` | `Try HyperFrames` | string | Closing-card button label, which names the brand this video promotes. Use the real name of that brand. | | |
| `ecFooter` | `HYPERFRAMES.HEYGEN.COM` | string | Domain of the brand this video promotes, letter-spaced at the very bottom. Use the real domain. | | |
| `brandLogo` | `assets/hyperframes-logo-white.svg` | image | Transparent mark of the brand this video promotes, in the closing-card 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
<div
  data-composition-id="ai-chat-reveal"
  data-composition-src="compositions/ai-chat-reveal.html"
  data-variable-values='{"botName":"Assistant","userMessage":"How do I turn my HTML into real video?","answer1":"You do not need an editor. HyperFrames renders the HTML you already write into deterministic, pixel-perfect video.","answer2":"It is markup, not magic.","answer3":"What you get out of the box:","bullet1":"A catalog of motion primitives you install and own","bullet2":"GSAP timelines that seek to any frame","bullet3":"4K renders from a single CLI command","ecHeadline":"It's not magic.|It's HTML.","ecSub":"Open-source video rendering — write HTML, ship pixel-perfect video.","ecCta":"Try HyperFrames","ecFooter":"HYPERFRAMES.HEYGEN.COM","brandLogo":"assets/hyperframes-logo-white.svg"}'
></div>
```

{/* hf:generated-footer */}

Tagged `showcase` `mock-ui` `ai` `chat` `vertical` `ad-template`.

Created by Miao Yang.

## Related topics

* [Browse the complete Catalog](/catalog.md)
* [Add assets and Catalog items in the browser editor](/studio/assets-and-blocks.md)
* [Put blocks together into one video](/concepts/compositions.md)
