Install
That writescompositions/ai-chat-reveal.html, plus 1 supporting file under assets/.
Add it to your video
It runs for 19.3333 seconds at 750×1624. Paste this into your composition: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="750"
data-height="1624"
></div>
data-start. Put it on a different timeline row with
data-track-index. See data attributes 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 | Title in the chat header. | ||
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. | ||
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 | Supporting sentence on the closing card. | ||
ecCta | Try HyperFrames | string | Label on the closing card button. | ||
ecFooter | HYPERFRAMES.HEYGEN.COM | string | Letter-spaced line at the very bottom. |
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:
<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"}'
></div>
Source
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": "Title in the chat header.", "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.", "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's not magic.|It's HTML." },
{ "id": "ecSub", "type": "string", "role": "content", "label": "End-card subline", "description": "Supporting sentence on the closing card.", "default": "Open-source video rendering — write HTML, ship pixel-perfect video." },
{ "id": "ecCta", "type": "string", "role": "content", "label": "End-card CTA", "description": "Label on the closing card button.", "default": "Try HyperFrames" },
{ "id": "ecFooter", "type": "string", "role": "content", "label": "End-card footer", "description": "Letter-spaced line at the very bottom.", "default": "HYPERFRAMES.HEYGEN.COM" }
]'
>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=750, height=1624" />
<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: 750px;
height: 1624px;
overflow: hidden;
background: #000;
}
body {
font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", Arial, sans-serif;
}
/* 750x1624 is the reference device capture, kept native so the measured
keyboard geometry stays pixel-true; scale the block when embedding */
#acr-root {
position: relative;
width: 750px;
height: 1624px;
overflow: hidden;
}
.clip {
position: absolute;
inset: 0;
}
#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="750"
data-height="1624"
data-duration="19.3333"
>
<section id="acr-chat" class="clip" data-start="0" data-duration="16.9" data-track-index="1">
<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>
</section>
<section
id="acr-endcard"
class="clip"
data-start="15.8"
data-duration="3.5333"
data-track-index="2"
>
<div id="acr-endcard-inner">
<img id="acr-eclogo" src="assets/hyperframes-logo-white.svg" alt="HyperFrames" />
<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>
</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>
showcase mock-ui ai chat vertical ad-template.
Created by Miao Yang.