Last active
August 14, 2026 08:38
-
-
Save miguel-heygen/3f7f28c41b9fd9a76972af4ad0b32f8f to your computer and use it in GitHub Desktop.
Anthropic Agentic Loop — HyperFrames composition (pixel-perfect recreation)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!doctype html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8" /> | |
| <meta name="viewport" content="width=720, height=720" /> | |
| <script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script> | |
| <style> | |
| *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; } | |
| html, body { width: 720px; height: 720px; overflow: hidden; background: #faf9f5; } | |
| /* ── Colors (Anthropic brand kit) ── */ | |
| :root { | |
| --cream: #faf9f5; | |
| --beige: #e3dacc; | |
| --dark: #141413; | |
| --medium: #5e5d59; | |
| --light: #b0aea5; | |
| --sage: #bcd1ca; | |
| --sage-text: #3d3d3a; | |
| --blue-pill: #cbcadb; | |
| --blue-text: #3d3d3a; | |
| --coral: #d97757; | |
| --terminal-dark: #141413; | |
| --card-white: #faf9f5; | |
| --divider: #1414131a; | |
| } | |
| /* ── Typography ── */ | |
| .serif { font-family: 'EB Garamond', serif; } | |
| .sans { font-family: 'Inter', sans-serif; } | |
| .mono { font-family: 'JetBrains Mono', monospace; } | |
| /* ── Main container ── */ | |
| #main { | |
| position: relative; | |
| width: 720px; | |
| height: 720px; | |
| overflow: hidden; | |
| } | |
| /* ── Subtle grain overlay ── */ | |
| .grain { | |
| position: fixed; | |
| inset: 0; | |
| pointer-events: none; | |
| z-index: 9999; | |
| opacity: 0.03; | |
| background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='256' height='256' filter='url(%23n)'/%3E%3C/svg%3E"); | |
| background-size: 256px 256px; | |
| } | |
| /* ── Scene base ── */ | |
| .scene { | |
| position: absolute; | |
| inset: 0; | |
| width: 720px; | |
| height: 720px; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| opacity: 0; | |
| } | |
| /* ── Scene 1: Flow Diagram (simple) ── */ | |
| .scene-flow { background: var(--cream); } | |
| .flow-chain { | |
| display: flex; | |
| align-items: center; | |
| gap: 0; | |
| } | |
| .flow-node { | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| padding: 14px 28px; | |
| border-radius: 8px; | |
| font-size: 22px; | |
| font-weight: 500; | |
| white-space: nowrap; | |
| } | |
| .flow-node.active { | |
| background: var(--sage); | |
| color: var(--sage-text); | |
| } | |
| .flow-node.ghost { | |
| background: #e8e5dd; | |
| color: var(--medium); | |
| } | |
| .flow-node.prompt { | |
| background: #EDE8E1; | |
| color: var(--dark); | |
| } | |
| .flow-arrow { | |
| display: flex; | |
| align-items: center; | |
| padding: 0 10px; | |
| color: var(--medium); | |
| font-size: 20px; | |
| } | |
| .flow-arrow svg { | |
| width: 40px; | |
| height: 16px; | |
| } | |
| .flow-arrow line, .flow-arrow polyline { | |
| stroke: var(--medium); | |
| stroke-width: 1.5; | |
| fill: none; | |
| } | |
| /* ── Scene 2: Terminal ── */ | |
| .scene-terminal { background: var(--coral); } | |
| .terminal-bg-lines { | |
| position: absolute; | |
| inset: 0; | |
| overflow: hidden; | |
| } | |
| .terminal-bg-lines svg { | |
| width: 100%; | |
| height: 100%; | |
| } | |
| .terminal-bg-lines path { | |
| fill: none; | |
| stroke: rgba(255, 255, 255, 0.12); | |
| stroke-width: 1.5; | |
| } | |
| .terminal-window { | |
| position: relative; | |
| width: 560px; | |
| height: 480px; | |
| background: var(--terminal-dark); | |
| border-radius: 16px; | |
| overflow: hidden; | |
| transform-origin: center center; | |
| } | |
| .pong-canvas { | |
| position: absolute; | |
| inset: 0; | |
| width: 100%; | |
| height: 100%; | |
| } | |
| /* ── Scene 3: Title "The agentic loop" ── */ | |
| .scene-title-agentic { background: var(--cream); padding-top: 40px; } | |
| .title-text { | |
| font-size: 78px; | |
| font-weight: 500; | |
| color: var(--dark); | |
| letter-spacing: -0.02em; | |
| line-height: 1.1; | |
| } | |
| .title-text .word { | |
| display: inline-block; | |
| color: var(--light); | |
| } | |
| /* ── Scene 4: Extended Flow with Verify ── */ | |
| .scene-flow-ext { background: var(--cream); } | |
| .flow-ext-wrap { | |
| position: relative; | |
| width: 720px; | |
| height: 400px; | |
| } | |
| .s4-node { | |
| position: absolute; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| padding: 14px 24px; | |
| border-radius: 8px; | |
| font-size: 19px; | |
| font-weight: 500; | |
| white-space: nowrap; | |
| } | |
| .s4-action { background: var(--sage); color: var(--sage-text); top: 130px; left: 30px; } | |
| .s4-verify { background: var(--blue-pill); color: var(--blue-text); top: 130px; left: 250px; opacity: 0; } | |
| .s4-response { background: #e8e5dd; color: var(--medium); top: 130px; left: 460px; } | |
| .s4-arrow { | |
| position: absolute; | |
| top: 142px; | |
| color: var(--medium); | |
| } | |
| .s4-arrow svg { width: 36px; height: 14px; } | |
| .s4-arrow line, .s4-arrow polyline { stroke: var(--medium); stroke-width: 1.5; fill: none; } | |
| .s4-a1 { left: 170px; } | |
| .s4-a2 { left: 400px; opacity: 0; } | |
| .s4-feedback-outer { | |
| position: absolute; | |
| top: 175px; | |
| left: 30px; | |
| width: 580px; | |
| height: 110px; | |
| opacity: 0; | |
| } | |
| .s4-feedback-outer svg { width: 100%; height: 100%; } | |
| .s4-feedback-outer path { fill: none; stroke: var(--medium); stroke-width: 1.5; } | |
| .s4-feedback-inner { | |
| position: absolute; | |
| top: 175px; | |
| left: 130px; | |
| width: 360px; | |
| height: 80px; | |
| opacity: 0; | |
| } | |
| .s4-feedback-inner svg { width: 100%; height: 100%; } | |
| .s4-feedback-inner path { fill: none; stroke: var(--medium); stroke-width: 1.5; stroke-dasharray: 6 4; } | |
| .s4-vert-dash { | |
| position: absolute; | |
| top: 60px; | |
| left: 340px; | |
| height: 200px; | |
| border-left: 1.5px dashed var(--light); | |
| opacity: 0; | |
| } | |
| .s4-label { | |
| position: absolute; | |
| top: 55px; | |
| left: 240px; | |
| font-size: 16px; | |
| font-style: italic; | |
| color: var(--medium); | |
| opacity: 0; | |
| } | |
| .s4-label-dash-l { | |
| position: absolute; | |
| top: 65px; | |
| left: 30px; | |
| width: 200px; | |
| border-top: 1.5px dashed var(--light); | |
| opacity: 0; | |
| } | |
| .s4-label-dash-r { | |
| position: absolute; | |
| top: 65px; | |
| left: 390px; | |
| width: 220px; | |
| border-top: 1.5px dashed var(--light); | |
| opacity: 0; | |
| } | |
| /* ── Scene 5: Title "Verification loops" ── */ | |
| .scene-title-verify { background: var(--beige); padding-top: 40px; } | |
| /* ── Scene 6: Domain Table ── */ | |
| .scene-table { background: var(--cream); align-items: flex-start; padding-top: 120px; } | |
| .domain-table { | |
| width: 560px; | |
| } | |
| .table-header { | |
| display: flex; | |
| padding: 0 0 16px 0; | |
| border-bottom: 1px solid var(--divider); | |
| margin-bottom: 8px; | |
| } | |
| .table-header span { | |
| font-size: 13px; | |
| font-weight: 500; | |
| letter-spacing: 0.08em; | |
| color: var(--medium); | |
| } | |
| .th-domain { width: 140px; } | |
| .th-behavior { flex: 1; } | |
| .table-row { | |
| display: flex; | |
| align-items: center; | |
| padding: 12px 0; | |
| border-bottom: 1px solid rgba(224, 216, 206, 0.5); | |
| opacity: 0; | |
| } | |
| .table-row:last-child { border-bottom: none; } | |
| .row-domain { | |
| width: 140px; | |
| } | |
| .domain-pill { | |
| display: inline-flex; | |
| align-items: center; | |
| padding: 6px 16px; | |
| border-radius: 20px; | |
| background: var(--sage); | |
| color: var(--sage-text); | |
| font-size: 15px; | |
| font-weight: 500; | |
| } | |
| .row-behavior { | |
| flex: 1; | |
| font-size: 18px; | |
| color: var(--dark); | |
| font-weight: 400; | |
| } | |
| /* ── Scene 7: Task Cards ── */ | |
| .scene-cards { background: var(--beige); align-items: flex-start; padding-top: 70px; } | |
| .card-stack { | |
| display: flex; | |
| flex-direction: column; | |
| align-items: center; | |
| gap: 12px; | |
| width: 480px; | |
| } | |
| .task-card { | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| width: 100%; | |
| padding: 18px 40px; | |
| background: var(--card-white); | |
| border-radius: 12px; | |
| font-size: 26px; | |
| font-weight: 400; | |
| color: var(--dark); | |
| opacity: 0; | |
| } | |
| /* ── Scene 8: Code Editor ── */ | |
| .scene-code { background: #DDD8CE; } | |
| .code-editor { | |
| width: 580px; | |
| background: var(--card-white); | |
| border-radius: 16px; | |
| overflow: hidden; | |
| box-shadow: 0 2px 20px rgba(0,0,0,0.06); | |
| } | |
| .code-titlebar { | |
| padding: 14px 20px; | |
| font-size: 14px; | |
| font-weight: 500; | |
| color: var(--medium); | |
| border-bottom: 1px solid var(--divider); | |
| } | |
| .code-body { | |
| padding: 20px; | |
| font-size: 14px; | |
| line-height: 2; | |
| color: var(--dark); | |
| } | |
| .code-line { | |
| display: flex; | |
| opacity: 0; | |
| } | |
| .line-num { | |
| width: 36px; | |
| text-align: right; | |
| padding-right: 16px; | |
| color: var(--light); | |
| user-select: none; | |
| } | |
| .line-content { flex: 1; } | |
| .code-keyword { color: #6B8E5A; } | |
| .code-string { color: #B07040; } | |
| .code-comment { color: var(--medium); } | |
| .code-punct { color: var(--light); } | |
| .code-cursor { | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| pointer-events: none; | |
| opacity: 0; | |
| z-index: 10; | |
| } | |
| .code-cursor svg { | |
| width: 20px; | |
| height: 28px; | |
| filter: drop-shadow(0 1px 2px rgba(0,0,0,0.15)); | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div id="main" data-composition-id="main" data-start="0" data-duration="13.3" data-width="720" data-height="720"> | |
| <div class="grain"></div> | |
| <!-- ═══ Scene 1: Simple Flow Diagram ═══ --> | |
| <div id="s1" class="scene scene-flow clip" data-start="0" data-duration="3.5" data-track-index="0"> | |
| <div class="flow-chain sans"> | |
| <div id="s1-prompt" class="flow-node prompt">Prompt</div> | |
| <div id="s1-a1" class="flow-arrow"><svg viewBox="0 0 40 16"><line x1="0" y1="8" x2="32" y2="8"/><polyline points="28,4 34,8 28,12"/></svg></div> | |
| <div id="s1-gather" class="flow-node active">Gather context</div> | |
| <div id="s1-a2" class="flow-arrow"><svg viewBox="0 0 40 16"><line x1="0" y1="8" x2="32" y2="8"/><polyline points="28,4 34,8 28,12"/></svg></div> | |
| <div id="s1-action" class="flow-node active">Take action</div> | |
| <div id="s1-a3" class="flow-arrow"><svg viewBox="0 0 40 16"><line x1="0" y1="8" x2="32" y2="8"/><polyline points="28,4 34,8 28,12"/></svg></div> | |
| <div id="s1-response" class="flow-node ghost">Response</div> | |
| </div> | |
| </div> | |
| <!-- ═══ Scene 2: Terminal Animation ═══ --> | |
| <div id="s2" class="scene scene-terminal clip" data-start="1.5" data-duration="2.0" data-track-index="1"> | |
| <div class="terminal-bg-lines"> | |
| <svg viewBox="0 0 720 720"> | |
| <path d="M-20,200 Q180,150 360,220 T740,180" /> | |
| <path d="M-20,350 Q200,300 400,380 T740,320" /> | |
| <path d="M-20,500 Q150,450 350,520 T740,480" /> | |
| <path d="M-20,600 Q200,560 420,620 T740,580" /> | |
| <path d="M100,-20 Q80,200 120,400 T90,740" /> | |
| <path d="M300,-20 Q280,180 320,380 T290,740" /> | |
| <path d="M550,-20 Q530,200 570,420 T540,740" /> | |
| </svg> | |
| </div> | |
| <div id="s2-terminal" class="terminal-window"> | |
| <canvas id="pong-canvas" class="pong-canvas" width="560" height="480"></canvas> | |
| </div> | |
| </div> | |
| <!-- ═══ Scene 3: "The agentic loop" Title ═══ --> | |
| <div id="s3" class="scene scene-title-agentic clip" data-start="3.25" data-duration="2.0" data-track-index="2"> | |
| <div class="title-text serif"> | |
| <span id="s3-w1" class="word">The</span> | |
| <span id="s3-w2" class="word">agentic</span> | |
| <span id="s3-w3" class="word">loop</span> | |
| </div> | |
| </div> | |
| <!-- ═══ Scene 4: Extended Flow with Verify ═══ --> | |
| <div id="s4" class="scene scene-flow-ext clip" data-start="4.75" data-duration="2.0" data-track-index="3"> | |
| <div class="flow-ext-wrap sans"> | |
| <div id="s4-action" class="s4-node s4-action">Take action</div> | |
| <div id="s4-a1" class="s4-arrow s4-a1"><svg viewBox="0 0 36 14"><line x1="0" y1="7" x2="28" y2="7"/><polyline points="24,3 30,7 24,11"/></svg></div> | |
| <div id="s4-verify" class="s4-node s4-verify">Verify results</div> | |
| <div id="s4-a2" class="s4-arrow s4-a2"><svg viewBox="0 0 36 14"><line x1="0" y1="7" x2="28" y2="7"/><polyline points="24,3 30,7 24,11"/></svg></div> | |
| <div id="s4-resp" class="s4-node s4-response">Response</div> | |
| <div id="s4-fb-outer" class="s4-feedback-outer"> | |
| <svg viewBox="0 0 580 110"><path d="M560,0 L560,75 Q560,95 540,95 L40,95 Q20,95 20,75 L20,0"/></svg> | |
| </div> | |
| <div id="s4-fb-inner" class="s4-feedback-inner"> | |
| <svg viewBox="0 0 360 80"><path d="M340,0 L340,50 Q340,65 320,65 L40,65 Q20,65 20,50 L20,0"/></svg> | |
| </div> | |
| <div id="s4-vdash" class="s4-vert-dash"></div> | |
| <div id="s4-label" class="s4-label">Agentic loop</div> | |
| <div id="s4-dl" class="s4-label-dash-l"></div> | |
| <div id="s4-dr" class="s4-label-dash-r"></div> | |
| </div> | |
| </div> | |
| <!-- ═══ Scene 5: "Verification loops" Title ═══ --> | |
| <div id="s5" class="scene scene-title-verify clip" data-start="6.5" data-duration="2.0" data-track-index="4"> | |
| <div class="title-text serif"> | |
| <span id="s5-w1" class="word">Verification</span> | |
| <span id="s5-w2" class="word">loops</span> | |
| </div> | |
| </div> | |
| <!-- ═══ Scene 6: Domain Validation Table ═══ --> | |
| <div id="s6" class="scene scene-table clip" data-start="8.0" data-duration="2.25" data-track-index="5"> | |
| <div class="domain-table sans"> | |
| <div id="s6-header" class="table-header" style="opacity:0"> | |
| <span class="th-domain">DOMAIN</span> | |
| <span class="th-behavior">VALIDATE BEHAVIOR</span> | |
| </div> | |
| <div id="s6-r1" class="table-row"> | |
| <div class="row-domain"><span class="domain-pill">Frontend</span></div> | |
| <div class="row-behavior serif">Run dev server, click around, read console</div> | |
| </div> | |
| <div id="s6-r2" class="table-row"> | |
| <div class="row-domain"><span class="domain-pill">Backend</span></div> | |
| <div class="row-behavior serif">Hit endpoint, send payload, check response</div> | |
| </div> | |
| <div id="s6-r3" class="table-row"> | |
| <div class="row-domain"><span class="domain-pill">Data</span></div> | |
| <div class="row-behavior serif">Run migration on staging, check row counts</div> | |
| </div> | |
| <div id="s6-r4" class="table-row"> | |
| <div class="row-domain"><span class="domain-pill">Mobile</span></div> | |
| <div class="row-behavior serif">Launch simulator, click around, watch logs</div> | |
| </div> | |
| <div id="s6-r5" class="table-row"> | |
| <div class="row-domain"><span class="domain-pill">Desktop</span></div> | |
| <div class="row-behavior serif">Launch app, click around, watch logs</div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- ═══ Scene 7: Task Cards ═══ --> | |
| <div id="s7" class="scene scene-cards clip" data-start="9.75" data-duration="1.75" data-track-index="6"> | |
| <div class="card-stack serif"> | |
| <div id="s7-c1" class="task-card">Make a change</div> | |
| <div id="s7-c2" class="task-card">Clean it up</div> | |
| <div id="s7-c3" class="task-card">Verify it works</div> | |
| <div id="s7-c4" class="task-card">Commit</div> | |
| <div id="s7-c5" class="task-card">Open a PR</div> | |
| <div id="s7-c6" class="task-card">Get it reviewed</div> | |
| <div id="s7-c7" class="task-card">Monitor CI</div> | |
| </div> | |
| </div> | |
| <!-- ═══ Scene 8: SKILL.md Code Editor ═══ --> | |
| <div id="s8" class="scene scene-code clip" data-start="11.0" data-duration="2.3" data-track-index="7"> | |
| <div class="code-editor" id="s8-editor"> | |
| <div class="code-titlebar mono">SKILL.md</div> | |
| <div class="code-body mono"> | |
| <div id="s8-l1" class="code-line"><span class="line-num">1</span><span class="line-content"><span class="code-punct">---</span></span></div> | |
| <div id="s8-l2" class="code-line"><span class="line-num">2</span><span class="line-content"><span class="code-keyword">name:</span> ship</span></div> | |
| <div id="s8-l3" class="code-line"><span class="line-num">3</span><span class="line-content"><span class="code-keyword">description:</span> <span class="code-string">Take a finished change f...</span></span></div> | |
| <div id="s8-l4" class="code-line"><span class="line-num">4</span><span class="line-content"><span class="code-punct">---</span></span></div> | |
| <div id="s8-l5" class="code-line"><span class="line-num">5</span><span class="line-content"> </span></div> | |
| <div id="s8-l6" class="code-line"><span class="line-num">6</span><span class="line-content">1. Run <span class="code-string">`/simplify`</span> <span class="code-comment">— Code quality pas...</span></span></div> | |
| <div id="s8-l7" class="code-line"><span class="line-num">7</span><span class="line-content">2. Run <span class="code-string">`/verify`</span> <span class="code-comment">— Edge-case sweep, r...</span></span></div> | |
| <div id="s8-l8" class="code-line"><span class="line-num">8</span><span class="line-content"><span class="code-comment"> change end-to-end. Tests passing #...</span></span></div> | |
| <div id="s8-l9" class="code-line"><span class="line-num">9</span><span class="line-content">3. Run <span class="code-string">`/design`</span> <span class="code-comment">— Design check if th...</span></span></div> | |
| <div id="s8-l10" class="code-line"><span class="line-num">10</span><span class="line-content">4. Run <span class="code-string">`/pr`</span> <span class="code-comment">— Commit, push, open a P...</span></span></div> | |
| <div id="s8-l11" class="code-line"><span class="line-num">11</span><span class="line-content">5. Run <span class="code-string">`/babysit`</span> <span class="code-comment">— Watch PR, fix fai...</span></span></div> | |
| </div> | |
| </div> | |
| <div id="s8-cursor" class="code-cursor"><svg viewBox="0 0 24 34" fill="none"><path d="M5.5 2L5.5 24.5L10.5 19.5L15.5 28L19 26L14 17.5L21 17.5L5.5 2Z" fill="white" stroke="#141413" stroke-width="1.5" stroke-linejoin="round"/></svg></div> | |
| </div> | |
| </div> | |
| <script> | |
| window.__timelines = window.__timelines || {}; | |
| const tl = gsap.timeline({ paused: true }); | |
| // ═══ Scene 1: Flow Diagram Build (0 – 1.5s) ═══ | |
| // Each pill+arrow pair fades from 0→1 over ~0.4s. No pre-existing ghosts. | |
| tl.to("#s1", { opacity: 1, duration: 0.01 }, 0); | |
| tl.set(".flow-chain", { x: 250 }, 0); | |
| // Hide everything initially | |
| tl.set(["#s1-prompt","#s1-a1","#s1-gather","#s1-a2","#s1-action","#s1-a3","#s1-response"], { opacity: 0 }, 0); | |
| // Prompt fades in alone (0→1 over 0.4s) | |
| tl.to("#s1-prompt", { opacity: 1, duration: 0.4, ease: "power1.out" }, 0.05); | |
| // Arrow 1 + Gather context fade in together | |
| tl.to("#s1-a1", { opacity: 1, duration: 0.4, ease: "power1.out" }, 0.35); | |
| tl.to("#s1-gather", { opacity: 1, duration: 0.4, ease: "power1.out" }, 0.35); | |
| // Pan left as chain grows | |
| tl.to(".flow-chain", { x: 120, duration: 0.5, ease: "power2.inOut" }, 0.5); | |
| // Arrow 2 + Take action fade in together | |
| tl.to("#s1-a2", { opacity: 1, duration: 0.4, ease: "power1.out" }, 0.7); | |
| tl.to("#s1-action", { opacity: 1, duration: 0.4, ease: "power1.out" }, 0.7); | |
| // Pan left more | |
| tl.to(".flow-chain", { x: -10, duration: 0.5, ease: "power2.inOut" }, 0.85); | |
| // Arrow 3 + Response fade in together | |
| tl.to("#s1-a3", { opacity: 1, duration: 0.4, ease: "power1.out" }, 1.05); | |
| tl.to("#s1-response", { opacity: 1, duration: 0.4, ease: "power1.out" }, 1.05); | |
| // Final pan left | |
| tl.to(".flow-chain", { x: -100, duration: 0.5, ease: "power2.inOut" }, 1.15); | |
| // ═══ Transition 1→2: Fast dissolve ═══ | |
| tl.to("#s1", { opacity: 0, duration: 0.08 }, 1.6); | |
| // ═══ Scene 2: Terminal (1.5 – 3.25s) ═══ | |
| tl.to("#s2", { opacity: 1, duration: 0.08 }, 1.6); | |
| tl.from("#s2-terminal", { scale: 1.3, rotation: -3, opacity: 0, duration: 0.4, ease: "power3.out" }, 1.6); | |
| // Terminal rotates during game | |
| tl.to("#s2-terminal", { rotation: 12, scale: 1.3, x: -50, y: -30, duration: 1.5, ease: "power1.inOut" }, 1.7); | |
| // Deterministic Canvas Pong simulation | |
| const canvas = document.getElementById("pong-canvas"); | |
| const ctx = canvas.getContext("2d"); | |
| const W = 560, H = 480; | |
| const PADDLE_W = 5, PADDLE_H = 44, BALL_S = 10; | |
| const PADDLE_MARGIN = 35; | |
| function simulatePong(t) { | |
| const speed = 320; | |
| let bx = W - 80, by = H * 0.7; | |
| let vx = -speed, vy = -speed * 0.3; | |
| let lpY = H * 0.7, rpY = H * 0.3; | |
| const dt = 1 / 120; | |
| const steps = Math.floor(t / dt); | |
| for (let i = 0; i < steps; i++) { | |
| bx += vx * dt; | |
| by += vy * dt; | |
| // Top/bottom wall bounce | |
| if (by <= BALL_S / 2) { by = BALL_S / 2; vy = Math.abs(vy); } | |
| if (by >= H - BALL_S / 2) { by = H - BALL_S / 2; vy = -Math.abs(vy); } | |
| // Left paddle hit | |
| if (bx <= PADDLE_MARGIN + PADDLE_W + BALL_S / 2 && vx < 0) { | |
| if (by >= lpY - PADDLE_H / 2 - 8 && by <= lpY + PADDLE_H / 2 + 8) { | |
| bx = PADDLE_MARGIN + PADDLE_W + BALL_S / 2; | |
| vx = Math.abs(vx); | |
| vy = (by - lpY) * 3.5; | |
| } | |
| } | |
| // Right paddle hit | |
| if (bx >= W - PADDLE_MARGIN - PADDLE_W - BALL_S / 2 && vx > 0) { | |
| if (by >= rpY - PADDLE_H / 2 - 8 && by <= rpY + PADDLE_H / 2 + 8) { | |
| bx = W - PADDLE_MARGIN - PADDLE_W - BALL_S / 2; | |
| vx = -Math.abs(vx); | |
| vy = (by - rpY) * 3.5; | |
| } | |
| } | |
| // Reset if off-screen | |
| if (bx < -20 || bx > W + 20) { bx = W / 2; by = H / 2; vx = speed * (bx > W / 2 ? -1 : 1); vy = speed * 0.4; } | |
| // AI paddles track ball | |
| const trackSpeed = 280 * dt; | |
| if (vx < 0) { lpY += Math.sign(by - lpY) * Math.min(Math.abs(by - lpY), trackSpeed); } | |
| else { rpY += Math.sign(by - rpY) * Math.min(Math.abs(by - rpY), trackSpeed); } | |
| lpY = Math.max(PADDLE_H / 2, Math.min(H - PADDLE_H / 2, lpY)); | |
| rpY = Math.max(PADDLE_H / 2, Math.min(H - PADDLE_H / 2, rpY)); | |
| } | |
| return { bx, by, lpY, rpY }; | |
| } | |
| function drawPong(t) { | |
| ctx.clearRect(0, 0, W, H); | |
| const { bx, by, lpY, rpY } = simulatePong(t); | |
| const col = "#E8E0D6"; | |
| // Center dashed line | |
| ctx.strokeStyle = "rgba(255,255,255,0.15)"; | |
| ctx.lineWidth = 2; | |
| ctx.setLineDash([6, 6]); | |
| ctx.beginPath(); ctx.moveTo(W / 2, 0); ctx.lineTo(W / 2, H); ctx.stroke(); | |
| ctx.setLineDash([]); | |
| // Left paddle | |
| ctx.fillStyle = col; | |
| ctx.fillRect(PADDLE_MARGIN, lpY - PADDLE_H / 2, PADDLE_W, PADDLE_H); | |
| // Right paddle (fade in after 0.5s) | |
| if (t > 0.5) { | |
| const a = Math.min(1, (t - 0.5) / 0.3); | |
| ctx.globalAlpha = a; | |
| ctx.fillRect(W - PADDLE_MARGIN - PADDLE_W, rpY - PADDLE_H / 2, PADDLE_W, PADDLE_H); | |
| ctx.globalAlpha = 1; | |
| } | |
| // Ball | |
| ctx.fillStyle = col; | |
| ctx.fillRect(bx - BALL_S / 2, by - BALL_S / 2, BALL_S, BALL_S); | |
| } | |
| // Drive Pong via GSAP progress tween (1.6s duration of pong scene) | |
| const pongObj = { t: 0 }; | |
| tl.to(pongObj, { | |
| t: 1.6, | |
| duration: 1.6, | |
| ease: "none", | |
| onUpdate: function() { drawPong(pongObj.t); } | |
| }, 1.6); | |
| // ═══ Transition 2→3: Fast dissolve ═══ | |
| tl.to("#s2", { opacity: 0, duration: 0.1 }, 3.2); | |
| tl.set("#s2", { opacity: 0 }, 3.25); | |
| // ═══ Scene 3: "The agentic loop" (3.25 – 4.75s) ═══ | |
| tl.to("#s3", { opacity: 1, duration: 0.1 }, 3.2); | |
| // Words fade from light gray → dark (color transition, not opacity) | |
| tl.to("#s3-w1", { color: "#141413", duration: 0.35, ease: "power2.out" }, 3.3); | |
| tl.to("#s3-w2", { color: "#141413", duration: 0.4, ease: "power2.out" }, 3.5); | |
| tl.to("#s3-w3", { color: "#141413", duration: 0.45, ease: "power2.out" }, 3.7); | |
| // ═══ Transition 3→4: Fast dissolve ═══ | |
| tl.to("#s3", { opacity: 0, duration: 0.1 }, 4.7); | |
| tl.set("#s3", { opacity: 0 }, 4.75); | |
| // ═══ Scene 4: Extended Flow with Verify (4.75 – 6.5s) ═══ | |
| tl.to("#s4", { opacity: 1, duration: 0.1 }, 4.7); | |
| // Start: Take action + Response close together, no Verify | |
| tl.set("#s4-resp", { left: 240 }, 4.75); | |
| tl.set("#s4-a1", { left: 170 }, 4.75); | |
| // Show outer feedback loop immediately | |
| tl.to("#s4-fb-outer", { opacity: 1, duration: 0.3, ease: "power2.out" }, 4.85); | |
| // Pan: spread Take action and Response apart to make room | |
| tl.to("#s4-resp", { left: 460, duration: 0.6, ease: "power2.inOut" }, 5.2); | |
| tl.to("#s4-fb-outer", { width: 580, duration: 0.6, ease: "power2.inOut" }, 5.2); | |
| // Insert Verify results in the gap | |
| tl.to("#s4-verify", { opacity: 1, duration: 0.35, ease: "power2.out" }, 5.5); | |
| tl.to("#s4-a1", { opacity: 1, duration: 0.2 }, 5.5); | |
| tl.to("#s4-a2", { opacity: 1, left: 400, duration: 0.3, ease: "power2.out" }, 5.6); | |
| // Inner dashed feedback loop | |
| tl.to("#s4-fb-inner", { opacity: 1, duration: 0.35, ease: "power2.out" }, 5.7); | |
| // Vertical dashed line | |
| tl.to("#s4-vdash", { opacity: 1, duration: 0.3, ease: "power2.out" }, 5.8); | |
| // "Agentic loop" label + horizontal dashes | |
| tl.to("#s4-label", { opacity: 1, duration: 0.3, ease: "power2.out" }, 5.9); | |
| tl.to("#s4-dl", { opacity: 1, duration: 0.25, ease: "power2.out" }, 5.95); | |
| tl.to("#s4-dr", { opacity: 1, duration: 0.25, ease: "power2.out" }, 5.95); | |
| // ═══ Transition 4→5: Fast dissolve ═══ | |
| tl.to("#s4", { opacity: 0, duration: 0.1 }, 6.4); | |
| tl.set("#s4", { opacity: 0 }, 6.5); | |
| // ═══ Scene 5: "Verification loops" (6.5 – 8.0s) ═══ | |
| tl.to("#s5", { opacity: 1, duration: 0.1 }, 6.4); | |
| tl.to("#s5-w1", { color: "#141413", duration: 0.35, ease: "power2.out" }, 6.55); | |
| tl.to("#s5-w2", { color: "#141413", duration: 0.4, ease: "power2.out" }, 6.8); | |
| // ═══ Transition 5→6 ═══ | |
| tl.to("#s5", { opacity: 0, duration: 0.1 }, 7.9); | |
| tl.set("#s5", { opacity: 0 }, 8.0); | |
| // ═══ Scene 6: Domain Table (8.0 – 9.75s) ═══ | |
| tl.to("#s6", { opacity: 1, duration: 0.1 }, 7.9); | |
| tl.to("#s6-header", { opacity: 1, duration: 0.3, ease: "power2.out" }, 8.1); | |
| tl.to("#s6-r1", { opacity: 1, y: 0, duration: 0.35, ease: "power2.out" }, 8.3); | |
| tl.from("#s6-r1", { y: 15 }, 8.3); | |
| tl.to("#s6-r2", { opacity: 1, y: 0, duration: 0.35, ease: "power2.out" }, 8.55); | |
| tl.from("#s6-r2", { y: 15 }, 8.55); | |
| tl.to("#s6-r3", { opacity: 1, y: 0, duration: 0.35, ease: "power2.out" }, 8.8); | |
| tl.from("#s6-r3", { y: 15 }, 8.8); | |
| tl.to("#s6-r4", { opacity: 1, y: 0, duration: 0.35, ease: "power2.out" }, 9.05); | |
| tl.from("#s6-r4", { y: 15 }, 9.05); | |
| tl.to("#s6-r5", { opacity: 1, y: 0, duration: 0.35, ease: "power2.out" }, 9.3); | |
| tl.from("#s6-r5", { y: 15 }, 9.3); | |
| // ═══ Transition 6→7 ═══ | |
| tl.to("#s6", { opacity: 0, duration: 0.1 }, 9.65); | |
| tl.set("#s6", { opacity: 0 }, 9.75); | |
| // ═══ Scene 7: Task Cards (9.75 – 11.0s) ═══ | |
| tl.to("#s7", { opacity: 1, duration: 0.1 }, 9.65); | |
| const cards = ["#s7-c1", "#s7-c2", "#s7-c3", "#s7-c4", "#s7-c5", "#s7-c6", "#s7-c7"]; | |
| cards.forEach((c, i) => { | |
| tl.to(c, { opacity: 1, duration: 0.25, ease: "power2.out" }, 9.85 + i * 0.12); | |
| tl.from(c, { y: 20, duration: 0.3, ease: "power2.out" }, 9.85 + i * 0.12); | |
| }); | |
| // ═══ Transition 7→8 ═══ | |
| tl.to("#s7", { opacity: 0, duration: 0.1 }, 10.9); | |
| tl.set("#s7", { opacity: 0 }, 11.0); | |
| // ═══ Scene 8: Code Editor (11.0 – 13.3s) ═══ | |
| tl.to("#s8", { opacity: 1, duration: 0.1 }, 10.9); | |
| tl.from("#s8-editor", { y: 30, scale: 0.95, duration: 0.5, ease: "power3.out" }, 11.0); | |
| // Lines appear | |
| const lines = ["#s8-l1","#s8-l2","#s8-l3","#s8-l4","#s8-l5","#s8-l6","#s8-l7","#s8-l8","#s8-l9","#s8-l10","#s8-l11"]; | |
| lines.forEach((l, i) => { | |
| tl.to(l, { opacity: 1, duration: 0.15, ease: "power1.out" }, 11.2 + i * 0.08); | |
| }); | |
| // Cursor appears from bottom-right, moves to /verify on line 7 | |
| tl.set("#s8-cursor", { x: 550, y: 550 }, 11.6); | |
| tl.to("#s8-cursor", { opacity: 1, duration: 0.2 }, 11.8); | |
| tl.to("#s8-cursor", { x: 240, y: 395, duration: 0.6, ease: "power2.out" }, 11.9); | |
| // Settle on /verify, small drift | |
| tl.to("#s8-cursor", { x: 235, y: 390, duration: 0.5, ease: "power1.inOut" }, 12.5); | |
| // Slight movement as if reading down | |
| tl.to("#s8-cursor", { x: 245, y: 400, duration: 0.5, ease: "power1.inOut" }, 13.0); | |
| // Zoom into editor as cursor settles | |
| tl.to("#s8-editor", { scale: 1.05, duration: 1.2, ease: "power1.inOut" }, 12.0); | |
| window.__timelines["main"] = tl; | |
| </script> | |
| </body> | |
| </html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment