feat(registry): add Code Animations catalog section (9 blocks, incl. GPU)

Adds a Code Animations catalog section — 9 self-contained, installable blocks:
morph, snippet-flight, typing, diff, highlight, scroll (DOM/GSAP) and 3d-extrude,
shader-dissolve, particle-assemble (WebGL). Each block ships only its own effect and
renders deterministically (paused GSAP timeline seeked per frame, seeded RNG, no
render-time data fetch). Wires the catalog nav, registry.json, a new code-animation
Studio category, and preview assets.
This commit is contained in:
Miguel Ángel
2026-06-15 21:32:45 -04:00
committed by GitHub
parent 38043d2a20
commit 68e635a283
32 changed files with 8029 additions and 12 deletions
+44
View File
@@ -0,0 +1,44 @@
---
title: "Code 3D Extrude"
description: "Syntax-highlighted code on a lit, beveled 3D slab that rotates through real space and settles to a readable rest — true WebGL depth and lighting, not a 2D transform."
---
# Code 3D Extrude
Syntax-highlighted code on a lit, beveled 3D slab that rotates through real space and settles to a readable rest — true WebGL depth and lighting, not a 2D transform.
`code` `code-animation` `3d` `webgl` `developer`
<video className="w-full aspect-video rounded-xl object-cover bg-zinc-100 dark:bg-zinc-800" src="https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/code-3d-extrude.mp4" poster="https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/code-3d-extrude.png" autoPlay muted loop playsInline />
## Install
<CodeGroup>
```bash Terminal
npx hyperframes add code-3d-extrude
```
</CodeGroup>
## Details
| Property | Value |
| --- | --- |
| Type | Block |
| Dimensions | 1920×1080 |
| Duration | 8s |
## Files
| File | Target | Type |
| --- | --- | --- |
| `code-3d-extrude.html` | `compositions/code-3d-extrude.html` | hyperframes:composition |
## Usage
After installing, add the block to your host composition:
```html
<div data-composition-id="code-3d-extrude" data-composition-src="compositions/code-3d-extrude.html" data-start="0" data-duration="8" data-track-index="1" data-width="1920" data-height="1080"></div>
```
+44
View File
@@ -0,0 +1,44 @@
---
title: "Code Diff"
description: "An edit shown as a colored diff — removed lines collapse in red, added lines expand in green."
---
# Code Diff
An edit shown as a colored diff — removed lines collapse in red, added lines expand in green.
`code` `code-animation` `diff` `developer`
<video className="w-full aspect-video rounded-xl object-cover bg-zinc-100 dark:bg-zinc-800" src="https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/code-diff.mp4" poster="https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/code-diff.png" autoPlay muted loop playsInline />
## Install
<CodeGroup>
```bash Terminal
npx hyperframes add code-diff
```
</CodeGroup>
## Details
| Property | Value |
| --- | --- |
| Type | Block |
| Dimensions | 1920×1080 |
| Duration | 6s |
## Files
| File | Target | Type |
| --- | --- | --- |
| `code-diff.html` | `compositions/code-diff.html` | hyperframes:composition |
## Usage
After installing, add the block to your host composition:
```html
<div data-composition-id="code-diff" data-composition-src="compositions/code-diff.html" data-start="0" data-duration="6" data-track-index="1" data-width="1920" data-height="1080"></div>
```
+44
View File
@@ -0,0 +1,44 @@
---
title: "Code Highlight Sweep"
description: "A highlight band sweeps across a target line while the surrounding context dims — draws the eye to one line."
---
# Code Highlight Sweep
A highlight band sweeps across a target line while the surrounding context dims — draws the eye to one line.
`code` `code-animation` `highlight` `developer`
<video className="w-full aspect-video rounded-xl object-cover bg-zinc-100 dark:bg-zinc-800" src="https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/code-highlight.mp4" poster="https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/code-highlight.png" autoPlay muted loop playsInline />
## Install
<CodeGroup>
```bash Terminal
npx hyperframes add code-highlight
```
</CodeGroup>
## Details
| Property | Value |
| --- | --- |
| Type | Block |
| Dimensions | 1920×1080 |
| Duration | 5s |
## Files
| File | Target | Type |
| --- | --- | --- |
| `code-highlight.html` | `compositions/code-highlight.html` | hyperframes:composition |
## Usage
After installing, add the block to your host composition:
```html
<div data-composition-id="code-highlight" data-composition-src="compositions/code-highlight.html" data-start="0" data-duration="5" data-track-index="1" data-width="1920" data-height="1080"></div>
```
+44
View File
@@ -0,0 +1,44 @@
---
title: "Code Morph"
description: "One snippet transforms into another — tokens glide between positions, leavers fade out, enterers fade in. Shiki Magic Move re-driven as a paused GSAP timeline."
---
# Code Morph
One snippet transforms into another — tokens glide between positions, leavers fade out, enterers fade in. Shiki Magic Move re-driven as a paused GSAP timeline.
`code` `code-animation` `morph` `refactor` `developer`
<video className="w-full aspect-video rounded-xl object-cover bg-zinc-100 dark:bg-zinc-800" src="https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/code-morph.mp4" poster="https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/code-morph.png" autoPlay muted loop playsInline />
## Install
<CodeGroup>
```bash Terminal
npx hyperframes add code-morph
```
</CodeGroup>
## Details
| Property | Value |
| --- | --- |
| Type | Block |
| Dimensions | 1920×1080 |
| Duration | 7s |
## Files
| File | Target | Type |
| --- | --- | --- |
| `code-morph.html` | `compositions/code-morph.html` | hyperframes:composition |
## Usage
After installing, add the block to your host composition:
```html
<div data-composition-id="code-morph" data-composition-src="compositions/code-morph.html" data-start="0" data-duration="7" data-track-index="1" data-width="1920" data-height="1080"></div>
```
@@ -0,0 +1,44 @@
---
title: "Code Particle Assemble"
description: "Thousands of GPU points scatter through space and fly to the exact glyph pixels of the code, resolving into readable syntax-highlighted text — a particle system, not a token tween."
---
# Code Particle Assemble
Thousands of GPU points scatter through space and fly to the exact glyph pixels of the code, resolving into readable syntax-highlighted text — a particle system, not a token tween.
`code` `code-animation` `particles` `gpu` `webgl` `developer`
<video className="w-full aspect-video rounded-xl object-cover bg-zinc-100 dark:bg-zinc-800" src="https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/code-particle-assemble.mp4" poster="https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/code-particle-assemble.png" autoPlay muted loop playsInline />
## Install
<CodeGroup>
```bash Terminal
npx hyperframes add code-particle-assemble
```
</CodeGroup>
## Details
| Property | Value |
| --- | --- |
| Type | Block |
| Dimensions | 1920×1080 |
| Duration | 8s |
## Files
| File | Target | Type |
| --- | --- | --- |
| `code-particle-assemble.html` | `compositions/code-particle-assemble.html` | hyperframes:composition |
## Usage
After installing, add the block to your host composition:
```html
<div data-composition-id="code-particle-assemble" data-composition-src="compositions/code-particle-assemble.html" data-start="0" data-duration="8" data-track-index="1" data-width="1920" data-height="1080"></div>
```
+44
View File
@@ -0,0 +1,44 @@
---
title: "Code Scroll To Line"
description: "The camera scrolls a long file to bring a target line to center and spotlights it — for walking through real modules."
---
# Code Scroll To Line
The camera scrolls a long file to bring a target line to center and spotlights it — for walking through real modules.
`code` `code-animation` `scroll` `developer`
<video className="w-full aspect-video rounded-xl object-cover bg-zinc-100 dark:bg-zinc-800" src="https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/code-scroll.mp4" poster="https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/code-scroll.png" autoPlay muted loop playsInline />
## Install
<CodeGroup>
```bash Terminal
npx hyperframes add code-scroll
```
</CodeGroup>
## Details
| Property | Value |
| --- | --- |
| Type | Block |
| Dimensions | 1920×1080 |
| Duration | 6s |
## Files
| File | Target | Type |
| --- | --- | --- |
| `code-scroll.html` | `compositions/code-scroll.html` | hyperframes:composition |
## Usage
After installing, add the block to your host composition:
```html
<div data-composition-id="code-scroll" data-composition-src="compositions/code-scroll.html" data-start="0" data-duration="6" data-track-index="1" data-width="1920" data-height="1080"></div>
```
@@ -0,0 +1,44 @@
---
title: "Code Shader Dissolve"
description: "The code compiles into existence: a GPU fragment shader resolves it out of seeded noise with a chromatic dissolve front and edge glow, then holds crisp."
---
# Code Shader Dissolve
The code compiles into existence: a GPU fragment shader resolves it out of seeded noise with a chromatic dissolve front and edge glow, then holds crisp.
`code` `code-animation` `shader` `webgl` `developer`
<video className="w-full aspect-video rounded-xl object-cover bg-zinc-100 dark:bg-zinc-800" src="https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/code-shader-dissolve.mp4" poster="https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/code-shader-dissolve.png" autoPlay muted loop playsInline />
## Install
<CodeGroup>
```bash Terminal
npx hyperframes add code-shader-dissolve
```
</CodeGroup>
## Details
| Property | Value |
| --- | --- |
| Type | Block |
| Dimensions | 1920×1080 |
| Duration | 7s |
## Files
| File | Target | Type |
| --- | --- | --- |
| `code-shader-dissolve.html` | `compositions/code-shader-dissolve.html` | hyperframes:composition |
## Usage
After installing, add the block to your host composition:
```html
<div data-composition-id="code-shader-dissolve" data-composition-src="compositions/code-shader-dissolve.html" data-start="0" data-duration="7" data-track-index="1" data-width="1920" data-height="1080"></div>
```
@@ -0,0 +1,44 @@
---
title: "Code Snippet Flight"
description: "Discrete code snippets fly in from the side and assemble into a stacked program, staggered. Block-level FLIP."
---
# Code Snippet Flight
Discrete code snippets fly in from the side and assemble into a stacked program, staggered. Block-level FLIP.
`code` `code-animation` `flight` `developer`
<video className="w-full aspect-video rounded-xl object-cover bg-zinc-100 dark:bg-zinc-800" src="https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/code-snippet-flight.mp4" poster="https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/code-snippet-flight.png" autoPlay muted loop playsInline />
## Install
<CodeGroup>
```bash Terminal
npx hyperframes add code-snippet-flight
```
</CodeGroup>
## Details
| Property | Value |
| --- | --- |
| Type | Block |
| Dimensions | 1920×1080 |
| Duration | 6s |
## Files
| File | Target | Type |
| --- | --- | --- |
| `code-snippet-flight.html` | `compositions/code-snippet-flight.html` | hyperframes:composition |
## Usage
After installing, add the block to your host composition:
```html
<div data-composition-id="code-snippet-flight" data-composition-src="compositions/code-snippet-flight.html" data-start="0" data-duration="6" data-track-index="1" data-width="1920" data-height="1080"></div>
```
+44
View File
@@ -0,0 +1,44 @@
---
title: "Code Typing"
description: "Token-streamed typing reveal with a caret that tracks the frontier — deterministic, no CSS animation."
---
# Code Typing
Token-streamed typing reveal with a caret that tracks the frontier — deterministic, no CSS animation.
`code` `code-animation` `typing` `developer`
<video className="w-full aspect-video rounded-xl object-cover bg-zinc-100 dark:bg-zinc-800" src="https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/code-typing.mp4" poster="https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/code-typing.png" autoPlay muted loop playsInline />
## Install
<CodeGroup>
```bash Terminal
npx hyperframes add code-typing
```
</CodeGroup>
## Details
| Property | Value |
| --- | --- |
| Type | Block |
| Dimensions | 1920×1080 |
| Duration | 5s |
## Files
| File | Target | Type |
| --- | --- | --- |
| `code-typing.html` | `compositions/code-typing.html` | hyperframes:composition |
## Usage
After installing, add the block to your host composition:
```html
<div data-composition-id="code-typing" data-composition-src="compositions/code-typing.html" data-start="0" data-duration="5" data-track-index="1" data-width="1920" data-height="1080"></div>
```
+14
View File
@@ -112,6 +112,20 @@
{
"tab": "Catalog",
"groups": [
{
"group": "Code Animations",
"pages": [
"catalog/blocks/code-3d-extrude",
"catalog/blocks/code-shader-dissolve",
"catalog/blocks/code-particle-assemble",
"catalog/blocks/code-morph",
"catalog/blocks/code-snippet-flight",
"catalog/blocks/code-typing",
"catalog/blocks/code-diff",
"catalog/blocks/code-highlight",
"catalog/blocks/code-scroll"
]
},
{
"group": "Captions",
"pages": [
+4 -1
View File
@@ -182,7 +182,8 @@ export type BlockCategory =
| "scenes"
| "captions"
| "effects"
| "text-effects";
| "text-effects"
| "code-animation";
export interface BlockCategoryMeta {
id: BlockCategory;
@@ -192,6 +193,7 @@ export interface BlockCategoryMeta {
export const BLOCK_CATEGORIES: BlockCategoryMeta[] = [
{ id: "captions", label: "Captions", color: "cyan" },
{ id: "code-animation", label: "Code Animations", color: "emerald" },
{ id: "vfx", label: "VFX", color: "purple" },
{ id: "transitions", label: "Transitions", color: "blue" },
{ id: "effects", label: "Effects", color: "rose" },
@@ -205,6 +207,7 @@ export function resolveBlockCategory(tags: string[] | undefined): BlockCategory
if (!tags || tags.length === 0) return "scenes";
const set = new Set(tags);
if (set.has("captions") || set.has("caption-style")) return "captions";
if (set.has("code-animation")) return "code-animation";
if (set.has("transition")) return "transitions";
if (set.has("social") || set.has("overlay")) return "social";
if (set.has("data") || set.has("chart") || set.has("map")) return "data";
+2 -2
View File
@@ -1,11 +1,10 @@
import {
type BlockCategory,
type BlockCategoryMeta,
BLOCK_CATEGORIES,
resolveBlockCategory,
} from "@hyperframes/core/registry";
export type { BlockCategory, BlockCategoryMeta };
export type { BlockCategory };
export { BLOCK_CATEGORIES, resolveBlockCategory };
const COLOR_MAP: Record<BlockCategory, { bg: string; text: string; dot: string }> = {
@@ -17,6 +16,7 @@ const COLOR_MAP: Record<BlockCategory, { bg: string; text: string; dot: string }
captions: { bg: "bg-cyan-500/15", text: "text-cyan-400", dot: "bg-cyan-400" },
effects: { bg: "bg-rose-500/15", text: "text-rose-400", dot: "bg-rose-400" },
"text-effects": { bg: "bg-violet-500/15", text: "text-violet-400", dot: "bg-violet-400" },
"code-animation": { bg: "bg-emerald-500/15", text: "text-emerald-400", dot: "bg-emerald-400" },
};
export function getCategoryColors(category: BlockCategory) {
+613
View File
@@ -0,0 +1,613 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=1920, height=1080" />
<title>Code 3D Extrude</title>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/gsap.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/build/three.min.js"></script>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html,
body {
width: 1920px;
height: 1080px;
overflow: hidden;
background: #05070b;
}
#root {
position: relative;
width: 1920px;
height: 1080px;
}
#gl {
position: absolute;
inset: 0;
width: 1920px;
height: 1080px;
display: block;
}
</style>
</head>
<body>
<div
id="root"
data-composition-id="code-3d-extrude"
data-start="0"
data-duration="8"
data-width="1920"
data-height="1080"
>
<canvas id="gl" width="1920" height="1080"></canvas>
</div>
<script>
window.__TOKENS = {
feature: {
lang: "js",
theme: "github-dark",
bg: "#24292e",
fg: "#e1e4e8",
states: [
{
code: 'async function loadConfig(path) {\n const raw = await readFile(path, "utf8")\n const config = JSON.parse(raw)\n return validate(config)\n}',
tokens: [
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-0",
content: "async",
color: "#F97583",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-1",
content: " ",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-2",
content: "function",
color: "#F97583",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-3",
content: " ",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-4",
content: "loadConfig",
color: "#B392F0",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-5",
content: "(",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-6",
content: "path",
color: "#FFAB70",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-7",
content: ") {",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-8",
content: "\n",
color: "#e1e4e8",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-9",
content: " ",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-10",
content: "const",
color: "#F97583",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-11",
content: " ",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-12",
content: "raw",
color: "#79B8FF",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-13",
content: " ",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-14",
content: "=",
color: "#F97583",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-15",
content: " ",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-16",
content: "await",
color: "#F97583",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-17",
content: " ",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-18",
content: "readFile",
color: "#B392F0",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-19",
content: "(path,",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-20",
content: " ",
color: "#e1e4e8",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-21",
content: '"utf8"',
color: "#9ECBFF",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-22",
content: ")",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-23",
content: "\n",
color: "#e1e4e8",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-24",
content: " ",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-25",
content: "const",
color: "#F97583",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-26",
content: " ",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-27",
content: "config",
color: "#79B8FF",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-28",
content: " ",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-29",
content: "=",
color: "#F97583",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-30",
content: " ",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-31",
content: "JSON",
color: "#79B8FF",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-32",
content: ".",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-33",
content: "parse",
color: "#B392F0",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-34",
content: "(raw)",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-35",
content: "\n",
color: "#e1e4e8",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-36",
content: " ",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-37",
content: "return",
color: "#F97583",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-38",
content: " ",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-39",
content: "validate",
color: "#B392F0",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-40",
content: "(config)",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-41",
content: "\n",
color: "#e1e4e8",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-42",
content: "}",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-43",
content: "\n",
color: "#e1e4e8",
fontStyle: 0,
},
],
},
],
},
};
window.__BLOCK = {
id: "code-3d-extrude",
effect: "extrude",
seq: "feature",
duration: 8,
seed: 7,
};
</script>
<script>
// hf-code-anim-gl.js — WebGL code-animation engine for the "crazy" Code Animations blocks.
//
// Bridges the baked Shiki tokens (window.__TOKENS) into Three.js: the tokens are drawn once
// to an offscreen 2D canvas (a CanvasTexture), which then drives a lit 3D slab, a dissolve
// shader, or a GPU particle system. Determinism per the project contract:
// - render only via tl.eventCallback("onUpdate", …) + one initial render() — never rAF
// - all randomness from a seeded mulberry32 — never Math.random / Date.now
// - canvas drawn once after document.fonts.ready (final glyph metrics)
// - paused GSAP timeline registered on window.__timelines[id]; HF seeks it per frame
//
// Each block sets window.__BLOCK = { id, effect, seq, duration, seed } and includes this engine.
(function () {
"use strict";
var T = window.__TOKENS || {};
// ---- seeded PRNG (no Math.random) -----------------------------------------
function mulberry32(a) {
return function () {
a |= 0;
a = (a + 0x6d2b79f5) | 0;
var t = Math.imul(a ^ (a >>> 15), 1 | a);
t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
};
}
// ---- split a token state into lines of {content,color} --------------------
function splitLines(state) {
var lines = [[]];
for (var i = 0; i < state.tokens.length; i++) {
var t = state.tokens[i];
if (t.content === "\n") {
lines.push([]);
continue;
}
lines[lines.length - 1].push(t);
}
while (lines.length && lines[lines.length - 1].length === 0) lines.pop();
return lines;
}
// ---- THE BRIDGE: draw baked tokens to an offscreen 2D canvas ---------------
// Returns { canvas, W, H, lines, lineH, padY, fontPx } so effects can sample it.
function drawCodeToCanvas(state, opts) {
opts = opts || {};
var fontPx = opts.fontPx || 36;
var lineH = opts.lineH || 54;
var padX = opts.padX || 70;
var padY = opts.padY || 60;
var weight = opts.weight || 500;
var font = weight + " " + fontPx + 'px "JetBrains Mono", monospace';
var lines = splitLines(state);
var measure = document.createElement("canvas").getContext("2d");
measure.font = font;
var maxW = 0;
for (var i = 0; i < lines.length; i++) {
var w = 0;
for (var j = 0; j < lines[i].length; j++)
w += measure.measureText(lines[i][j].content).width;
if (w > maxW) maxW = w;
}
var W = Math.max(2, Math.ceil(maxW + padX * 2));
var H = Math.max(2, Math.ceil(lines.length * lineH + padY * 2));
var canvas = document.createElement("canvas");
canvas.width = W;
canvas.height = H;
var ctx = canvas.getContext("2d");
if (opts.transparent) {
ctx.clearRect(0, 0, W, H);
} else {
ctx.fillStyle = opts.bg || "#0b0f17";
ctx.fillRect(0, 0, W, H);
}
ctx.textBaseline = "top";
ctx.font = font;
for (var li = 0; li < lines.length; li++) {
var x = padX;
var y = padY + li * lineH;
for (var k = 0; k < lines[li].length; k++) {
var tok = lines[li][k];
ctx.fillStyle = tok.color;
ctx.fillText(tok.content, x, y);
x += ctx.measureText(tok.content).width;
}
}
return {
canvas: canvas,
W: W,
H: H,
lines: lines.length,
lineH: lineH,
padY: padY,
fontPx: fontPx,
};
}
function fxExtrude(THREE, scene, camera, renderer, state, st, tl, dur) {
scene.background = new THREE.Color(0x05070b);
var code = drawCodeToCanvas(state, {
fontPx: 38,
lineH: 56,
padX: 80,
padY: 70,
weight: 600,
});
var tex = new THREE.CanvasTexture(code.canvas);
tex.minFilter = THREE.LinearFilter;
tex.magFilter = THREE.LinearFilter;
tex.generateMipmaps = false;
var aspect = code.W / code.H;
var slabH = 6.2;
var slabW = slabH * aspect;
var depth = 0.5;
// beveled slab: rounded extruded rectangle
var hw = slabW / 2;
var hh = slabH / 2;
var r = 0.18;
var shape = new THREE.Shape();
shape.moveTo(-hw + r, -hh);
shape.lineTo(hw - r, -hh);
shape.quadraticCurveTo(hw, -hh, hw, -hh + r);
shape.lineTo(hw, hh - r);
shape.quadraticCurveTo(hw, hh, hw - r, hh);
shape.lineTo(-hw + r, hh);
shape.quadraticCurveTo(-hw, hh, -hw, hh - r);
shape.lineTo(-hw, -hh + r);
shape.quadraticCurveTo(-hw, -hh, -hw + r, -hh);
var geo = new THREE.ExtrudeGeometry(shape, {
depth: depth,
bevelEnabled: true,
bevelThickness: 0.12,
bevelSize: 0.1,
bevelSegments: 3,
steps: 1,
});
geo.center();
// UVs from ExtrudeGeometry don't map the texture nicely to the face; remap front-face UVs from XY bounds.
geo.computeBoundingBox();
var bb = geo.boundingBox;
var sizeX = bb.max.x - bb.min.x;
var sizeY = bb.max.y - bb.min.y;
var pos = geo.attributes.position;
var uv = geo.attributes.uv;
for (var vi = 0; vi < pos.count; vi++) {
uv.setXY(vi, (pos.getX(vi) - bb.min.x) / sizeX, (pos.getY(vi) - bb.min.y) / sizeY);
}
uv.needsUpdate = true;
// Face is an unlit "screen" — shows the exact vivid Shiki colors (no PBR/emissive wash).
// The 3D read comes from the lit, beveled EDGES + perspective, like a glowing monitor.
var faceMat = new THREE.MeshBasicMaterial({ map: tex });
var edgeMat = new THREE.MeshStandardMaterial({
color: 0x141d2b,
roughness: 0.3,
metalness: 0.4,
});
// ExtrudeGeometry groups: 0 = front+back faces, 1 = sides. Front shows the code.
var slab = new THREE.Mesh(geo, [faceMat, edgeMat]);
scene.add(slab);
scene.add(new THREE.AmbientLight(0xffffff, 0.5));
var key = new THREE.DirectionalLight(0xbcd4ff, 1.25);
key.position.set(5, 7, 9);
scene.add(key);
var rim = new THREE.DirectionalLight(0x4fffa0, 0.45);
rim.position.set(-7, -3, 5);
scene.add(rim);
camera.position.set(0, 0, 10);
// fit camera distance so the WHOLE slab fits (width-limited for wide code), ~78% fill
var vfov = (camera.fov * Math.PI) / 180;
var screenAspect = 1920 / 1080;
var fitH = slabH / 2 / Math.tan(vfov / 2) / 0.78;
var fitW = slabW / 2 / (Math.tan(vfov / 2) * screenAspect) / 0.78;
var fitZ = Math.max(fitH, fitW);
st.rotY = -1.0;
st.rotX = 0.4;
st.camZ = fitZ + 6;
tl.to(st, { rotY: 0, rotX: 0, camZ: fitZ, duration: dur * 0.6, ease: "power3.out" }, 0);
// gentle breathing drift at rest (subtle — stays readable)
tl.to(
st,
{ rotY: 0.06, rotX: -0.02, duration: dur * 0.4, ease: "sine.inOut" },
dur * 0.6,
);
return function () {
slab.rotation.y = st.rotY;
slab.rotation.x = st.rotX;
camera.position.z = st.camZ;
camera.lookAt(0, 0, 0);
// boot loop's render() issues the single renderer.render() — don't double-render here
};
}
// ---- dispatch + boot ------------------------------------------------------
function buildEffect(THREE, scene, camera, renderer, spec, st, tl) {
var state = T[spec.seq].states[0];
var dur = spec.duration;
if (spec.effect === "extrude")
return fxExtrude(THREE, scene, camera, renderer, state, st, tl, dur);
return function () {};
}
// Free GPU resources (renderer, geometries, materials, textures) on page teardown.
// One-shot renders exit per job so nothing leaks there; this guards long-lived
// Studio/player sessions that reload. Fires only on unload — never during capture.
function disposeGL(renderer, scene) {
scene.traverse(function (o) {
if (o.geometry && o.geometry.dispose) o.geometry.dispose();
var mats = o.material ? (Array.isArray(o.material) ? o.material : [o.material]) : [];
mats.forEach(function (m) {
if (m.map && m.map.dispose) m.map.dispose();
if (
m.uniforms &&
m.uniforms.uTex &&
m.uniforms.uTex.value &&
m.uniforms.uTex.value.dispose
) {
m.uniforms.uTex.value.dispose();
}
if (m.dispose) m.dispose();
});
});
if (renderer.dispose) renderer.dispose();
}
function go() {
var spec = window.__BLOCK;
var root = document.getElementById("root");
var glcanvas = document.getElementById("gl");
var W = 1920;
var H = 1080;
window.__timelines = window.__timelines || {};
var tl = gsap.timeline({ paused: true });
window.__timelines[spec.id] = tl; // register synchronously
var THREE = window.THREE;
var renderer = new THREE.WebGLRenderer({
canvas: glcanvas,
antialias: true,
alpha: false,
preserveDrawingBuffer: true,
});
renderer.setPixelRatio(1);
renderer.setSize(W, H, false);
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(45, W / H, 0.1, 200);
var st = {};
document.fonts.ready.then(function () {
var renderFn = buildEffect(THREE, scene, camera, renderer, spec, st, tl);
function render() {
renderFn();
renderer.render(scene, camera);
}
tl.eventCallback("onUpdate", render);
var dur = parseFloat(root.dataset.duration) || tl.duration();
tl.to({}, { duration: dur }, 0); // pad to full composition length
render(); // paint frame 0
window.addEventListener("beforeunload", function () {
disposeGL(renderer, scene);
});
});
}
if (document.readyState === "loading") document.addEventListener("DOMContentLoaded", go);
else go();
})();
</script>
</body>
</html>
@@ -0,0 +1,24 @@
{
"$schema": "https://hyperframes.heygen.com/schema/registry-item.json",
"name": "code-3d-extrude",
"type": "hyperframes:block",
"title": "Code 3D Extrude",
"description": "Syntax-highlighted code on a lit, beveled 3D slab that rotates through real space and settles to a readable rest — true WebGL depth and lighting, not a 2D transform.",
"dimensions": {
"width": 1920,
"height": 1080
},
"duration": 8,
"tags": ["code", "code-animation", "3d", "webgl", "developer"],
"files": [
{
"path": "code-3d-extrude.html",
"target": "compositions/code-3d-extrude.html",
"type": "hyperframes:composition"
}
],
"preview": {
"video": "https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/code-3d-extrude.mp4",
"poster": "https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/code-3d-extrude.png"
}
}
+807
View File
@@ -0,0 +1,807 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=1920, height=1080" />
<title>Code Diff</title>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/gsap.min.js"></script>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html,
body {
width: 1920px;
height: 1080px;
overflow: hidden;
background: #05070b;
}
#root {
position: relative;
width: 1920px;
height: 1080px;
font-family: "JetBrains Mono", monospace;
color: #e6edf3;
}
.bg-field {
position: absolute;
inset: 0;
background: radial-gradient(1200px 700px at 50% 18%, #0e1726 0%, #070b12 55%, #05070b 100%);
}
.bg-grid {
position: absolute;
inset: 0;
background-image:
linear-gradient(rgba(88, 166, 255, 0.05) 1px, transparent 1px),
linear-gradient(90deg, rgba(88, 166, 255, 0.05) 1px, transparent 1px);
background-size: 64px 64px;
mask-image: radial-gradient(1100px 620px at 50% 42%, #000 0%, transparent 78%);
}
.bg-glow {
position: absolute;
width: 900px;
height: 900px;
border-radius: 50%;
filter: blur(120px);
opacity: 0.5;
}
.bg-glow.a {
background: #1f6feb55;
left: -180px;
top: -200px;
}
.bg-glow.b {
background: #2ea04355;
right: -200px;
bottom: -260px;
}
.editor {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
width: 1380px;
height: 800px;
background: #0b0f17;
border: 1px solid #1d2733;
border-radius: 16px;
box-shadow:
0 40px 120px rgba(0, 0, 0, 0.6),
0 2px 0 rgba(255, 255, 255, 0.03) inset;
overflow: hidden;
display: flex;
flex-direction: column;
}
.titlebar {
height: 56px;
flex: 0 0 56px;
display: flex;
align-items: center;
gap: 16px;
padding: 0 22px;
background: linear-gradient(#11161f, #0c111a);
border-bottom: 1px solid #1b2430;
}
.dots {
display: flex;
gap: 10px;
}
.dot {
width: 14px;
height: 14px;
border-radius: 50%;
}
.dot.r {
background: #ff5f57;
}
.dot.y {
background: #febc2e;
}
.dot.g {
background: #28c840;
}
.filename {
font-size: 19px;
color: #8b98a9;
letter-spacing: 0.2px;
}
.filename .accent {
color: #d6e2f0;
}
.code-surface {
position: relative;
flex: 1 1 auto;
overflow: hidden;
}
.scene {
position: absolute;
inset: 0;
opacity: 0;
will-change: opacity;
}
.code {
position: absolute;
top: 38px;
left: 0;
right: 0;
padding: 0 44px 0 104px;
font-size: 30px;
line-height: 46px;
font-variant-ligatures: none;
tab-size: 2;
}
.gutter {
position: absolute;
top: 38px;
left: 0;
width: 84px;
padding-right: 18px;
font-size: 30px;
line-height: 46px;
text-align: right;
color: #828c9b;
user-select: none;
}
.ln {
display: block;
height: 46px;
}
.line {
display: block;
min-height: 46px;
white-space: pre;
position: relative;
z-index: 1;
}
.tok {
display: inline-block;
white-space: pre;
will-change: transform, opacity;
}
.caret {
position: absolute;
top: 0;
left: 0;
width: 3px;
height: 32px;
margin-top: 6px;
background: #58a6ff;
border-radius: 1px;
z-index: 3;
}
.hl-box {
position: absolute;
background: rgba(88, 166, 255, 0.16);
border-left: 3px solid #58a6ff;
border-radius: 6px;
z-index: 0;
overflow: hidden;
}
.diff-line {
display: block;
min-height: 0;
line-height: 46px;
white-space: pre;
position: relative;
overflow: hidden;
padding-left: 14px;
}
.diff-sign {
display: inline-block;
width: 22px;
color: #41506220;
}
.diff-del {
background: rgba(248, 81, 73, 0.12);
box-shadow: inset 3px 0 #f85149;
}
.diff-add {
background: rgba(63, 185, 80, 0.12);
box-shadow: inset 3px 0 #3fb950;
}
.diff-del .diff-sign {
color: #f85149;
}
.diff-add .diff-sign {
color: #3fb950;
}
.flight-block {
position: absolute;
white-space: pre;
font-size: 27px;
line-height: 40px;
padding: 11px 22px;
border-radius: 11px;
background: #0f1722;
border: 1px solid #233044;
box-shadow: 0 16px 44px rgba(0, 0, 0, 0.5);
will-change: transform, opacity;
}
.flight-target {
position: absolute;
border: 1.5px dashed #2a3a4f;
border-radius: 14px;
opacity: 0;
}
</style>
</head>
<body>
<div
id="root"
data-composition-id="code-diff"
data-start="0"
data-duration="6"
data-width="1920"
data-height="1080"
>
<div class="bg-field" data-layout-ignore></div>
<div class="bg-grid" data-layout-ignore></div>
<div class="bg-glow a" data-layout-ignore></div>
<div class="bg-glow b" data-layout-ignore></div>
<div class="editor" id="editor" data-layout-ignore>
<div class="titlebar">
<div class="dots">
<span class="dot r"></span><span class="dot y"></span><span class="dot g"></span>
</div>
<span class="filename"><span class="accent">greet.js</span> — Code Diff</span>
</div>
<div class="code-surface" id="surface"></div>
</div>
</div>
<script>
window.__TOKENS = {
diff: {
lang: "js",
theme: "github-dark",
bg: "#24292e",
fg: "#e1e4e8",
states: [
{
code: 'function greet(name) {\n console.log("hi " + name)\n}',
tokens: [
{
key: "97wperY-uI8_OA5W5UX94Q5keKEoIobaKXQ6V8Tcd5I-0",
content: "function",
color: "#F97583",
fontStyle: 0,
},
{
key: "97wperY-uI8_OA5W5UX94Q5keKEoIobaKXQ6V8Tcd5I-1",
content: " ",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "97wperY-uI8_OA5W5UX94Q5keKEoIobaKXQ6V8Tcd5I-2",
content: "greet",
color: "#B392F0",
fontStyle: 0,
},
{
key: "97wperY-uI8_OA5W5UX94Q5keKEoIobaKXQ6V8Tcd5I-3",
content: "(",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "97wperY-uI8_OA5W5UX94Q5keKEoIobaKXQ6V8Tcd5I-4",
content: "name",
color: "#FFAB70",
fontStyle: 0,
},
{
key: "97wperY-uI8_OA5W5UX94Q5keKEoIobaKXQ6V8Tcd5I-5",
content: ") {",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "97wperY-uI8_OA5W5UX94Q5keKEoIobaKXQ6V8Tcd5I-6",
content: "\n",
color: "#e1e4e8",
fontStyle: 0,
},
{
key: "97wperY-uI8_OA5W5UX94Q5keKEoIobaKXQ6V8Tcd5I-7",
content: " ",
color: "#e1e4e8",
fontStyle: 0,
},
{
key: "97wperY-uI8_OA5W5UX94Q5keKEoIobaKXQ6V8Tcd5I-8",
content: "console.",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "97wperY-uI8_OA5W5UX94Q5keKEoIobaKXQ6V8Tcd5I-9",
content: "log",
color: "#B392F0",
fontStyle: 0,
},
{
key: "97wperY-uI8_OA5W5UX94Q5keKEoIobaKXQ6V8Tcd5I-10",
content: "(",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "97wperY-uI8_OA5W5UX94Q5keKEoIobaKXQ6V8Tcd5I-11",
content: '"hi "',
color: "#9ECBFF",
fontStyle: 0,
},
{
key: "97wperY-uI8_OA5W5UX94Q5keKEoIobaKXQ6V8Tcd5I-12",
content: " ",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "97wperY-uI8_OA5W5UX94Q5keKEoIobaKXQ6V8Tcd5I-13",
content: "+",
color: "#F97583",
fontStyle: 0,
},
{
key: "97wperY-uI8_OA5W5UX94Q5keKEoIobaKXQ6V8Tcd5I-14",
content: " ",
color: "#e1e4e8",
fontStyle: 0,
},
{
key: "97wperY-uI8_OA5W5UX94Q5keKEoIobaKXQ6V8Tcd5I-15",
content: "name)",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "97wperY-uI8_OA5W5UX94Q5keKEoIobaKXQ6V8Tcd5I-16",
content: "\n",
color: "#e1e4e8",
fontStyle: 0,
},
{
key: "97wperY-uI8_OA5W5UX94Q5keKEoIobaKXQ6V8Tcd5I-17",
content: "}",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "97wperY-uI8_OA5W5UX94Q5keKEoIobaKXQ6V8Tcd5I-18",
content: "\n",
color: "#e1e4e8",
fontStyle: 0,
},
],
},
{
code: 'function greet(name, lang) {\n const msg = translate("hi", lang)\n console.log(`${msg} ${name}`)\n}',
tokens: [
{
key: "97wperY-uI8_OA5W5UX94Q5keKEoIobaKXQ6V8Tcd5I-0",
content: "function",
color: "#F97583",
fontStyle: 0,
},
{
key: "97wperY-uI8_OA5W5UX94Q5keKEoIobaKXQ6V8Tcd5I-1",
content: " ",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "97wperY-uI8_OA5W5UX94Q5keKEoIobaKXQ6V8Tcd5I-2",
content: "greet",
color: "#B392F0",
fontStyle: 0,
},
{
key: "97wperY-uI8_OA5W5UX94Q5keKEoIobaKXQ6V8Tcd5I-3",
content: "(",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "97wperY-uI8_OA5W5UX94Q5keKEoIobaKXQ6V8Tcd5I-4",
content: "name",
color: "#FFAB70",
fontStyle: 0,
},
{
key: "w2rmoAdPXgC70S4pDXgHIIwz8Jqq0QKYf1B6jvTMUJ8-5",
content: ",",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "w2rmoAdPXgC70S4pDXgHIIwz8Jqq0QKYf1B6jvTMUJ8-6",
content: " ",
color: "#e1e4e8",
fontStyle: 0,
},
{
key: "w2rmoAdPXgC70S4pDXgHIIwz8Jqq0QKYf1B6jvTMUJ8-7",
content: "lang",
color: "#FFAB70",
fontStyle: 0,
},
{
key: "97wperY-uI8_OA5W5UX94Q5keKEoIobaKXQ6V8Tcd5I-5",
content: ") {",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "97wperY-uI8_OA5W5UX94Q5keKEoIobaKXQ6V8Tcd5I-6",
content: "\n",
color: "#e1e4e8",
fontStyle: 0,
},
{
key: "97wperY-uI8_OA5W5UX94Q5keKEoIobaKXQ6V8Tcd5I-7",
content: " ",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "w2rmoAdPXgC70S4pDXgHIIwz8Jqq0QKYf1B6jvTMUJ8-11",
content: "const",
color: "#F97583",
fontStyle: 0,
},
{
key: "w2rmoAdPXgC70S4pDXgHIIwz8Jqq0QKYf1B6jvTMUJ8-12",
content: " ",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "w2rmoAdPXgC70S4pDXgHIIwz8Jqq0QKYf1B6jvTMUJ8-13",
content: "msg",
color: "#79B8FF",
fontStyle: 0,
},
{
key: "w2rmoAdPXgC70S4pDXgHIIwz8Jqq0QKYf1B6jvTMUJ8-14",
content: " ",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "w2rmoAdPXgC70S4pDXgHIIwz8Jqq0QKYf1B6jvTMUJ8-15",
content: "=",
color: "#F97583",
fontStyle: 0,
},
{
key: "w2rmoAdPXgC70S4pDXgHIIwz8Jqq0QKYf1B6jvTMUJ8-16",
content: " ",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "w2rmoAdPXgC70S4pDXgHIIwz8Jqq0QKYf1B6jvTMUJ8-17",
content: "translate",
color: "#B392F0",
fontStyle: 0,
},
{
key: "97wperY-uI8_OA5W5UX94Q5keKEoIobaKXQ6V8Tcd5I-10",
content: "(",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "w2rmoAdPXgC70S4pDXgHIIwz8Jqq0QKYf1B6jvTMUJ8-19",
content: '"hi"',
color: "#9ECBFF",
fontStyle: 0,
},
{
key: "w2rmoAdPXgC70S4pDXgHIIwz8Jqq0QKYf1B6jvTMUJ8-20",
content: ", lang)",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "w2rmoAdPXgC70S4pDXgHIIwz8Jqq0QKYf1B6jvTMUJ8-21",
content: "\n",
color: "#e1e4e8",
fontStyle: 0,
},
{
key: "w2rmoAdPXgC70S4pDXgHIIwz8Jqq0QKYf1B6jvTMUJ8-22",
content: " ",
color: "#e1e4e8",
fontStyle: 0,
},
{
key: "w2rmoAdPXgC70S4pDXgHIIwz8Jqq0QKYf1B6jvTMUJ8-23",
content: "console.",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "97wperY-uI8_OA5W5UX94Q5keKEoIobaKXQ6V8Tcd5I-9",
content: "log",
color: "#B392F0",
fontStyle: 0,
},
{
key: "w2rmoAdPXgC70S4pDXgHIIwz8Jqq0QKYf1B6jvTMUJ8-25",
content: "(",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "w2rmoAdPXgC70S4pDXgHIIwz8Jqq0QKYf1B6jvTMUJ8-26",
content: "`${",
color: "#9ECBFF",
fontStyle: 0,
},
{
key: "w2rmoAdPXgC70S4pDXgHIIwz8Jqq0QKYf1B6jvTMUJ8-27",
content: "msg",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "w2rmoAdPXgC70S4pDXgHIIwz8Jqq0QKYf1B6jvTMUJ8-28",
content: "} ${",
color: "#9ECBFF",
fontStyle: 0,
},
{
key: "w2rmoAdPXgC70S4pDXgHIIwz8Jqq0QKYf1B6jvTMUJ8-29",
content: "name",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "w2rmoAdPXgC70S4pDXgHIIwz8Jqq0QKYf1B6jvTMUJ8-30",
content: "}`",
color: "#9ECBFF",
fontStyle: 0,
},
{
key: "w2rmoAdPXgC70S4pDXgHIIwz8Jqq0QKYf1B6jvTMUJ8-31",
content: ")",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "97wperY-uI8_OA5W5UX94Q5keKEoIobaKXQ6V8Tcd5I-16",
content: "\n",
color: "#e1e4e8",
fontStyle: 0,
},
{
key: "97wperY-uI8_OA5W5UX94Q5keKEoIobaKXQ6V8Tcd5I-17",
content: "}",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "w2rmoAdPXgC70S4pDXgHIIwz8Jqq0QKYf1B6jvTMUJ8-34",
content: "\n",
color: "#e1e4e8",
fontStyle: 0,
},
],
},
],
},
};
window.__BLOCK = { id: "code-diff", effect: "diff", seq: "diff", duration: 6 };
</script>
<script>
// hf-code-anim.js — self-contained code-animation engine for HyperFrames registry blocks.
//
// Reuses Shiki Magic Move's baked output (window.__TOKENS, baked at author time) and
// drives the animation as a PAUSED GSAP timeline the HyperFrames engine seeks per frame —
// NOT CSS transitions (those break under non-sequential frame seeking). FLIP measurement
// runs once inside document.fonts.ready (glyph metrics must be final). Each block sets
// window.__BLOCK = { id, effect, seq, line?, duration } and includes this engine; the
// engine builds one scene that fills the composition and registers window.__timelines[id].
(function () {
"use strict";
var T = window.__TOKENS || {};
function lineDiv() {
var d = document.createElement("div");
d.className = "line";
return d;
}
// Render a code state's keyed tokens into codeEl as lines of inline-block .tok spans.
function renderState(codeEl, state) {
codeEl.textContent = "";
var keyToEl = new Map();
var line = lineDiv();
codeEl.appendChild(line);
for (var i = 0; i < state.tokens.length; i++) {
var t = state.tokens[i];
if (t.content === "\n") {
line = lineDiv();
codeEl.appendChild(line);
continue;
}
var span = document.createElement("span");
span.className = "tok";
span.textContent = t.content;
span.style.color = t.color;
if (t.fontStyle & 1) span.style.fontStyle = "italic";
if (t.fontStyle & 2) span.style.fontWeight = "700";
line.appendChild(span);
keyToEl.set(t.key, span);
}
var last = codeEl.lastChild;
if (last && last.className === "line" && last.children.length === 0)
codeEl.removeChild(last);
return keyToEl;
}
function rectsOf(keyToEl) {
var m = new Map();
keyToEl.forEach(function (el, k) {
var r = el.getBoundingClientRect();
m.set(k, { left: r.left, top: r.top });
});
return m;
}
function makeScene(surface, withGutter) {
var scene = document.createElement("div");
scene.className = "scene";
if (withGutter) {
var g = document.createElement("div");
g.className = "gutter";
scene.appendChild(g);
}
var code = document.createElement("div");
code.className = "code";
scene.appendChild(code);
surface.appendChild(scene);
return { scene: scene, code: code, gutter: scene.querySelector(".gutter") };
}
function fillGutter(g, n) {
var html = "";
for (var i = 1; i <= n; i++) html += '<span class="ln">' + i + "</span>";
g.innerHTML = html;
}
function splitLines(state) {
var lines = [{ text: "", tokens: [] }];
for (var i = 0; i < state.tokens.length; i++) {
var t = state.tokens[i];
if (t.content === "\n") {
lines.push({ text: "", tokens: [] });
continue;
}
var cur = lines[lines.length - 1];
cur.tokens.push(t);
cur.text += t.content;
}
if (lines.length && lines[lines.length - 1].tokens.length === 0) lines.pop();
return lines;
}
function lineDiff(a, b) {
var n = a.length;
var m = b.length;
var dp = [];
for (var i = 0; i <= n; i++) dp.push(new Array(m + 1).fill(0));
for (var i = n - 1; i >= 0; i--) {
for (var j = m - 1; j >= 0; j--) {
dp[i][j] =
a[i].text === b[j].text
? dp[i + 1][j + 1] + 1
: Math.max(dp[i + 1][j], dp[i][j + 1]);
}
}
var ops = [];
var i = 0;
var j = 0;
while (i < n && j < m) {
if (a[i].text === b[j].text) {
ops.push({ type: "same", line: b[j] });
i++;
j++;
} else if (dp[i + 1][j] >= dp[i][j + 1]) {
ops.push({ type: "del", line: a[i] });
i++;
} else {
ops.push({ type: "add", line: b[j] });
j++;
}
}
while (i < n) ops.push({ type: "del", line: a[i++] });
while (j < m) ops.push({ type: "add", line: b[j++] });
return ops;
}
// ---- single-scene effect builders (fade in, play, hold to end) -------------
var FADE = 0.45;
function diff(tl, surface, states, start) {
var DEL = 0.55;
var ADD = 0.6;
var ops = lineDiff(splitLines(states[0]), splitLines(states[1]));
var parts = makeScene(surface, false);
var code = parts.code;
var delEls = [];
var addEls = [];
ops.forEach(function (op) {
var ln = document.createElement("div");
ln.className =
"diff-line" +
(op.type === "del" ? " diff-del" : op.type === "add" ? " diff-add" : "");
var sign = document.createElement("span");
sign.className = "diff-sign";
sign.textContent = op.type === "del" ? "- " : op.type === "add" ? "+ " : " ";
ln.appendChild(sign);
op.line.tokens.forEach(function (t) {
var s = document.createElement("span");
s.className = "tok";
s.textContent = t.content;
s.style.color = t.color;
ln.appendChild(s);
});
code.appendChild(ln);
if (op.type === "del") delEls.push(ln);
if (op.type === "add") addEls.push(ln);
});
tl.set(parts.scene, { opacity: 0 }, 0);
tl.to(parts.scene, { opacity: 1, duration: FADE, ease: "power1.out" }, start);
var atDel = start + FADE + 0.3;
delEls.forEach(function (el, k) {
tl.fromTo(
el,
{ height: 46, opacity: 1 },
{ height: 0, opacity: 0, duration: DEL, ease: "power2.inOut" },
atDel + k * 0.08,
);
});
var atAdd = atDel + DEL + 0.15;
addEls.forEach(function (el, k) {
tl.set(el, { height: 0, opacity: 0 }, 0);
tl.fromTo(
el,
{ height: 0, opacity: 0 },
{ height: 46, opacity: 1, duration: ADD, ease: "power2.out" },
atAdd + k * 0.12,
);
});
}
function buildEffect(tl, surface, spec) {
var start = 0.45;
var seq = T[spec.seq];
if (spec.effect === "diff") diff(tl, surface, seq.states, start);
}
function go() {
var spec = window.__BLOCK;
var surface = document.getElementById("surface");
var root = document.getElementById("root");
window.__timelines = window.__timelines || {};
var tl = gsap.timeline({ paused: true });
window.__timelines["code-diff"] = tl; // register synchronously
document.fonts.ready.then(function () {
tl.from("#editor", { opacity: 0, scale: 0.985, duration: 0.5, ease: "power2.out" }, 0);
buildEffect(tl, surface, spec);
var dur = parseFloat(root.dataset.duration) || tl.duration();
tl.to({}, { duration: dur }, 0); // pad to full composition length
});
}
if (document.readyState === "loading") document.addEventListener("DOMContentLoaded", go);
else go();
})();
</script>
</body>
</html>
@@ -0,0 +1,24 @@
{
"$schema": "https://hyperframes.heygen.com/schema/registry-item.json",
"name": "code-diff",
"type": "hyperframes:block",
"title": "Code Diff",
"description": "An edit shown as a colored diff — removed lines collapse in red, added lines expand in green.",
"dimensions": {
"width": 1920,
"height": 1080
},
"duration": 6,
"tags": ["code", "code-animation", "diff", "developer"],
"files": [
{
"path": "code-diff.html",
"target": "compositions/code-diff.html",
"type": "hyperframes:composition"
}
],
"preview": {
"video": "https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/code-diff.mp4",
"poster": "https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/code-diff.png"
}
}
+736
View File
@@ -0,0 +1,736 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=1920, height=1080" />
<title>Code Highlight Sweep</title>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/gsap.min.js"></script>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html,
body {
width: 1920px;
height: 1080px;
overflow: hidden;
background: #05070b;
}
#root {
position: relative;
width: 1920px;
height: 1080px;
font-family: "JetBrains Mono", monospace;
color: #e6edf3;
}
.bg-field {
position: absolute;
inset: 0;
background: radial-gradient(1200px 700px at 50% 18%, #0e1726 0%, #070b12 55%, #05070b 100%);
}
.bg-grid {
position: absolute;
inset: 0;
background-image:
linear-gradient(rgba(88, 166, 255, 0.05) 1px, transparent 1px),
linear-gradient(90deg, rgba(88, 166, 255, 0.05) 1px, transparent 1px);
background-size: 64px 64px;
mask-image: radial-gradient(1100px 620px at 50% 42%, #000 0%, transparent 78%);
}
.bg-glow {
position: absolute;
width: 900px;
height: 900px;
border-radius: 50%;
filter: blur(120px);
opacity: 0.5;
}
.bg-glow.a {
background: #1f6feb55;
left: -180px;
top: -200px;
}
.bg-glow.b {
background: #2ea04355;
right: -200px;
bottom: -260px;
}
.editor {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
width: 1380px;
height: 800px;
background: #0b0f17;
border: 1px solid #1d2733;
border-radius: 16px;
box-shadow:
0 40px 120px rgba(0, 0, 0, 0.6),
0 2px 0 rgba(255, 255, 255, 0.03) inset;
overflow: hidden;
display: flex;
flex-direction: column;
}
.titlebar {
height: 56px;
flex: 0 0 56px;
display: flex;
align-items: center;
gap: 16px;
padding: 0 22px;
background: linear-gradient(#11161f, #0c111a);
border-bottom: 1px solid #1b2430;
}
.dots {
display: flex;
gap: 10px;
}
.dot {
width: 14px;
height: 14px;
border-radius: 50%;
}
.dot.r {
background: #ff5f57;
}
.dot.y {
background: #febc2e;
}
.dot.g {
background: #28c840;
}
.filename {
font-size: 19px;
color: #8b98a9;
letter-spacing: 0.2px;
}
.filename .accent {
color: #d6e2f0;
}
.code-surface {
position: relative;
flex: 1 1 auto;
overflow: hidden;
}
.scene {
position: absolute;
inset: 0;
opacity: 0;
will-change: opacity;
}
.code {
position: absolute;
top: 38px;
left: 0;
right: 0;
padding: 0 44px 0 104px;
font-size: 30px;
line-height: 46px;
font-variant-ligatures: none;
tab-size: 2;
}
.gutter {
position: absolute;
top: 38px;
left: 0;
width: 84px;
padding-right: 18px;
font-size: 30px;
line-height: 46px;
text-align: right;
color: #828c9b;
user-select: none;
}
.ln {
display: block;
height: 46px;
}
.line {
display: block;
min-height: 46px;
white-space: pre;
position: relative;
z-index: 1;
}
.tok {
display: inline-block;
white-space: pre;
will-change: transform, opacity;
}
.caret {
position: absolute;
top: 0;
left: 0;
width: 3px;
height: 32px;
margin-top: 6px;
background: #58a6ff;
border-radius: 1px;
z-index: 3;
}
.hl-box {
position: absolute;
background: rgba(88, 166, 255, 0.16);
border-left: 3px solid #58a6ff;
border-radius: 6px;
z-index: 0;
overflow: hidden;
}
.diff-line {
display: block;
min-height: 0;
line-height: 46px;
white-space: pre;
position: relative;
overflow: hidden;
padding-left: 14px;
}
.diff-sign {
display: inline-block;
width: 22px;
color: #41506220;
}
.diff-del {
background: rgba(248, 81, 73, 0.12);
box-shadow: inset 3px 0 #f85149;
}
.diff-add {
background: rgba(63, 185, 80, 0.12);
box-shadow: inset 3px 0 #3fb950;
}
.diff-del .diff-sign {
color: #f85149;
}
.diff-add .diff-sign {
color: #3fb950;
}
.flight-block {
position: absolute;
white-space: pre;
font-size: 27px;
line-height: 40px;
padding: 11px 22px;
border-radius: 11px;
background: #0f1722;
border: 1px solid #233044;
box-shadow: 0 16px 44px rgba(0, 0, 0, 0.5);
will-change: transform, opacity;
}
.flight-target {
position: absolute;
border: 1.5px dashed #2a3a4f;
border-radius: 14px;
opacity: 0;
}
</style>
</head>
<body>
<div
id="root"
data-composition-id="code-highlight"
data-start="0"
data-duration="5"
data-width="1920"
data-height="1080"
>
<div class="bg-field" data-layout-ignore></div>
<div class="bg-grid" data-layout-ignore></div>
<div class="bg-glow a" data-layout-ignore></div>
<div class="bg-glow b" data-layout-ignore></div>
<div class="editor" id="editor" data-layout-ignore>
<div class="titlebar">
<div class="dots">
<span class="dot r"></span><span class="dot y"></span><span class="dot g"></span>
</div>
<span class="filename"
><span class="accent">loadConfig.js</span> — Code Highlight Sweep</span
>
</div>
<div class="code-surface" id="surface"></div>
</div>
</div>
<script>
window.__TOKENS = {
feature: {
lang: "js",
theme: "github-dark",
bg: "#24292e",
fg: "#e1e4e8",
states: [
{
code: 'async function loadConfig(path) {\n const raw = await readFile(path, "utf8")\n const config = JSON.parse(raw)\n return validate(config)\n}',
tokens: [
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-0",
content: "async",
color: "#F97583",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-1",
content: " ",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-2",
content: "function",
color: "#F97583",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-3",
content: " ",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-4",
content: "loadConfig",
color: "#B392F0",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-5",
content: "(",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-6",
content: "path",
color: "#FFAB70",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-7",
content: ") {",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-8",
content: "\n",
color: "#e1e4e8",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-9",
content: " ",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-10",
content: "const",
color: "#F97583",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-11",
content: " ",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-12",
content: "raw",
color: "#79B8FF",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-13",
content: " ",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-14",
content: "=",
color: "#F97583",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-15",
content: " ",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-16",
content: "await",
color: "#F97583",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-17",
content: " ",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-18",
content: "readFile",
color: "#B392F0",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-19",
content: "(path,",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-20",
content: " ",
color: "#e1e4e8",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-21",
content: '"utf8"',
color: "#9ECBFF",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-22",
content: ")",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-23",
content: "\n",
color: "#e1e4e8",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-24",
content: " ",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-25",
content: "const",
color: "#F97583",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-26",
content: " ",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-27",
content: "config",
color: "#79B8FF",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-28",
content: " ",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-29",
content: "=",
color: "#F97583",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-30",
content: " ",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-31",
content: "JSON",
color: "#79B8FF",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-32",
content: ".",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-33",
content: "parse",
color: "#B392F0",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-34",
content: "(raw)",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-35",
content: "\n",
color: "#e1e4e8",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-36",
content: " ",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-37",
content: "return",
color: "#F97583",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-38",
content: " ",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-39",
content: "validate",
color: "#B392F0",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-40",
content: "(config)",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-41",
content: "\n",
color: "#e1e4e8",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-42",
content: "}",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-43",
content: "\n",
color: "#e1e4e8",
fontStyle: 0,
},
],
},
],
},
};
window.__BLOCK = {
id: "code-highlight",
effect: "highlight",
seq: "feature",
line: 1,
duration: 5,
};
</script>
<script>
// hf-code-anim.js — self-contained code-animation engine for HyperFrames registry blocks.
//
// Reuses Shiki Magic Move's baked output (window.__TOKENS, baked at author time) and
// drives the animation as a PAUSED GSAP timeline the HyperFrames engine seeks per frame —
// NOT CSS transitions (those break under non-sequential frame seeking). FLIP measurement
// runs once inside document.fonts.ready (glyph metrics must be final). Each block sets
// window.__BLOCK = { id, effect, seq, line?, duration } and includes this engine; the
// engine builds one scene that fills the composition and registers window.__timelines[id].
(function () {
"use strict";
var T = window.__TOKENS || {};
function lineDiv() {
var d = document.createElement("div");
d.className = "line";
return d;
}
// Render a code state's keyed tokens into codeEl as lines of inline-block .tok spans.
function renderState(codeEl, state) {
codeEl.textContent = "";
var keyToEl = new Map();
var line = lineDiv();
codeEl.appendChild(line);
for (var i = 0; i < state.tokens.length; i++) {
var t = state.tokens[i];
if (t.content === "\n") {
line = lineDiv();
codeEl.appendChild(line);
continue;
}
var span = document.createElement("span");
span.className = "tok";
span.textContent = t.content;
span.style.color = t.color;
if (t.fontStyle & 1) span.style.fontStyle = "italic";
if (t.fontStyle & 2) span.style.fontWeight = "700";
line.appendChild(span);
keyToEl.set(t.key, span);
}
var last = codeEl.lastChild;
if (last && last.className === "line" && last.children.length === 0)
codeEl.removeChild(last);
return keyToEl;
}
function rectsOf(keyToEl) {
var m = new Map();
keyToEl.forEach(function (el, k) {
var r = el.getBoundingClientRect();
m.set(k, { left: r.left, top: r.top });
});
return m;
}
function makeScene(surface, withGutter) {
var scene = document.createElement("div");
scene.className = "scene";
if (withGutter) {
var g = document.createElement("div");
g.className = "gutter";
scene.appendChild(g);
}
var code = document.createElement("div");
code.className = "code";
scene.appendChild(code);
surface.appendChild(scene);
return { scene: scene, code: code, gutter: scene.querySelector(".gutter") };
}
function fillGutter(g, n) {
var html = "";
for (var i = 1; i <= n; i++) html += '<span class="ln">' + i + "</span>";
g.innerHTML = html;
}
function splitLines(state) {
var lines = [{ text: "", tokens: [] }];
for (var i = 0; i < state.tokens.length; i++) {
var t = state.tokens[i];
if (t.content === "\n") {
lines.push({ text: "", tokens: [] });
continue;
}
var cur = lines[lines.length - 1];
cur.tokens.push(t);
cur.text += t.content;
}
if (lines.length && lines[lines.length - 1].tokens.length === 0) lines.pop();
return lines;
}
function lineDiff(a, b) {
var n = a.length;
var m = b.length;
var dp = [];
for (var i = 0; i <= n; i++) dp.push(new Array(m + 1).fill(0));
for (var i = n - 1; i >= 0; i--) {
for (var j = m - 1; j >= 0; j--) {
dp[i][j] =
a[i].text === b[j].text
? dp[i + 1][j + 1] + 1
: Math.max(dp[i + 1][j], dp[i][j + 1]);
}
}
var ops = [];
var i = 0;
var j = 0;
while (i < n && j < m) {
if (a[i].text === b[j].text) {
ops.push({ type: "same", line: b[j] });
i++;
j++;
} else if (dp[i + 1][j] >= dp[i][j + 1]) {
ops.push({ type: "del", line: a[i] });
i++;
} else {
ops.push({ type: "add", line: b[j] });
j++;
}
}
while (i < n) ops.push({ type: "del", line: a[i++] });
while (j < m) ops.push({ type: "add", line: b[j++] });
return ops;
}
// ---- single-scene effect builders (fade in, play, hold to end) -------------
var FADE = 0.45;
function highlight(tl, surface, state, start, targetIdx) {
var SWEEP = 0.9;
var parts = makeScene(surface, true);
var code = parts.code;
renderState(code, state);
var lines = code.querySelectorAll(".line");
fillGutter(parts.gutter, lines.length);
var base = code.getBoundingClientRect();
var idx = Math.max(0, Math.min(lines.length - 1, targetIdx));
var lr = lines[idx].getBoundingClientRect();
var box = document.createElement("div");
box.className = "hl-box";
box.style.left = lr.left - base.left - 8 + "px";
box.style.top = lr.top - base.top - 1 + "px";
box.style.height = lr.height + 2 + "px";
code.insertBefore(box, code.firstChild);
tl.set(parts.scene, { opacity: 0 }, 0);
tl.to(parts.scene, { opacity: 1, duration: FADE, ease: "power1.out" }, start);
var at = start + FADE + 0.2;
tl.fromTo(
box,
{ width: 0, opacity: 0 },
{ width: lr.width + 18, opacity: 1, duration: SWEEP, ease: "power2.inOut" },
at,
);
for (var i = 0; i < lines.length; i++) {
if (i === idx) continue;
tl.fromTo(
lines[i],
{ opacity: 1 },
{ opacity: 0.45, duration: SWEEP, ease: "power2.inOut" },
at,
);
}
}
function buildEffect(tl, surface, spec) {
var start = 0.45;
var seq = T[spec.seq];
if (spec.effect === "highlight")
highlight(tl, surface, seq.states[0], start, spec.line || 0);
}
function go() {
var spec = window.__BLOCK;
var surface = document.getElementById("surface");
var root = document.getElementById("root");
window.__timelines = window.__timelines || {};
var tl = gsap.timeline({ paused: true });
window.__timelines["code-highlight"] = tl; // register synchronously
document.fonts.ready.then(function () {
tl.from("#editor", { opacity: 0, scale: 0.985, duration: 0.5, ease: "power2.out" }, 0);
buildEffect(tl, surface, spec);
var dur = parseFloat(root.dataset.duration) || tl.duration();
tl.to({}, { duration: dur }, 0); // pad to full composition length
});
}
if (document.readyState === "loading") document.addEventListener("DOMContentLoaded", go);
else go();
})();
</script>
</body>
</html>
@@ -0,0 +1,24 @@
{
"$schema": "https://hyperframes.heygen.com/schema/registry-item.json",
"name": "code-highlight",
"type": "hyperframes:block",
"title": "Code Highlight Sweep",
"description": "A highlight band sweeps across a target line while the surrounding context dims — draws the eye to one line.",
"dimensions": {
"width": 1920,
"height": 1080
},
"duration": 5,
"tags": ["code", "code-animation", "highlight", "developer"],
"files": [
{
"path": "code-highlight.html",
"target": "compositions/code-highlight.html",
"type": "hyperframes:composition"
}
],
"preview": {
"video": "https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/code-highlight.mp4",
"poster": "https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/code-highlight.png"
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,24 @@
{
"$schema": "https://hyperframes.heygen.com/schema/registry-item.json",
"name": "code-morph",
"type": "hyperframes:block",
"title": "Code Morph",
"description": "One snippet transforms into another — tokens glide between positions, leavers fade out, enterers fade in. Shiki Magic Move re-driven as a paused GSAP timeline.",
"dimensions": {
"width": 1920,
"height": 1080
},
"duration": 7,
"tags": ["code", "code-animation", "morph", "refactor", "developer"],
"files": [
{
"path": "code-morph.html",
"target": "compositions/code-morph.html",
"type": "hyperframes:composition"
}
],
"preview": {
"video": "https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/code-morph.mp4",
"poster": "https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/code-morph.png"
}
}
@@ -0,0 +1,479 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=1920, height=1080" />
<title>Code Particle Assemble</title>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/gsap.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/build/three.min.js"></script>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html,
body {
width: 1920px;
height: 1080px;
overflow: hidden;
background: #05070b;
}
#root {
position: relative;
width: 1920px;
height: 1080px;
}
#gl {
position: absolute;
inset: 0;
width: 1920px;
height: 1080px;
display: block;
}
</style>
</head>
<body>
<div
id="root"
data-composition-id="code-particle-assemble"
data-start="0"
data-duration="8"
data-width="1920"
data-height="1080"
>
<canvas id="gl" width="1920" height="1080"></canvas>
</div>
<script>
window.__TOKENS = {
assemble: {
lang: "js",
theme: "github-dark",
bg: "#24292e",
fg: "#e1e4e8",
states: [
{
code: "const app = pipe(\n parse,\n optimize,\n emit,\n)",
tokens: [
{
key: "AFffx9qQBGKnMZ-vDjoaRwC68xQloGJNUSICY1myCK4-0",
content: "const",
color: "#F97583",
fontStyle: 0,
},
{
key: "AFffx9qQBGKnMZ-vDjoaRwC68xQloGJNUSICY1myCK4-1",
content: " ",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "AFffx9qQBGKnMZ-vDjoaRwC68xQloGJNUSICY1myCK4-2",
content: "app",
color: "#79B8FF",
fontStyle: 0,
},
{
key: "AFffx9qQBGKnMZ-vDjoaRwC68xQloGJNUSICY1myCK4-3",
content: " ",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "AFffx9qQBGKnMZ-vDjoaRwC68xQloGJNUSICY1myCK4-4",
content: "=",
color: "#F97583",
fontStyle: 0,
},
{
key: "AFffx9qQBGKnMZ-vDjoaRwC68xQloGJNUSICY1myCK4-5",
content: " ",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "AFffx9qQBGKnMZ-vDjoaRwC68xQloGJNUSICY1myCK4-6",
content: "pipe",
color: "#B392F0",
fontStyle: 0,
},
{
key: "AFffx9qQBGKnMZ-vDjoaRwC68xQloGJNUSICY1myCK4-7",
content: "(",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "AFffx9qQBGKnMZ-vDjoaRwC68xQloGJNUSICY1myCK4-8",
content: "\n",
color: "#e1e4e8",
fontStyle: 0,
},
{
key: "AFffx9qQBGKnMZ-vDjoaRwC68xQloGJNUSICY1myCK4-9",
content: " ",
color: "#e1e4e8",
fontStyle: 0,
},
{
key: "AFffx9qQBGKnMZ-vDjoaRwC68xQloGJNUSICY1myCK4-10",
content: "parse,",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "AFffx9qQBGKnMZ-vDjoaRwC68xQloGJNUSICY1myCK4-11",
content: "\n",
color: "#e1e4e8",
fontStyle: 0,
},
{
key: "AFffx9qQBGKnMZ-vDjoaRwC68xQloGJNUSICY1myCK4-12",
content: " ",
color: "#e1e4e8",
fontStyle: 0,
},
{
key: "AFffx9qQBGKnMZ-vDjoaRwC68xQloGJNUSICY1myCK4-13",
content: "optimize,",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "AFffx9qQBGKnMZ-vDjoaRwC68xQloGJNUSICY1myCK4-14",
content: "\n",
color: "#e1e4e8",
fontStyle: 0,
},
{
key: "AFffx9qQBGKnMZ-vDjoaRwC68xQloGJNUSICY1myCK4-15",
content: " ",
color: "#e1e4e8",
fontStyle: 0,
},
{
key: "AFffx9qQBGKnMZ-vDjoaRwC68xQloGJNUSICY1myCK4-16",
content: "emit,",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "AFffx9qQBGKnMZ-vDjoaRwC68xQloGJNUSICY1myCK4-17",
content: "\n",
color: "#e1e4e8",
fontStyle: 0,
},
{
key: "AFffx9qQBGKnMZ-vDjoaRwC68xQloGJNUSICY1myCK4-18",
content: ")",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "AFffx9qQBGKnMZ-vDjoaRwC68xQloGJNUSICY1myCK4-19",
content: "\n",
color: "#e1e4e8",
fontStyle: 0,
},
],
},
],
},
};
window.__BLOCK = {
id: "code-particle-assemble",
effect: "particles",
seq: "assemble",
duration: 8,
seed: 23,
};
</script>
<script>
// hf-code-anim-gl.js — WebGL code-animation engine for the "crazy" Code Animations blocks.
//
// Bridges the baked Shiki tokens (window.__TOKENS) into Three.js: the tokens are drawn once
// to an offscreen 2D canvas (a CanvasTexture), which then drives a lit 3D slab, a dissolve
// shader, or a GPU particle system. Determinism per the project contract:
// - render only via tl.eventCallback("onUpdate", …) + one initial render() — never rAF
// - all randomness from a seeded mulberry32 — never Math.random / Date.now
// - canvas drawn once after document.fonts.ready (final glyph metrics)
// - paused GSAP timeline registered on window.__timelines[id]; HF seeks it per frame
//
// Each block sets window.__BLOCK = { id, effect, seq, duration, seed } and includes this engine.
(function () {
"use strict";
var T = window.__TOKENS || {};
// ---- seeded PRNG (no Math.random) -----------------------------------------
function mulberry32(a) {
return function () {
a |= 0;
a = (a + 0x6d2b79f5) | 0;
var t = Math.imul(a ^ (a >>> 15), 1 | a);
t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
};
}
// ---- split a token state into lines of {content,color} --------------------
function splitLines(state) {
var lines = [[]];
for (var i = 0; i < state.tokens.length; i++) {
var t = state.tokens[i];
if (t.content === "\n") {
lines.push([]);
continue;
}
lines[lines.length - 1].push(t);
}
while (lines.length && lines[lines.length - 1].length === 0) lines.pop();
return lines;
}
// ---- THE BRIDGE: draw baked tokens to an offscreen 2D canvas ---------------
// Returns { canvas, W, H, lines, lineH, padY, fontPx } so effects can sample it.
function drawCodeToCanvas(state, opts) {
opts = opts || {};
var fontPx = opts.fontPx || 36;
var lineH = opts.lineH || 54;
var padX = opts.padX || 70;
var padY = opts.padY || 60;
var weight = opts.weight || 500;
var font = weight + " " + fontPx + 'px "JetBrains Mono", monospace';
var lines = splitLines(state);
var measure = document.createElement("canvas").getContext("2d");
measure.font = font;
var maxW = 0;
for (var i = 0; i < lines.length; i++) {
var w = 0;
for (var j = 0; j < lines[i].length; j++)
w += measure.measureText(lines[i][j].content).width;
if (w > maxW) maxW = w;
}
var W = Math.max(2, Math.ceil(maxW + padX * 2));
var H = Math.max(2, Math.ceil(lines.length * lineH + padY * 2));
var canvas = document.createElement("canvas");
canvas.width = W;
canvas.height = H;
var ctx = canvas.getContext("2d");
if (opts.transparent) {
ctx.clearRect(0, 0, W, H);
} else {
ctx.fillStyle = opts.bg || "#0b0f17";
ctx.fillRect(0, 0, W, H);
}
ctx.textBaseline = "top";
ctx.font = font;
for (var li = 0; li < lines.length; li++) {
var x = padX;
var y = padY + li * lineH;
for (var k = 0; k < lines[li].length; k++) {
var tok = lines[li][k];
ctx.fillStyle = tok.color;
ctx.fillText(tok.content, x, y);
x += ctx.measureText(tok.content).width;
}
}
return {
canvas: canvas,
W: W,
H: H,
lines: lines.length,
lineH: lineH,
padY: padY,
fontPx: fontPx,
};
}
function fxParticles(THREE, scene, camera, renderer, state, st, tl, dur, seed) {
scene.background = new THREE.Color(0x05070b);
var code = drawCodeToCanvas(state, {
fontPx: 46,
lineH: 70,
padX: 70,
padY: 64,
weight: 700,
});
var cw = code.W;
var ch = code.H;
var data = code.canvas.getContext("2d").getImageData(0, 0, cw, ch).data;
var rng = mulberry32(((seed || 1) * 2654435761) >>> 0);
var worldW = 13.0;
var worldH = worldW * (ch / cw);
// sample glyph pixels (brighter than the dark bg) on a fixed grid
var step = 2;
var tx = [];
var col = [];
for (var y = 0; y < ch; y += step) {
for (var x = 0; x < cw; x += step) {
var idx = (y * cw + x) * 4;
var r = data[idx];
var g = data[idx + 1];
var b = data[idx + 2];
if (r + g + b < 120) continue; // skip background
tx.push((x / cw - 0.5) * worldW, -(y / ch - 0.5) * worldH, 0);
col.push(r / 255, g / 255, b / 255);
}
}
var n = tx.length / 3;
var positions = new Float32Array(n * 3);
var aOrigin = new Float32Array(n * 3);
var aTarget = new Float32Array(tx);
var aColor = new Float32Array(col);
var aDelay = new Float32Array(n);
for (var i = 0; i < n; i++) {
var sx = (rng() * 2 - 1) * worldW * 1.15;
var sy = (rng() * 2 - 1) * worldH * 1.7;
var sz = (rng() * 2 - 1) * 9.0 - 2.0;
aOrigin[i * 3] = sx;
aOrigin[i * 3 + 1] = sy;
aOrigin[i * 3 + 2] = sz;
positions[i * 3] = sx;
positions[i * 3 + 1] = sy;
positions[i * 3 + 2] = sz;
aDelay[i] = rng();
}
var geo = new THREE.BufferGeometry();
geo.setAttribute("position", new THREE.BufferAttribute(positions, 3));
geo.setAttribute("aOrigin", new THREE.BufferAttribute(aOrigin, 3));
geo.setAttribute("aTarget", new THREE.BufferAttribute(aTarget, 3));
geo.setAttribute("aColor", new THREE.BufferAttribute(aColor, 3));
geo.setAttribute("aDelay", new THREE.BufferAttribute(aDelay, 1));
var uniforms = { uProgress: { value: 0 }, uSize: { value: 3.4 } };
var mat = new THREE.ShaderMaterial({
uniforms: uniforms,
transparent: true,
depthTest: false,
depthWrite: false,
blending: THREE.NormalBlending,
vertexShader: [
"attribute vec3 aOrigin; attribute vec3 aTarget; attribute vec3 aColor; attribute float aDelay;",
"uniform float uProgress; uniform float uSize;",
"varying vec3 vColor; varying float vP;",
"void main(){",
" float span = 0.62;",
" float p = clamp((uProgress - aDelay*(1.0-span)) / span, 0.0, 1.0);",
" p = p*p*(3.0-2.0*p);",
" vec3 pos = mix(aOrigin, aTarget, p);",
" vec4 mv = modelViewMatrix * vec4(pos, 1.0);",
" gl_Position = projectionMatrix * mv;",
" gl_PointSize = uSize * (60.0 / -mv.z) * (0.45 + 0.55*p);",
" vColor = aColor; vP = p;",
"}",
].join("\n"),
fragmentShader: [
"precision highp float;",
"varying vec3 vColor; varying float vP;",
"void main(){",
" vec2 d = gl_PointCoord - 0.5;",
" float r2 = dot(d, d);",
" if (r2 > 0.25) discard;",
" float a = smoothstep(0.25, 0.02, r2);",
// brighter & whiter in flight, resolves to true color as it lands
" vec3 c = mix(vColor + vec3(0.32)*(1.0-vP), vColor, vP);",
" gl_FragColor = vec4(c, a * (0.7 + 0.3*vP));",
"}",
].join("\n"),
});
var pts = new THREE.Points(geo, mat);
scene.add(pts);
camera.position.set(0, 0, 10);
var vfov = (camera.fov * Math.PI) / 180;
var sa = 1920 / 1080;
var fitH = worldH / 2 / Math.tan(vfov / 2) / 0.78;
var fitW = worldW / 2 / (Math.tan(vfov / 2) * sa) / 0.78;
camera.position.z = Math.max(fitH, fitW);
camera.lookAt(0, 0, 0);
tl.to(uniforms.uProgress, { value: 1.0, duration: dur * 0.72, ease: "power2.out" }, 0);
// uProgress is tweened by GSAP; the boot loop's render() draws once per frame.
return function () {};
}
// ---- dispatch + boot ------------------------------------------------------
function buildEffect(THREE, scene, camera, renderer, spec, st, tl) {
var state = T[spec.seq].states[0];
var dur = spec.duration;
if (spec.effect === "particles")
return fxParticles(THREE, scene, camera, renderer, state, st, tl, dur, spec.seed);
return function () {};
}
// Free GPU resources (renderer, geometries, materials, textures) on page teardown.
// One-shot renders exit per job so nothing leaks there; this guards long-lived
// Studio/player sessions that reload. Fires only on unload — never during capture.
function disposeGL(renderer, scene) {
scene.traverse(function (o) {
if (o.geometry && o.geometry.dispose) o.geometry.dispose();
var mats = o.material ? (Array.isArray(o.material) ? o.material : [o.material]) : [];
mats.forEach(function (m) {
if (m.map && m.map.dispose) m.map.dispose();
if (
m.uniforms &&
m.uniforms.uTex &&
m.uniforms.uTex.value &&
m.uniforms.uTex.value.dispose
) {
m.uniforms.uTex.value.dispose();
}
if (m.dispose) m.dispose();
});
});
if (renderer.dispose) renderer.dispose();
}
function go() {
var spec = window.__BLOCK;
var root = document.getElementById("root");
var glcanvas = document.getElementById("gl");
var W = 1920;
var H = 1080;
window.__timelines = window.__timelines || {};
var tl = gsap.timeline({ paused: true });
window.__timelines[spec.id] = tl; // register synchronously
var THREE = window.THREE;
var renderer = new THREE.WebGLRenderer({
canvas: glcanvas,
antialias: true,
alpha: false,
preserveDrawingBuffer: true,
});
renderer.setPixelRatio(1);
renderer.setSize(W, H, false);
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(45, W / H, 0.1, 200);
var st = {};
document.fonts.ready.then(function () {
var renderFn = buildEffect(THREE, scene, camera, renderer, spec, st, tl);
function render() {
renderFn();
renderer.render(scene, camera);
}
tl.eventCallback("onUpdate", render);
var dur = parseFloat(root.dataset.duration) || tl.duration();
tl.to({}, { duration: dur }, 0); // pad to full composition length
render(); // paint frame 0
window.addEventListener("beforeunload", function () {
disposeGL(renderer, scene);
});
});
}
if (document.readyState === "loading") document.addEventListener("DOMContentLoaded", go);
else go();
})();
</script>
</body>
</html>
@@ -0,0 +1,24 @@
{
"$schema": "https://hyperframes.heygen.com/schema/registry-item.json",
"name": "code-particle-assemble",
"type": "hyperframes:block",
"title": "Code Particle Assemble",
"description": "Thousands of GPU points scatter through space and fly to the exact glyph pixels of the code, resolving into readable syntax-highlighted text — a particle system, not a token tween.",
"dimensions": {
"width": 1920,
"height": 1080
},
"duration": 8,
"tags": ["code", "code-animation", "particles", "gpu", "webgl", "developer"],
"files": [
{
"path": "code-particle-assemble.html",
"target": "compositions/code-particle-assemble.html",
"type": "hyperframes:composition"
}
],
"preview": {
"video": "https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/code-particle-assemble.mp4",
"poster": "https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/code-particle-assemble.png"
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,24 @@
{
"$schema": "https://hyperframes.heygen.com/schema/registry-item.json",
"name": "code-scroll",
"type": "hyperframes:block",
"title": "Code Scroll To Line",
"description": "The camera scrolls a long file to bring a target line to center and spotlights it — for walking through real modules.",
"dimensions": {
"width": 1920,
"height": 1080
},
"duration": 6,
"tags": ["code", "code-animation", "scroll", "developer"],
"files": [
{
"path": "code-scroll.html",
"target": "compositions/code-scroll.html",
"type": "hyperframes:composition"
}
],
"preview": {
"video": "https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/code-scroll.mp4",
"poster": "https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/code-scroll.png"
}
}
@@ -0,0 +1,535 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=1920, height=1080" />
<title>Code Shader Dissolve</title>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/gsap.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/build/three.min.js"></script>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html,
body {
width: 1920px;
height: 1080px;
overflow: hidden;
background: #05070b;
}
#root {
position: relative;
width: 1920px;
height: 1080px;
}
#gl {
position: absolute;
inset: 0;
width: 1920px;
height: 1080px;
display: block;
}
</style>
</head>
<body>
<div
id="root"
data-composition-id="code-shader-dissolve"
data-start="0"
data-duration="7"
data-width="1920"
data-height="1080"
>
<canvas id="gl" width="1920" height="1080"></canvas>
</div>
<script>
window.__TOKENS = {
compile: {
lang: "js",
theme: "github-dark",
bg: "#24292e",
fg: "#e1e4e8",
states: [
{
code: "function compile(src) {\n const ast = parse(src)\n const ir = optimize(ast)\n return emit(ir)\n}",
tokens: [
{
key: "zGtBjd8ajYlOewpKFJD53Oc7W94UowxIfV0186O1l8A-0",
content: "function",
color: "#F97583",
fontStyle: 0,
},
{
key: "zGtBjd8ajYlOewpKFJD53Oc7W94UowxIfV0186O1l8A-1",
content: " ",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "zGtBjd8ajYlOewpKFJD53Oc7W94UowxIfV0186O1l8A-2",
content: "compile",
color: "#B392F0",
fontStyle: 0,
},
{
key: "zGtBjd8ajYlOewpKFJD53Oc7W94UowxIfV0186O1l8A-3",
content: "(",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "zGtBjd8ajYlOewpKFJD53Oc7W94UowxIfV0186O1l8A-4",
content: "src",
color: "#FFAB70",
fontStyle: 0,
},
{
key: "zGtBjd8ajYlOewpKFJD53Oc7W94UowxIfV0186O1l8A-5",
content: ") {",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "zGtBjd8ajYlOewpKFJD53Oc7W94UowxIfV0186O1l8A-6",
content: "\n",
color: "#e1e4e8",
fontStyle: 0,
},
{
key: "zGtBjd8ajYlOewpKFJD53Oc7W94UowxIfV0186O1l8A-7",
content: " ",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "zGtBjd8ajYlOewpKFJD53Oc7W94UowxIfV0186O1l8A-8",
content: "const",
color: "#F97583",
fontStyle: 0,
},
{
key: "zGtBjd8ajYlOewpKFJD53Oc7W94UowxIfV0186O1l8A-9",
content: " ",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "zGtBjd8ajYlOewpKFJD53Oc7W94UowxIfV0186O1l8A-10",
content: "ast",
color: "#79B8FF",
fontStyle: 0,
},
{
key: "zGtBjd8ajYlOewpKFJD53Oc7W94UowxIfV0186O1l8A-11",
content: " ",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "zGtBjd8ajYlOewpKFJD53Oc7W94UowxIfV0186O1l8A-12",
content: "=",
color: "#F97583",
fontStyle: 0,
},
{
key: "zGtBjd8ajYlOewpKFJD53Oc7W94UowxIfV0186O1l8A-13",
content: " ",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "zGtBjd8ajYlOewpKFJD53Oc7W94UowxIfV0186O1l8A-14",
content: "parse",
color: "#B392F0",
fontStyle: 0,
},
{
key: "zGtBjd8ajYlOewpKFJD53Oc7W94UowxIfV0186O1l8A-15",
content: "(src)",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "zGtBjd8ajYlOewpKFJD53Oc7W94UowxIfV0186O1l8A-16",
content: "\n",
color: "#e1e4e8",
fontStyle: 0,
},
{
key: "zGtBjd8ajYlOewpKFJD53Oc7W94UowxIfV0186O1l8A-17",
content: " ",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "zGtBjd8ajYlOewpKFJD53Oc7W94UowxIfV0186O1l8A-18",
content: "const",
color: "#F97583",
fontStyle: 0,
},
{
key: "zGtBjd8ajYlOewpKFJD53Oc7W94UowxIfV0186O1l8A-19",
content: " ",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "zGtBjd8ajYlOewpKFJD53Oc7W94UowxIfV0186O1l8A-20",
content: "ir",
color: "#79B8FF",
fontStyle: 0,
},
{
key: "zGtBjd8ajYlOewpKFJD53Oc7W94UowxIfV0186O1l8A-21",
content: " ",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "zGtBjd8ajYlOewpKFJD53Oc7W94UowxIfV0186O1l8A-22",
content: "=",
color: "#F97583",
fontStyle: 0,
},
{
key: "zGtBjd8ajYlOewpKFJD53Oc7W94UowxIfV0186O1l8A-23",
content: " ",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "zGtBjd8ajYlOewpKFJD53Oc7W94UowxIfV0186O1l8A-24",
content: "optimize",
color: "#B392F0",
fontStyle: 0,
},
{
key: "zGtBjd8ajYlOewpKFJD53Oc7W94UowxIfV0186O1l8A-25",
content: "(ast)",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "zGtBjd8ajYlOewpKFJD53Oc7W94UowxIfV0186O1l8A-26",
content: "\n",
color: "#e1e4e8",
fontStyle: 0,
},
{
key: "zGtBjd8ajYlOewpKFJD53Oc7W94UowxIfV0186O1l8A-27",
content: " ",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "zGtBjd8ajYlOewpKFJD53Oc7W94UowxIfV0186O1l8A-28",
content: "return",
color: "#F97583",
fontStyle: 0,
},
{
key: "zGtBjd8ajYlOewpKFJD53Oc7W94UowxIfV0186O1l8A-29",
content: " ",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "zGtBjd8ajYlOewpKFJD53Oc7W94UowxIfV0186O1l8A-30",
content: "emit",
color: "#B392F0",
fontStyle: 0,
},
{
key: "zGtBjd8ajYlOewpKFJD53Oc7W94UowxIfV0186O1l8A-31",
content: "(ir)",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "zGtBjd8ajYlOewpKFJD53Oc7W94UowxIfV0186O1l8A-32",
content: "\n",
color: "#e1e4e8",
fontStyle: 0,
},
{
key: "zGtBjd8ajYlOewpKFJD53Oc7W94UowxIfV0186O1l8A-33",
content: "}",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "zGtBjd8ajYlOewpKFJD53Oc7W94UowxIfV0186O1l8A-34",
content: "\n",
color: "#e1e4e8",
fontStyle: 0,
},
],
},
],
},
};
window.__BLOCK = {
id: "code-shader-dissolve",
effect: "dissolve",
seq: "compile",
duration: 7,
seed: 11,
};
</script>
<script>
// hf-code-anim-gl.js — WebGL code-animation engine for the "crazy" Code Animations blocks.
//
// Bridges the baked Shiki tokens (window.__TOKENS) into Three.js: the tokens are drawn once
// to an offscreen 2D canvas (a CanvasTexture), which then drives a lit 3D slab, a dissolve
// shader, or a GPU particle system. Determinism per the project contract:
// - render only via tl.eventCallback("onUpdate", …) + one initial render() — never rAF
// - all randomness from a seeded mulberry32 — never Math.random / Date.now
// - canvas drawn once after document.fonts.ready (final glyph metrics)
// - paused GSAP timeline registered on window.__timelines[id]; HF seeks it per frame
//
// Each block sets window.__BLOCK = { id, effect, seq, duration, seed } and includes this engine.
(function () {
"use strict";
var T = window.__TOKENS || {};
// ---- seeded PRNG (no Math.random) -----------------------------------------
function mulberry32(a) {
return function () {
a |= 0;
a = (a + 0x6d2b79f5) | 0;
var t = Math.imul(a ^ (a >>> 15), 1 | a);
t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
};
}
// ---- split a token state into lines of {content,color} --------------------
function splitLines(state) {
var lines = [[]];
for (var i = 0; i < state.tokens.length; i++) {
var t = state.tokens[i];
if (t.content === "\n") {
lines.push([]);
continue;
}
lines[lines.length - 1].push(t);
}
while (lines.length && lines[lines.length - 1].length === 0) lines.pop();
return lines;
}
// ---- THE BRIDGE: draw baked tokens to an offscreen 2D canvas ---------------
// Returns { canvas, W, H, lines, lineH, padY, fontPx } so effects can sample it.
function drawCodeToCanvas(state, opts) {
opts = opts || {};
var fontPx = opts.fontPx || 36;
var lineH = opts.lineH || 54;
var padX = opts.padX || 70;
var padY = opts.padY || 60;
var weight = opts.weight || 500;
var font = weight + " " + fontPx + 'px "JetBrains Mono", monospace';
var lines = splitLines(state);
var measure = document.createElement("canvas").getContext("2d");
measure.font = font;
var maxW = 0;
for (var i = 0; i < lines.length; i++) {
var w = 0;
for (var j = 0; j < lines[i].length; j++)
w += measure.measureText(lines[i][j].content).width;
if (w > maxW) maxW = w;
}
var W = Math.max(2, Math.ceil(maxW + padX * 2));
var H = Math.max(2, Math.ceil(lines.length * lineH + padY * 2));
var canvas = document.createElement("canvas");
canvas.width = W;
canvas.height = H;
var ctx = canvas.getContext("2d");
if (opts.transparent) {
ctx.clearRect(0, 0, W, H);
} else {
ctx.fillStyle = opts.bg || "#0b0f17";
ctx.fillRect(0, 0, W, H);
}
ctx.textBaseline = "top";
ctx.font = font;
for (var li = 0; li < lines.length; li++) {
var x = padX;
var y = padY + li * lineH;
for (var k = 0; k < lines[li].length; k++) {
var tok = lines[li][k];
ctx.fillStyle = tok.color;
ctx.fillText(tok.content, x, y);
x += ctx.measureText(tok.content).width;
}
}
return {
canvas: canvas,
W: W,
H: H,
lines: lines.length,
lineH: lineH,
padY: padY,
fontPx: fontPx,
};
}
function fxDissolve(THREE, scene, camera, renderer, state, st, tl, dur, seed) {
scene.background = new THREE.Color(0x05070b);
var code = drawCodeToCanvas(state, {
fontPx: 40,
lineH: 60,
padX: 90,
padY: 80,
weight: 600,
});
var tex = new THREE.CanvasTexture(code.canvas);
tex.minFilter = THREE.LinearFilter;
tex.magFilter = THREE.LinearFilter;
tex.generateMipmaps = false;
var aspect = code.W / code.H;
var planeH = 6.6;
var planeW = planeH * aspect;
var uniforms = {
uTex: { value: tex },
uProgress: { value: 0 },
uSeed: { value: (seed || 1) * 13.37 },
};
var mat = new THREE.ShaderMaterial({
uniforms: uniforms,
transparent: true,
vertexShader: [
"varying vec2 vUv;",
"void main(){ vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0); }",
].join("\n"),
fragmentShader: [
"precision highp float;",
"uniform sampler2D uTex;",
"uniform float uProgress;",
"uniform float uSeed;",
"varying vec2 vUv;",
"float hash(vec2 p){ p=fract(p*vec2(123.34,456.21)); p+=dot(p,p+45.32); return fract(p.x*p.y); }",
"float noise(vec2 p){ vec2 i=floor(p),f=fract(p); float a=hash(i),b=hash(i+vec2(1.,0.)),c=hash(i+vec2(0.,1.)),d=hash(i+vec2(1.,1.)); vec2 u=f*f*(3.-2.*f); return mix(mix(a,b,u.x),mix(c,d,u.x),u.y); }",
"void main(){",
" vec2 uv = vUv;",
// multi-octave seeded noise field
" float n = noise(uv*16.0+uSeed)*0.6 + noise(uv*48.0+uSeed*1.7)*0.3 + noise(uv*120.0+uSeed*2.3)*0.1;",
" float edge = 0.12;",
" float reveal = smoothstep(n-edge, n+edge, uProgress);", // 0 hidden -> 1 shown
// chromatic fringe strongest at the dissolving edge
" float band = reveal*(1.0-reveal)*4.0;",
" float off = band*0.010;",
" float r = texture2D(uTex, uv+vec2(off,0.0)).r;",
" float g = texture2D(uTex, uv).g;",
" float b = texture2D(uTex, uv-vec2(off,0.0)).b;",
" vec3 codeCol = vec3(r,g,b);",
// additive cyan edge glow along the dissolve front
" vec3 glow = vec3(0.30,0.72,1.0) * band * 0.9;",
" vec3 outc = codeCol*reveal + glow;",
" float a = max(reveal, band*0.8);",
" gl_FragColor = vec4(outc, a);",
"}",
].join("\n"),
});
var plane = new THREE.Mesh(new THREE.PlaneGeometry(planeW, planeH), mat);
scene.add(plane);
camera.position.set(0, 0, 10);
var vfov = (camera.fov * Math.PI) / 180;
var screenAspect = 1920 / 1080;
var fitH = planeH / 2 / Math.tan(vfov / 2) / 0.82;
var fitW = planeW / 2 / (Math.tan(vfov / 2) * screenAspect) / 0.82;
camera.position.z = Math.max(fitH, fitW);
camera.lookAt(0, 0, 0);
// compile in, then hold crisp. Overshoot to 1.15 so even max-noise pixels
// (n up to ~1.0, edge 0.12) fully resolve — no leftover specks at rest.
tl.to(uniforms.uProgress, { value: 1.15, duration: dur * 0.62, ease: "power1.inOut" }, 0);
// uProgress is tweened by GSAP; the boot loop's render() draws once per frame.
return function () {};
}
// ---- dispatch + boot ------------------------------------------------------
function buildEffect(THREE, scene, camera, renderer, spec, st, tl) {
var state = T[spec.seq].states[0];
var dur = spec.duration;
if (spec.effect === "dissolve")
return fxDissolve(THREE, scene, camera, renderer, state, st, tl, dur, spec.seed);
return function () {};
}
// Free GPU resources (renderer, geometries, materials, textures) on page teardown.
// One-shot renders exit per job so nothing leaks there; this guards long-lived
// Studio/player sessions that reload. Fires only on unload — never during capture.
function disposeGL(renderer, scene) {
scene.traverse(function (o) {
if (o.geometry && o.geometry.dispose) o.geometry.dispose();
var mats = o.material ? (Array.isArray(o.material) ? o.material : [o.material]) : [];
mats.forEach(function (m) {
if (m.map && m.map.dispose) m.map.dispose();
if (
m.uniforms &&
m.uniforms.uTex &&
m.uniforms.uTex.value &&
m.uniforms.uTex.value.dispose
) {
m.uniforms.uTex.value.dispose();
}
if (m.dispose) m.dispose();
});
});
if (renderer.dispose) renderer.dispose();
}
function go() {
var spec = window.__BLOCK;
var root = document.getElementById("root");
var glcanvas = document.getElementById("gl");
var W = 1920;
var H = 1080;
window.__timelines = window.__timelines || {};
var tl = gsap.timeline({ paused: true });
window.__timelines[spec.id] = tl; // register synchronously
var THREE = window.THREE;
var renderer = new THREE.WebGLRenderer({
canvas: glcanvas,
antialias: true,
alpha: false,
preserveDrawingBuffer: true,
});
renderer.setPixelRatio(1);
renderer.setSize(W, H, false);
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(45, W / H, 0.1, 200);
var st = {};
document.fonts.ready.then(function () {
var renderFn = buildEffect(THREE, scene, camera, renderer, spec, st, tl);
function render() {
renderFn();
renderer.render(scene, camera);
}
tl.eventCallback("onUpdate", render);
var dur = parseFloat(root.dataset.duration) || tl.duration();
tl.to({}, { duration: dur }, 0); // pad to full composition length
render(); // paint frame 0
window.addEventListener("beforeunload", function () {
disposeGL(renderer, scene);
});
});
}
if (document.readyState === "loading") document.addEventListener("DOMContentLoaded", go);
else go();
})();
</script>
</body>
</html>
@@ -0,0 +1,24 @@
{
"$schema": "https://hyperframes.heygen.com/schema/registry-item.json",
"name": "code-shader-dissolve",
"type": "hyperframes:block",
"title": "Code Shader Dissolve",
"description": "The code compiles into existence: a GPU fragment shader resolves it out of seeded noise with a chromatic dissolve front and edge glow, then holds crisp.",
"dimensions": {
"width": 1920,
"height": 1080
},
"duration": 7,
"tags": ["code", "code-animation", "shader", "webgl", "developer"],
"files": [
{
"path": "code-shader-dissolve.html",
"target": "compositions/code-shader-dissolve.html",
"type": "hyperframes:composition"
}
],
"preview": {
"video": "https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/code-shader-dissolve.mp4",
"poster": "https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/code-shader-dissolve.png"
}
}
@@ -0,0 +1,665 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=1920, height=1080" />
<title>Code Snippet Flight</title>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/gsap.min.js"></script>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html,
body {
width: 1920px;
height: 1080px;
overflow: hidden;
background: #05070b;
}
#root {
position: relative;
width: 1920px;
height: 1080px;
font-family: "JetBrains Mono", monospace;
color: #e6edf3;
}
.bg-field {
position: absolute;
inset: 0;
background: radial-gradient(1200px 700px at 50% 18%, #0e1726 0%, #070b12 55%, #05070b 100%);
}
.bg-grid {
position: absolute;
inset: 0;
background-image:
linear-gradient(rgba(88, 166, 255, 0.05) 1px, transparent 1px),
linear-gradient(90deg, rgba(88, 166, 255, 0.05) 1px, transparent 1px);
background-size: 64px 64px;
mask-image: radial-gradient(1100px 620px at 50% 42%, #000 0%, transparent 78%);
}
.bg-glow {
position: absolute;
width: 900px;
height: 900px;
border-radius: 50%;
filter: blur(120px);
opacity: 0.5;
}
.bg-glow.a {
background: #1f6feb55;
left: -180px;
top: -200px;
}
.bg-glow.b {
background: #2ea04355;
right: -200px;
bottom: -260px;
}
.editor {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
width: 1380px;
height: 800px;
background: #0b0f17;
border: 1px solid #1d2733;
border-radius: 16px;
box-shadow:
0 40px 120px rgba(0, 0, 0, 0.6),
0 2px 0 rgba(255, 255, 255, 0.03) inset;
overflow: hidden;
display: flex;
flex-direction: column;
}
.titlebar {
height: 56px;
flex: 0 0 56px;
display: flex;
align-items: center;
gap: 16px;
padding: 0 22px;
background: linear-gradient(#11161f, #0c111a);
border-bottom: 1px solid #1b2430;
}
.dots {
display: flex;
gap: 10px;
}
.dot {
width: 14px;
height: 14px;
border-radius: 50%;
}
.dot.r {
background: #ff5f57;
}
.dot.y {
background: #febc2e;
}
.dot.g {
background: #28c840;
}
.filename {
font-size: 19px;
color: #8b98a9;
letter-spacing: 0.2px;
}
.filename .accent {
color: #d6e2f0;
}
.code-surface {
position: relative;
flex: 1 1 auto;
overflow: hidden;
}
.scene {
position: absolute;
inset: 0;
opacity: 0;
will-change: opacity;
}
.code {
position: absolute;
top: 38px;
left: 0;
right: 0;
padding: 0 44px 0 104px;
font-size: 30px;
line-height: 46px;
font-variant-ligatures: none;
tab-size: 2;
}
.gutter {
position: absolute;
top: 38px;
left: 0;
width: 84px;
padding-right: 18px;
font-size: 30px;
line-height: 46px;
text-align: right;
color: #828c9b;
user-select: none;
}
.ln {
display: block;
height: 46px;
}
.line {
display: block;
min-height: 46px;
white-space: pre;
position: relative;
z-index: 1;
}
.tok {
display: inline-block;
white-space: pre;
will-change: transform, opacity;
}
.caret {
position: absolute;
top: 0;
left: 0;
width: 3px;
height: 32px;
margin-top: 6px;
background: #58a6ff;
border-radius: 1px;
z-index: 3;
}
.hl-box {
position: absolute;
background: rgba(88, 166, 255, 0.16);
border-left: 3px solid #58a6ff;
border-radius: 6px;
z-index: 0;
overflow: hidden;
}
.diff-line {
display: block;
min-height: 0;
line-height: 46px;
white-space: pre;
position: relative;
overflow: hidden;
padding-left: 14px;
}
.diff-sign {
display: inline-block;
width: 22px;
color: #41506220;
}
.diff-del {
background: rgba(248, 81, 73, 0.12);
box-shadow: inset 3px 0 #f85149;
}
.diff-add {
background: rgba(63, 185, 80, 0.12);
box-shadow: inset 3px 0 #3fb950;
}
.diff-del .diff-sign {
color: #f85149;
}
.diff-add .diff-sign {
color: #3fb950;
}
.flight-block {
position: absolute;
white-space: pre;
font-size: 27px;
line-height: 40px;
padding: 11px 22px;
border-radius: 11px;
background: #0f1722;
border: 1px solid #233044;
box-shadow: 0 16px 44px rgba(0, 0, 0, 0.5);
will-change: transform, opacity;
}
.flight-target {
position: absolute;
border: 1.5px dashed #2a3a4f;
border-radius: 14px;
opacity: 0;
}
</style>
</head>
<body>
<div
id="root"
data-composition-id="code-snippet-flight"
data-start="0"
data-duration="6"
data-width="1920"
data-height="1080"
>
<div class="bg-field" data-layout-ignore></div>
<div class="bg-grid" data-layout-ignore></div>
<div class="bg-glow a" data-layout-ignore></div>
<div class="bg-glow b" data-layout-ignore></div>
<div class="editor" id="editor" data-layout-ignore>
<div class="titlebar">
<div class="dots">
<span class="dot r"></span><span class="dot y"></span><span class="dot g"></span>
</div>
<span class="filename"
><span class="accent">pipeline.js</span> — Code Snippet Flight</span
>
</div>
<div class="code-surface" id="surface"></div>
</div>
</div>
<script>
window.__TOKENS = {
flight: {
lang: "js",
theme: "github-dark",
bg: "#24292e",
fg: "#e1e4e8",
states: [
{
code: "const x = parse(input)",
tokens: [
{
key: "qABRRXThYBzvlFJqemsh1aauYJqWMIPHhxkj-uoxex4-0",
content: "const",
color: "#F97583",
fontStyle: 0,
},
{
key: "qABRRXThYBzvlFJqemsh1aauYJqWMIPHhxkj-uoxex4-1",
content: " ",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "qABRRXThYBzvlFJqemsh1aauYJqWMIPHhxkj-uoxex4-2",
content: "x",
color: "#79B8FF",
fontStyle: 0,
},
{
key: "qABRRXThYBzvlFJqemsh1aauYJqWMIPHhxkj-uoxex4-3",
content: " ",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "qABRRXThYBzvlFJqemsh1aauYJqWMIPHhxkj-uoxex4-4",
content: "=",
color: "#F97583",
fontStyle: 0,
},
{
key: "qABRRXThYBzvlFJqemsh1aauYJqWMIPHhxkj-uoxex4-5",
content: " ",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "qABRRXThYBzvlFJqemsh1aauYJqWMIPHhxkj-uoxex4-6",
content: "parse",
color: "#B392F0",
fontStyle: 0,
},
{
key: "qABRRXThYBzvlFJqemsh1aauYJqWMIPHhxkj-uoxex4-7",
content: "(input)",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "qABRRXThYBzvlFJqemsh1aauYJqWMIPHhxkj-uoxex4-8",
content: "\n",
color: "#e1e4e8",
fontStyle: 0,
},
],
},
{
code: "const y = transform(x)",
tokens: [
{
key: "qABRRXThYBzvlFJqemsh1aauYJqWMIPHhxkj-uoxex4-0",
content: "const",
color: "#F97583",
fontStyle: 0,
},
{
key: "qABRRXThYBzvlFJqemsh1aauYJqWMIPHhxkj-uoxex4-1",
content: " ",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "ulDeJnFQa2Vy-61y5zTTA7sNfq7d4aUawNMAxQ6sVgg-2",
content: "y",
color: "#79B8FF",
fontStyle: 0,
},
{
key: "qABRRXThYBzvlFJqemsh1aauYJqWMIPHhxkj-uoxex4-3",
content: " ",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "qABRRXThYBzvlFJqemsh1aauYJqWMIPHhxkj-uoxex4-4",
content: "=",
color: "#F97583",
fontStyle: 0,
},
{
key: "qABRRXThYBzvlFJqemsh1aauYJqWMIPHhxkj-uoxex4-5",
content: " ",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "ulDeJnFQa2Vy-61y5zTTA7sNfq7d4aUawNMAxQ6sVgg-6",
content: "transform",
color: "#B392F0",
fontStyle: 0,
},
{
key: "ulDeJnFQa2Vy-61y5zTTA7sNfq7d4aUawNMAxQ6sVgg-7",
content: "(x)",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "ulDeJnFQa2Vy-61y5zTTA7sNfq7d4aUawNMAxQ6sVgg-8",
content: "\n",
color: "#e1e4e8",
fontStyle: 0,
},
],
},
{
code: "const z = render(y)",
tokens: [
{
key: "qABRRXThYBzvlFJqemsh1aauYJqWMIPHhxkj-uoxex4-0",
content: "const",
color: "#F97583",
fontStyle: 0,
},
{
key: "qABRRXThYBzvlFJqemsh1aauYJqWMIPHhxkj-uoxex4-1",
content: " ",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "wFuhzZNydG-3inU2s1h1gU8Kivya8CAP9gl9S4MBQj4-2",
content: "z",
color: "#79B8FF",
fontStyle: 0,
},
{
key: "qABRRXThYBzvlFJqemsh1aauYJqWMIPHhxkj-uoxex4-3",
content: " ",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "qABRRXThYBzvlFJqemsh1aauYJqWMIPHhxkj-uoxex4-4",
content: "=",
color: "#F97583",
fontStyle: 0,
},
{
key: "qABRRXThYBzvlFJqemsh1aauYJqWMIPHhxkj-uoxex4-5",
content: " ",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "wFuhzZNydG-3inU2s1h1gU8Kivya8CAP9gl9S4MBQj4-6",
content: "render",
color: "#B392F0",
fontStyle: 0,
},
{
key: "wFuhzZNydG-3inU2s1h1gU8Kivya8CAP9gl9S4MBQj4-7",
content: "(y)",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "wFuhzZNydG-3inU2s1h1gU8Kivya8CAP9gl9S4MBQj4-8",
content: "\n",
color: "#e1e4e8",
fontStyle: 0,
},
],
},
],
},
};
window.__BLOCK = { id: "code-snippet-flight", effect: "flight", seq: "flight", duration: 6 };
</script>
<script>
// hf-code-anim.js — self-contained code-animation engine for HyperFrames registry blocks.
//
// Reuses Shiki Magic Move's baked output (window.__TOKENS, baked at author time) and
// drives the animation as a PAUSED GSAP timeline the HyperFrames engine seeks per frame —
// NOT CSS transitions (those break under non-sequential frame seeking). FLIP measurement
// runs once inside document.fonts.ready (glyph metrics must be final). Each block sets
// window.__BLOCK = { id, effect, seq, line?, duration } and includes this engine; the
// engine builds one scene that fills the composition and registers window.__timelines[id].
(function () {
"use strict";
var T = window.__TOKENS || {};
function lineDiv() {
var d = document.createElement("div");
d.className = "line";
return d;
}
// Render a code state's keyed tokens into codeEl as lines of inline-block .tok spans.
function renderState(codeEl, state) {
codeEl.textContent = "";
var keyToEl = new Map();
var line = lineDiv();
codeEl.appendChild(line);
for (var i = 0; i < state.tokens.length; i++) {
var t = state.tokens[i];
if (t.content === "\n") {
line = lineDiv();
codeEl.appendChild(line);
continue;
}
var span = document.createElement("span");
span.className = "tok";
span.textContent = t.content;
span.style.color = t.color;
if (t.fontStyle & 1) span.style.fontStyle = "italic";
if (t.fontStyle & 2) span.style.fontWeight = "700";
line.appendChild(span);
keyToEl.set(t.key, span);
}
var last = codeEl.lastChild;
if (last && last.className === "line" && last.children.length === 0)
codeEl.removeChild(last);
return keyToEl;
}
function rectsOf(keyToEl) {
var m = new Map();
keyToEl.forEach(function (el, k) {
var r = el.getBoundingClientRect();
m.set(k, { left: r.left, top: r.top });
});
return m;
}
function makeScene(surface, withGutter) {
var scene = document.createElement("div");
scene.className = "scene";
if (withGutter) {
var g = document.createElement("div");
g.className = "gutter";
scene.appendChild(g);
}
var code = document.createElement("div");
code.className = "code";
scene.appendChild(code);
surface.appendChild(scene);
return { scene: scene, code: code, gutter: scene.querySelector(".gutter") };
}
function fillGutter(g, n) {
var html = "";
for (var i = 1; i <= n; i++) html += '<span class="ln">' + i + "</span>";
g.innerHTML = html;
}
function splitLines(state) {
var lines = [{ text: "", tokens: [] }];
for (var i = 0; i < state.tokens.length; i++) {
var t = state.tokens[i];
if (t.content === "\n") {
lines.push({ text: "", tokens: [] });
continue;
}
var cur = lines[lines.length - 1];
cur.tokens.push(t);
cur.text += t.content;
}
if (lines.length && lines[lines.length - 1].tokens.length === 0) lines.pop();
return lines;
}
function lineDiff(a, b) {
var n = a.length;
var m = b.length;
var dp = [];
for (var i = 0; i <= n; i++) dp.push(new Array(m + 1).fill(0));
for (var i = n - 1; i >= 0; i--) {
for (var j = m - 1; j >= 0; j--) {
dp[i][j] =
a[i].text === b[j].text
? dp[i + 1][j + 1] + 1
: Math.max(dp[i + 1][j], dp[i][j + 1]);
}
}
var ops = [];
var i = 0;
var j = 0;
while (i < n && j < m) {
if (a[i].text === b[j].text) {
ops.push({ type: "same", line: b[j] });
i++;
j++;
} else if (dp[i + 1][j] >= dp[i][j + 1]) {
ops.push({ type: "del", line: a[i] });
i++;
} else {
ops.push({ type: "add", line: b[j] });
j++;
}
}
while (i < n) ops.push({ type: "del", line: a[i++] });
while (j < m) ops.push({ type: "add", line: b[j++] });
return ops;
}
// ---- single-scene effect builders (fade in, play, hold to end) -------------
var FADE = 0.45;
function flight(tl, surface, states, start) {
var FLY = 0.95;
var STAGGER = 0.26;
var GAP = 20;
var parts = makeScene(surface, false);
parts.code.remove();
var scene = parts.scene;
var target = document.createElement("div");
target.className = "flight-target";
scene.appendChild(target);
var W = surface.clientWidth;
var H = surface.clientHeight;
var blocks = states.map(function (st) {
var blk = document.createElement("div");
blk.className = "flight-block";
renderState(blk, st);
scene.appendChild(blk);
return blk;
});
var n = blocks.length;
// Pass 1: measure natural size, then make every chip the SAME width (symmetry).
var maxW = 0;
var chipH = 0;
blocks.forEach(function (blk) {
var r = blk.getBoundingClientRect();
if (r.width > maxW) maxW = r.width;
if (r.height > chipH) chipH = r.height;
});
blocks.forEach(function (blk) {
blk.style.width = maxW + "px";
});
// Pass 2: evenly-stacked, left-aligned, group centered in the surface.
var stackH = n * chipH + (n - 1) * GAP;
var destLeft = Math.round((W - maxW) / 2);
var topY = Math.round((H - stackH) / 2);
blocks.forEach(function (blk, i) {
var destY = topY + i * (chipH + GAP);
blk.style.left = destLeft + "px";
blk.style.top = destY + "px";
// fly straight in from the left to the shared left edge — clean, no skew
var srcX = destLeft - W * 0.32;
tl.fromTo(
blk,
{ x: srcX - destLeft, opacity: 0, scale: 0.96 },
{ x: 0, opacity: 1, scale: 1, duration: FLY, ease: "power3.out" },
start + FADE + i * STAGGER,
);
});
// Even-padded panel tightly wrapping the uniform stack.
var padX = 30;
var padY = 24;
target.style.left = destLeft - padX + "px";
target.style.top = topY - padY + "px";
target.style.width = maxW + padX * 2 + "px";
target.style.height = stackH + padY * 2 + "px";
var landAt = start + FADE + (n - 1) * STAGGER + FLY;
tl.set(scene, { opacity: 1 }, 0);
tl.to(target, { opacity: 1, duration: 0.5, ease: "power1.out" }, landAt - 0.5);
}
function buildEffect(tl, surface, spec) {
var start = 0.45;
var seq = T[spec.seq];
if (spec.effect === "flight") flight(tl, surface, seq.states, start);
}
function go() {
var spec = window.__BLOCK;
var surface = document.getElementById("surface");
var root = document.getElementById("root");
window.__timelines = window.__timelines || {};
var tl = gsap.timeline({ paused: true });
window.__timelines["code-snippet-flight"] = tl; // register synchronously
document.fonts.ready.then(function () {
tl.from("#editor", { opacity: 0, scale: 0.985, duration: 0.5, ease: "power2.out" }, 0);
buildEffect(tl, surface, spec);
var dur = parseFloat(root.dataset.duration) || tl.duration();
tl.to({}, { duration: dur }, 0); // pad to full composition length
});
}
if (document.readyState === "loading") document.addEventListener("DOMContentLoaded", go);
else go();
})();
</script>
</body>
</html>
@@ -0,0 +1,24 @@
{
"$schema": "https://hyperframes.heygen.com/schema/registry-item.json",
"name": "code-snippet-flight",
"type": "hyperframes:block",
"title": "Code Snippet Flight",
"description": "Discrete code snippets fly in from the side and assemble into a stacked program, staggered. Block-level FLIP.",
"dimensions": {
"width": 1920,
"height": 1080
},
"duration": 6,
"tags": ["code", "code-animation", "flight", "developer"],
"files": [
{
"path": "code-snippet-flight.html",
"target": "compositions/code-snippet-flight.html",
"type": "hyperframes:composition"
}
],
"preview": {
"video": "https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/code-snippet-flight.mp4",
"poster": "https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/code-snippet-flight.png"
}
}
+754
View File
@@ -0,0 +1,754 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=1920, height=1080" />
<title>Code Typing</title>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/gsap.min.js"></script>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html,
body {
width: 1920px;
height: 1080px;
overflow: hidden;
background: #05070b;
}
#root {
position: relative;
width: 1920px;
height: 1080px;
font-family: "JetBrains Mono", monospace;
color: #e6edf3;
}
.bg-field {
position: absolute;
inset: 0;
background: radial-gradient(1200px 700px at 50% 18%, #0e1726 0%, #070b12 55%, #05070b 100%);
}
.bg-grid {
position: absolute;
inset: 0;
background-image:
linear-gradient(rgba(88, 166, 255, 0.05) 1px, transparent 1px),
linear-gradient(90deg, rgba(88, 166, 255, 0.05) 1px, transparent 1px);
background-size: 64px 64px;
mask-image: radial-gradient(1100px 620px at 50% 42%, #000 0%, transparent 78%);
}
.bg-glow {
position: absolute;
width: 900px;
height: 900px;
border-radius: 50%;
filter: blur(120px);
opacity: 0.5;
}
.bg-glow.a {
background: #1f6feb55;
left: -180px;
top: -200px;
}
.bg-glow.b {
background: #2ea04355;
right: -200px;
bottom: -260px;
}
.editor {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
width: 1380px;
height: 800px;
background: #0b0f17;
border: 1px solid #1d2733;
border-radius: 16px;
box-shadow:
0 40px 120px rgba(0, 0, 0, 0.6),
0 2px 0 rgba(255, 255, 255, 0.03) inset;
overflow: hidden;
display: flex;
flex-direction: column;
}
.titlebar {
height: 56px;
flex: 0 0 56px;
display: flex;
align-items: center;
gap: 16px;
padding: 0 22px;
background: linear-gradient(#11161f, #0c111a);
border-bottom: 1px solid #1b2430;
}
.dots {
display: flex;
gap: 10px;
}
.dot {
width: 14px;
height: 14px;
border-radius: 50%;
}
.dot.r {
background: #ff5f57;
}
.dot.y {
background: #febc2e;
}
.dot.g {
background: #28c840;
}
.filename {
font-size: 19px;
color: #8b98a9;
letter-spacing: 0.2px;
}
.filename .accent {
color: #d6e2f0;
}
.code-surface {
position: relative;
flex: 1 1 auto;
overflow: hidden;
}
.scene {
position: absolute;
inset: 0;
opacity: 0;
will-change: opacity;
}
.code {
position: absolute;
top: 38px;
left: 0;
right: 0;
padding: 0 44px 0 104px;
font-size: 30px;
line-height: 46px;
font-variant-ligatures: none;
tab-size: 2;
}
.gutter {
position: absolute;
top: 38px;
left: 0;
width: 84px;
padding-right: 18px;
font-size: 30px;
line-height: 46px;
text-align: right;
color: #828c9b;
user-select: none;
}
.ln {
display: block;
height: 46px;
}
.line {
display: block;
min-height: 46px;
white-space: pre;
position: relative;
z-index: 1;
}
.tok {
display: inline-block;
white-space: pre;
will-change: transform, opacity;
}
.caret {
position: absolute;
top: 0;
left: 0;
width: 3px;
height: 32px;
margin-top: 6px;
background: #58a6ff;
border-radius: 1px;
z-index: 3;
}
.hl-box {
position: absolute;
background: rgba(88, 166, 255, 0.16);
border-left: 3px solid #58a6ff;
border-radius: 6px;
z-index: 0;
overflow: hidden;
}
.diff-line {
display: block;
min-height: 0;
line-height: 46px;
white-space: pre;
position: relative;
overflow: hidden;
padding-left: 14px;
}
.diff-sign {
display: inline-block;
width: 22px;
color: #41506220;
}
.diff-del {
background: rgba(248, 81, 73, 0.12);
box-shadow: inset 3px 0 #f85149;
}
.diff-add {
background: rgba(63, 185, 80, 0.12);
box-shadow: inset 3px 0 #3fb950;
}
.diff-del .diff-sign {
color: #f85149;
}
.diff-add .diff-sign {
color: #3fb950;
}
.flight-block {
position: absolute;
white-space: pre;
font-size: 27px;
line-height: 40px;
padding: 11px 22px;
border-radius: 11px;
background: #0f1722;
border: 1px solid #233044;
box-shadow: 0 16px 44px rgba(0, 0, 0, 0.5);
will-change: transform, opacity;
}
.flight-target {
position: absolute;
border: 1.5px dashed #2a3a4f;
border-radius: 14px;
opacity: 0;
}
</style>
</head>
<body>
<div
id="root"
data-composition-id="code-typing"
data-start="0"
data-duration="5"
data-width="1920"
data-height="1080"
>
<div class="bg-field" data-layout-ignore></div>
<div class="bg-grid" data-layout-ignore></div>
<div class="bg-glow a" data-layout-ignore></div>
<div class="bg-glow b" data-layout-ignore></div>
<div class="editor" id="editor" data-layout-ignore>
<div class="titlebar">
<div class="dots">
<span class="dot r"></span><span class="dot y"></span><span class="dot g"></span>
</div>
<span class="filename"><span class="accent">loadConfig.js</span> — Code Typing</span>
</div>
<div class="code-surface" id="surface"></div>
</div>
</div>
<script>
window.__TOKENS = {
feature: {
lang: "js",
theme: "github-dark",
bg: "#24292e",
fg: "#e1e4e8",
states: [
{
code: 'async function loadConfig(path) {\n const raw = await readFile(path, "utf8")\n const config = JSON.parse(raw)\n return validate(config)\n}',
tokens: [
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-0",
content: "async",
color: "#F97583",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-1",
content: " ",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-2",
content: "function",
color: "#F97583",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-3",
content: " ",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-4",
content: "loadConfig",
color: "#B392F0",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-5",
content: "(",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-6",
content: "path",
color: "#FFAB70",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-7",
content: ") {",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-8",
content: "\n",
color: "#e1e4e8",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-9",
content: " ",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-10",
content: "const",
color: "#F97583",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-11",
content: " ",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-12",
content: "raw",
color: "#79B8FF",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-13",
content: " ",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-14",
content: "=",
color: "#F97583",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-15",
content: " ",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-16",
content: "await",
color: "#F97583",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-17",
content: " ",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-18",
content: "readFile",
color: "#B392F0",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-19",
content: "(path,",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-20",
content: " ",
color: "#e1e4e8",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-21",
content: '"utf8"',
color: "#9ECBFF",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-22",
content: ")",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-23",
content: "\n",
color: "#e1e4e8",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-24",
content: " ",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-25",
content: "const",
color: "#F97583",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-26",
content: " ",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-27",
content: "config",
color: "#79B8FF",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-28",
content: " ",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-29",
content: "=",
color: "#F97583",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-30",
content: " ",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-31",
content: "JSON",
color: "#79B8FF",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-32",
content: ".",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-33",
content: "parse",
color: "#B392F0",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-34",
content: "(raw)",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-35",
content: "\n",
color: "#e1e4e8",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-36",
content: " ",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-37",
content: "return",
color: "#F97583",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-38",
content: " ",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-39",
content: "validate",
color: "#B392F0",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-40",
content: "(config)",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-41",
content: "\n",
color: "#e1e4e8",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-42",
content: "}",
color: "#E1E4E8",
fontStyle: 0,
},
{
key: "SPx9u4TyzfMqSHpbjPHnWFQU8lPVEPieF6W5Vtu-bNA-43",
content: "\n",
color: "#e1e4e8",
fontStyle: 0,
},
],
},
],
},
};
window.__BLOCK = { id: "code-typing", effect: "typing", seq: "feature", duration: 5 };
</script>
<script>
// hf-code-anim.js — self-contained code-animation engine for HyperFrames registry blocks.
//
// Reuses Shiki Magic Move's baked output (window.__TOKENS, baked at author time) and
// drives the animation as a PAUSED GSAP timeline the HyperFrames engine seeks per frame —
// NOT CSS transitions (those break under non-sequential frame seeking). FLIP measurement
// runs once inside document.fonts.ready (glyph metrics must be final). Each block sets
// window.__BLOCK = { id, effect, seq, line?, duration } and includes this engine; the
// engine builds one scene that fills the composition and registers window.__timelines[id].
(function () {
"use strict";
var T = window.__TOKENS || {};
function lineDiv() {
var d = document.createElement("div");
d.className = "line";
return d;
}
// Render a code state's keyed tokens into codeEl as lines of inline-block .tok spans.
function renderState(codeEl, state) {
codeEl.textContent = "";
var keyToEl = new Map();
var line = lineDiv();
codeEl.appendChild(line);
for (var i = 0; i < state.tokens.length; i++) {
var t = state.tokens[i];
if (t.content === "\n") {
line = lineDiv();
codeEl.appendChild(line);
continue;
}
var span = document.createElement("span");
span.className = "tok";
span.textContent = t.content;
span.style.color = t.color;
if (t.fontStyle & 1) span.style.fontStyle = "italic";
if (t.fontStyle & 2) span.style.fontWeight = "700";
line.appendChild(span);
keyToEl.set(t.key, span);
}
var last = codeEl.lastChild;
if (last && last.className === "line" && last.children.length === 0)
codeEl.removeChild(last);
return keyToEl;
}
function rectsOf(keyToEl) {
var m = new Map();
keyToEl.forEach(function (el, k) {
var r = el.getBoundingClientRect();
m.set(k, { left: r.left, top: r.top });
});
return m;
}
function makeScene(surface, withGutter) {
var scene = document.createElement("div");
scene.className = "scene";
if (withGutter) {
var g = document.createElement("div");
g.className = "gutter";
scene.appendChild(g);
}
var code = document.createElement("div");
code.className = "code";
scene.appendChild(code);
surface.appendChild(scene);
return { scene: scene, code: code, gutter: scene.querySelector(".gutter") };
}
function fillGutter(g, n) {
var html = "";
for (var i = 1; i <= n; i++) html += '<span class="ln">' + i + "</span>";
g.innerHTML = html;
}
function splitLines(state) {
var lines = [{ text: "", tokens: [] }];
for (var i = 0; i < state.tokens.length; i++) {
var t = state.tokens[i];
if (t.content === "\n") {
lines.push({ text: "", tokens: [] });
continue;
}
var cur = lines[lines.length - 1];
cur.tokens.push(t);
cur.text += t.content;
}
if (lines.length && lines[lines.length - 1].tokens.length === 0) lines.pop();
return lines;
}
function lineDiff(a, b) {
var n = a.length;
var m = b.length;
var dp = [];
for (var i = 0; i <= n; i++) dp.push(new Array(m + 1).fill(0));
for (var i = n - 1; i >= 0; i--) {
for (var j = m - 1; j >= 0; j--) {
dp[i][j] =
a[i].text === b[j].text
? dp[i + 1][j + 1] + 1
: Math.max(dp[i + 1][j], dp[i][j + 1]);
}
}
var ops = [];
var i = 0;
var j = 0;
while (i < n && j < m) {
if (a[i].text === b[j].text) {
ops.push({ type: "same", line: b[j] });
i++;
j++;
} else if (dp[i + 1][j] >= dp[i][j + 1]) {
ops.push({ type: "del", line: a[i] });
i++;
} else {
ops.push({ type: "add", line: b[j] });
j++;
}
}
while (i < n) ops.push({ type: "del", line: a[i++] });
while (j < m) ops.push({ type: "add", line: b[j++] });
return ops;
}
// ---- single-scene effect builders (fade in, play, hold to end) -------------
var FADE = 0.45;
function typing(tl, surface, state, start) {
var PER = 0.028; // seconds per character — smooth, fast typewriter
var parts = makeScene(surface, true);
var code = parts.code;
// Per-CHARACTER render (granular reveal reads smoother than per-token pops).
code.textContent = "";
var line = document.createElement("div");
line.className = "line";
code.appendChild(line);
var chars = [];
for (var i = 0; i < state.tokens.length; i++) {
var t = state.tokens[i];
if (t.content === "\n") {
line = document.createElement("div");
line.className = "line";
code.appendChild(line);
continue;
}
for (var c = 0; c < t.content.length; c++) {
var span = document.createElement("span");
span.className = "tok";
span.textContent = t.content[c];
span.style.color = t.color;
if (t.fontStyle & 1) span.style.fontStyle = "italic";
if (t.fontStyle & 2) span.style.fontWeight = "700";
line.appendChild(span);
chars.push({ el: span, ws: t.content[c] === " " });
}
}
var last = code.lastChild;
if (last && last.className === "line" && last.children.length === 0)
code.removeChild(last);
fillGutter(parts.gutter, code.querySelectorAll(".line").length);
var base = code.getBoundingClientRect();
var caret = document.createElement("div");
caret.className = "caret";
code.appendChild(caret);
tl.set(parts.scene, { opacity: 0 }, 0);
tl.to(parts.scene, { opacity: 1, duration: FADE, ease: "power1.out" }, start);
var t0 = start + FADE;
if (chars.length) {
var r0 = chars[0].el.getBoundingClientRect();
tl.set(caret, { x: r0.left - base.left, y: r0.top - base.top, opacity: 1 }, 0);
}
chars.forEach(function (c, i) {
var at = t0 + i * PER;
tl.set(c.el, { opacity: 0 }, 0);
// soft per-char fade (overlaps neighbors → smooth wave); whitespace appears instantly
tl.to(c.el, { opacity: 1, duration: c.ws ? 0.01 : 0.12, ease: "power1.out" }, at);
// caret glides at constant speed to each char's right edge (linear = no stop-start jitter)
var r = c.el.getBoundingClientRect();
tl.to(
caret,
{ x: r.right - base.left, y: r.top - base.top, duration: PER, ease: "none" },
at,
);
});
}
function buildEffect(tl, surface, spec) {
var start = 0.45;
var seq = T[spec.seq];
if (spec.effect === "typing") typing(tl, surface, seq.states[0], start);
}
function go() {
var spec = window.__BLOCK;
var surface = document.getElementById("surface");
var root = document.getElementById("root");
window.__timelines = window.__timelines || {};
var tl = gsap.timeline({ paused: true });
window.__timelines["code-typing"] = tl; // register synchronously
document.fonts.ready.then(function () {
tl.from("#editor", { opacity: 0, scale: 0.985, duration: 0.5, ease: "power2.out" }, 0);
buildEffect(tl, surface, spec);
var dur = parseFloat(root.dataset.duration) || tl.duration();
tl.to({}, { duration: dur }, 0); // pad to full composition length
});
}
if (document.readyState === "loading") document.addEventListener("DOMContentLoaded", go);
else go();
})();
</script>
</body>
</html>
@@ -0,0 +1,24 @@
{
"$schema": "https://hyperframes.heygen.com/schema/registry-item.json",
"name": "code-typing",
"type": "hyperframes:block",
"title": "Code Typing",
"description": "Token-streamed typing reveal with a caret that tracks the frontier — deterministic, no CSS animation.",
"dimensions": {
"width": 1920,
"height": 1080
},
"duration": 5,
"tags": ["code", "code-animation", "typing", "developer"],
"files": [
{
"path": "code-typing.html",
"target": "compositions/code-typing.html",
"type": "hyperframes:composition"
}
],
"preview": {
"video": "https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/code-typing.mp4",
"poster": "https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/code-typing.png"
}
}
+36
View File
@@ -471,6 +471,42 @@
"name": "code-snippet-visual-studio-light",
"type": "hyperframes:block"
},
{
"name": "code-morph",
"type": "hyperframes:block"
},
{
"name": "code-snippet-flight",
"type": "hyperframes:block"
},
{
"name": "code-typing",
"type": "hyperframes:block"
},
{
"name": "code-diff",
"type": "hyperframes:block"
},
{
"name": "code-highlight",
"type": "hyperframes:block"
},
{
"name": "code-scroll",
"type": "hyperframes:block"
},
{
"name": "code-3d-extrude",
"type": "hyperframes:block"
},
{
"name": "code-shader-dissolve",
"type": "hyperframes:block"
},
{
"name": "code-particle-assemble",
"type": "hyperframes:block"
},
{
"name": "flowchart-vertical",
"type": "hyperframes:block"
+14 -9
View File
@@ -519,17 +519,19 @@ function main(): void {
// Items with the same first tag are grouped together. Items without tags
// go into an "Other" group. Groups are sorted with a priority order.
const GROUP_ORDER: Record<string, number> = {
Captions: 0,
"HTML-in-Canvas": 1,
"Social Overlays": 2,
"Shader Transitions": 3,
"CSS Transitions": 4,
Showcases: 5,
Data: 6,
Effects: 7,
Blocks: 8,
"Code Animations": 0,
Captions: 1,
"HTML-in-Canvas": 2,
"Social Overlays": 3,
"Shader Transitions": 4,
"CSS Transitions": 5,
Showcases: 6,
Data: 7,
Effects: 8,
Blocks: 9,
};
// fallow-ignore-next-line complexity
function groupForItem(entry: CatalogEntry): string {
const tags = entry.tags;
// Two-tag combos for specific grouping
@@ -537,6 +539,9 @@ function main(): void {
if (tags.includes("transition") && tags.includes("showcase")) return "CSS Transitions";
if (tags.includes("captions")) return "Captions";
if (tags.includes("html-in-canvas")) return "HTML-in-Canvas";
// Code animations (morph, flight, diff, …) — keyed on the code-animation tag so
// they group separately from the static code-snippet themes.
if (tags.includes("code-animation")) return "Code Animations";
// Single-tag mapping
if (tags.includes("social")) return "Social Overlays";
if (tags.includes("transition"))