mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-10-30 09:16:55 +00:00
feat(catalog): add the seven 3D-motion pieces as installable registry blocks (#4056)
* feat(catalog): add the seven 3D-motion pieces as installable registry blocks Each piece ships as a registry block with a CDN poster and preview, a live catalog detail page and its source. Scripts travel inside the catalog payload (shared vendor libraries fetched once, item scripts inline, the one HDR as a data URI) because the docs host only serves fonts, images and JSON. NO_ESSAY_COMMENTS_OK: the long comment blocks are in vendored libraries and the ported frost source under registry/blocks, not hand-written here. HF_MAX_NONLFS_KB=6000: four hosted texture PNGs under docs/public/catalog/items are runtime assets the docs host must serve as files. fallow skipped locally: it audits against origin/main and this branch is stacked on an older main. * chore(catalog): regenerate gallery data and nav after rebasing onto main * fix(catalog): read the install manifest through the bounded project reader * fix(catalog): a script the inliner missed fails the payload instead of shipping a dead URL * refactor(catalog): the leftover-script check reuses the reference finder * fix(catalog): frost and glass textures load from the CDN and the iteration presets are gone * chore(catalog): regenerate the frost and glass payloads and nav * fix(catalog): frost and glass textures are hosted through the registry url field, not hardcoded * chore(catalog): regenerate the frost and glass payloads against the hosted texture URLs * fix(catalog): the hosted-reference rewrite refuses a bare name it cannot safely replace * ci(catalog): verify changed catalog payloads in headless Chrome and check the docs catalog * feat(catalog): every item page follows the shared layout with a Tune panel and tabs * fix(catalog): let the texture-mask item host its masks so its tile plays live * chore(catalog): regenerate item pages, gallery data and the texture-mask payload * chore(catalog): regenerate item pages after merging main * fix(catalog): declared-only variables get the Tune panel, Code and Docs tabs capped * feat(catalog): give the blend-difference caption a demo scene so its tile plays live * chore(catalog): live payload for the blend-difference caption * perf(catalog): heavy 3d pieces compile and draw before ready, not on the first visible frame * feat(catalog): the avatar promo card hosts its videos, audio and fonts on the CDN * feat(catalog): the avatar promo card tile plays live from its CDN-hosted media * chore(catalog): the avatar promo card plays live, no allowlist left * chore(catalog): shorten two comment blocks * chore(catalog): trim first-party comment blocks to the comment-check limits * chore(catalog): shorten two frost source comments * fix(catalog): the Docs tab renders as prose again, and the detail page honours reduced motion Frame-mode pages carry no prose wrapper, and the detail component sat inside not-prose, so the Docs slot's headings, paragraphs, links and lists had no styling at all. The slots now carry their own prose classes and the chrome around them keeps not-prose. The recorded clip also gets the reduced-motion guard the docs-snippet check requires, and the generator reads stability from the source metadata type so the scripts typecheck passes. * fix(catalog): docs-tab paragraphs render as blocks Mintlify emits paragraphs as inline spans, so two paragraphs in the Docs slot ran together. * fix(ci): drop the duplicate docs_catalog job output git's line-based auto-merge silently duplicated the outputs key when main and this branch each added it on non-adjacent lines during the rebase; oxfmt's yaml parser rejects the repeated map key. * fix(catalog): close four CodeQL alerts in the frost source * chore(catalog): allowlist the avatar-promo-card CDN asset deletions, format sync-docs-catalog check-no-main-deletions.mjs was rejecting 11 local mp4/font files this branch already deleted in favor of CDN hosting; scripts/sync-docs-catalog.mjs had one line over the 100-col print width from an earlier edit in this branch. * fix(catalog): detail tabs use docs prose spacing, Install first; previews skip no-WebGPU runners * fix(catalog): the docs tab scrolls with the page instead of a capped inner box * refactor(catalog): split the payload asset and verify scripts into single-purpose helpers * refactor(catalog): split the preview generator into single-purpose helpers * ci(catalog): fail when the committed catalog differs from generator output The docs host serves the committed catalog and nothing regenerates it, so a stale copy shipped silently. The catalog payloads job now generates into a temp dir and compares it with the committed tree. * chore(catalog): regenerate docs/public/catalog with the refactored scripts One full regeneration from an empty output tree with the current scripts. The tree grows from 58.4 MB to 65.1 MB. Two hosted assets are about 2 MB each (an avatar png and a wav); the same bytes are already committed under assets/, so HF_MAX_NONLFS_KB is raised for this commit only. * fix(catalog): run the catalog script tests under the runner they are written for The rebase put three node:test files in the vitest half of test:scripts, where they report "No test suite found". They now sit in the node --test list. * fix(catalog): a WebGPU item without an adapter shows its recorded clip and says why * ci(catalog): the drift check runs from a committed fetch mirror with no network Generation now serves every font and CDN script fetch from scripts/catalog-fetch-mirror and fails when a fetch is not in it, even if the caller swallowed the error. CATALOG_FETCH_MIRROR=record refreshes the mirror from the live network. The docs_catalog filter also watches packages/core, packages/producer and the mirror, and the 13 re-hashed font files are listed in ALLOWED_DELETIONS. * chore(catalog): clean up the temp font cache and sort the mirror index without a locale * fix(catalog): hide Tune when the clip stands in, time out the adapter request, share one check * fix(catalog): the adapter probe survives a sync throw and a hang; test the clip guard * fix(catalog): keep the adapter probe inside the component, Mintlify evaluates only exports * fix(catalog): run the detail and page generator tests in CI; no Tune flash while probing WebGPU * docs(catalog): credit vendored libraries and make each SKILL.md list what the block installs CREDITS.md names every vendored dependency with its upstream and licence, and flags the two leaf textures whose origin is still to be confirmed. Each SKILL.md Files table now comes from the block manifest and its install and render paths point at compositions/<name>/. A test checks that every path a SKILL.md names is a manifest file that exists. * fix(catalog): a declared WebGPU item passes verify by its manifest tag, not by console wording verify-catalog-payloads forgave a missing adapter only by matching the markup and the message, which library-reported errors and widgets never satisfy. The webgpu tag in the item manifest is now the owner: a tagged item ignores its page and console errors, an untagged one gets no forgiveness, and a marker payload with no html is listed instead of crashing the run. * docs(catalog): correct the credits per block and guard the mount and render paths CREDITS.md states the three.js version and vendored-or-CDN status per block, adds d3-delaunay, Clipper on the CDN and the Google Fonts loads, and lists every texture with no known origin. The SKILL.md test now also checks the mount and render paths against the manifest composition. * test(catalog): read each block manifest once in the SKILL.md file test The two checks shared a copied manifest read, which fallow flagged as a clone with a high score. * fix(catalog): commit the hosted item files a gitignore rule kept out, and check git not the disk The compositions/ and *-ad/ ignore rules matched folder names under docs/public/catalog/items, so 33 hosted fonts, textures and marks were generated but never committed and would 404 on the docs host. The rules now have a negation for that tree and the files are added. The drift check read the checkout, where git-ignored files still exist, so it passed locally and failed only in CI. It now unpacks HEAD with git archive and compares that. * fix(catalog): a tile keeps its poster until the composition has painted * fix(catalog): a tagged WebGPU item forgives only the adapter-absent error, and test the wiring The manifest webgpu tag now decides whether an item may lack an adapter, and the error wording decides which lines are that absence. Any other page or console error on the item, and every failed request, still fails. The per-item decision is one exported function the tests drive, marker payloads are parsed by a tested helper, and the SKILL.md variable counts are checked against their tables (cuboid-carousel said forty-nine for forty). * fix(catalog): the pinned group's posters load eagerly at high priority * fix(catalog): type the tile reveal test so the scripts typecheck passes * fix(catalog): stop the adapter signature matching an unrelated failed adapter request failed to request .*adapter also matched errors about other adapters. It now matches only a request for the WebGPU adapter, so a tagged item cannot hide an unrelated failure behind it. * test(catalog): a tagged item still fails on an error about some other adapter * feat(catalog): the install command sits above the preview, as on the live catalog * fix(catalog): find the install slot through Mintlify's child wrapper * fix(docs): catalog copy buttons keep their size when the label swaps to Copied * refactor(docs): name the shown copy label once in the catalog copy button * fix(docs): primary catalog button keeps a readable label on hover and after copy * fix(docs): inset the catalog tab strip and tab content to match the boxed panels
This commit is contained in:
+2
-2
@@ -967,8 +967,8 @@
|
||||
// file-level fingerprint invalidation re-flags the inherited findings even at
|
||||
// unchanged line numbers.
|
||||
"packages/core/src/runtime/picker.ts",
|
||||
// generate-catalog-payloads.ts: hostsOwnDirectory, parseArgs and main are
|
||||
// untouched. buildPayload's canvas-draw-element branch now writes a marker
|
||||
// generate-catalog-payloads.ts: hostsOwnDirectory, parseArgs and generate (the
|
||||
// former main body, moved unchanged) are untouched. buildPayload's canvas-draw-element branch now writes a marker
|
||||
// via a small writePayload helper instead of dropping the payload, but at
|
||||
// the same branch count — its flagged 14-branch CRAP score predates this PR.
|
||||
"scripts/generate-catalog-payloads.ts",
|
||||
|
||||
@@ -61,7 +61,11 @@ jobs:
|
||||
docs_catalog:
|
||||
- "docs/**"
|
||||
- "registry/**"
|
||||
- "scripts/*docs-catalog*"
|
||||
- "scripts/*catalog*"
|
||||
- "scripts/catalog-fetch-mirror/**"
|
||||
- "scripts/docs-catalog-shared.mjs"
|
||||
- "packages/core/**"
|
||||
- "packages/producer/**"
|
||||
- "package.json"
|
||||
- ".github/workflows/ci.yml"
|
||||
code:
|
||||
@@ -168,6 +172,30 @@ jobs:
|
||||
- run: bun run lint
|
||||
- run: bun run check:docs-catalog
|
||||
|
||||
# Loads every catalog payload a PR changes in headless Chrome. A static scan of the
|
||||
# markup cannot see an asset a script fetches, and the docs host serves only fonts,
|
||||
# images and JSON, so a payload that runs locally can 404 there.
|
||||
catalog-payloads:
|
||||
name: Catalog payloads
|
||||
needs: changes
|
||||
if: needs.changes.outputs.docs_catalog == 'true'
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
steps:
|
||||
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
|
||||
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
||||
with:
|
||||
node-version: 22
|
||||
- run: bash scripts/ci/install-workspace-dependencies.sh
|
||||
- run: bun run --filter '@hyperframes/{parsers,lint,studio-server}' build
|
||||
- run: bun run --cwd packages/core build
|
||||
- run: bun run --cwd packages/core build:hyperframes-runtime
|
||||
- run: bun run verify:catalog-payloads --changed origin/main
|
||||
- run: bun run check:catalog-drift
|
||||
|
||||
# `fallow audit` runs dead-code + complexity + duplication analysis scoped to
|
||||
# the changed files. The default `--gate new-only` means existing legacy
|
||||
# findings don't fail the build — only NEW issues introduced by the PR do.
|
||||
|
||||
@@ -188,3 +188,7 @@ tmp/
|
||||
|
||||
# Local Chrome-for-Testing downloads (large binaries, never commit)
|
||||
chrome/
|
||||
|
||||
# Hosted catalog item files are generated output the docs host serves; the rules above match their folder names
|
||||
!docs/public/catalog/items/**/compositions/
|
||||
!docs/public/catalog/items/*-ad/
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
"packages/producer/tests/**/src/vendor/**",
|
||||
"docs/public/catalog/",
|
||||
"registry/blocks/**/lib/*.iife.js",
|
||||
"registry/blocks/{glass-shard-title,code-slice-hero,cuboid-carousel,frost-sequence-camera-orbit,orbit-card}/**/*.{js,mjs}",
|
||||
"registry/blocks/frost-sequence-camera-orbit/source/**",
|
||||
"chrome/"
|
||||
],
|
||||
"overrides": [
|
||||
|
||||
@@ -12,6 +12,23 @@ packages/**/__goldens__/
|
||||
# vendored third-party bundles inside skills — never reformat
|
||||
skills/**/assets/vendor/
|
||||
skills/**/*.min.js
|
||||
# vendored/bundled third-party JS inside registry blocks — never reformat.
|
||||
# Without this the format hook silently re-pretty-prints a minified bundle,
|
||||
# roughly doubling its size. Listed by exact file: these blocks also ship
|
||||
# hand-authored source (glass-main.mjs, surface.js, orbit-scene.js, etc) that must stay formattable.
|
||||
registry/blocks/**/lib/*.iife.js
|
||||
registry/blocks/frost-sequence-camera-orbit/assets/frost.js
|
||||
registry/blocks/frost-sequence-camera-orbit/assets/motion.js
|
||||
registry/blocks/frost-sequence-camera-orbit/assets/gsap-3.14.2.min.js
|
||||
registry/blocks/glass-shard-title/assets/glass-main.js
|
||||
registry/blocks/code-slice-hero/assets/gsap-3.14.2.min.js
|
||||
registry/blocks/cuboid-carousel/assets/three.core.min.js
|
||||
registry/blocks/cuboid-carousel/assets/three.module.min.js
|
||||
registry/blocks/cuboid-carousel/assets/gsap-3.14.2.min.js
|
||||
registry/blocks/cuboid-carousel/assets/addons/**/*.js
|
||||
registry/blocks/orbit-card/assets/three.core.min.js
|
||||
registry/blocks/orbit-card/assets/three.module.min.js
|
||||
registry/blocks/orbit-card/assets/gsap-3.14.2.min.js
|
||||
# reference snippets with intentional pseudo-markup (literal "..." attributes)
|
||||
skills/talking-head-recut/references/frames/polaroid.html
|
||||
# repo-native skill examples with intentional pseudo-markup placeholders
|
||||
|
||||
+31
@@ -28,3 +28,34 @@ broader Node.js ecosystem.
|
||||
- **[mediabunny](https://github.com/Vanilagy/mediabunny)** — media toolkit used
|
||||
in the studio for fast metadata extraction from file headers. Licensed under
|
||||
the [Mozilla Public License 2.0 (MPL-2.0)](https://mozilla.org/MPL/2.0/).
|
||||
- The seven 3D-motion catalog pieces (`canopy-part-title`, `glass-shard-title`,
|
||||
`code-slice-hero`, `frost-sequence-camera-orbit`, `cuboid-carousel`,
|
||||
`orbit-card`, `wireframe-portal-title`) are contributed with their author's
|
||||
permission under this repository's license. What they vendor or load, by upstream:
|
||||
- **[three.js](https://threejs.org)**, MIT. Vendored as r185 in `frost-sequence-camera-orbit`,
|
||||
`cuboid-carousel` and `orbit-card` (`Three-LICENSE.txt` beside the copy); bundled as r181 inside
|
||||
`glass-shard-title`'s `glass-main.js`; loaded from the jsDelivr CDN by `canopy-part-title`
|
||||
(0.170.0) and `wireframe-portal-title` (0.181.2).
|
||||
- **[GSAP](https://gsap.com)** 3.14.2, under the [GSAP Standard License](https://gsap.com/standard-license)
|
||||
(not an OSI open-source licence). Vendored in `code-slice-hero`, `cuboid-carousel`,
|
||||
`frost-sequence-camera-orbit` and `orbit-card` with `GSAP-NOTICE.txt`; loaded from the CDN by
|
||||
`glass-shard-title`, `canopy-part-title` and `wireframe-portal-title`.
|
||||
- **[three-mesh-bvh](https://github.com/gkjohnson/three-mesh-bvh)** 0.9.14 and
|
||||
**[opentype.js](https://github.com/opentypejs/opentype.js)** 2.x (range in the block's
|
||||
`source/package.json`), both MIT, bundled into the frost block's `frost.js`.
|
||||
- **[Clipper](https://sourceforge.net/projects/jsclipper/)** 6.4.2 (JavaScript port of Angus
|
||||
Johnson's Clipper), Boost Software License 1.0: build-time source of `frost-sequence-camera-orbit`
|
||||
and loaded from the CDN as `clipper-lib` by `wireframe-portal-title`.
|
||||
- **[d3-delaunay](https://github.com/d3/d3-delaunay)** 6.0.4, ISC, loaded from the CDN by
|
||||
`glass-shard-title`.
|
||||
- **[Geist](https://github.com/vercel/geist-font)**, **[Archivo](https://github.com/Omnibus-Type/Archivo)**
|
||||
and **[Cormorant Garamond](https://github.com/CatharsisFonts/Cormorant)**, SIL Open Font License 1.1,
|
||||
vendored with their licence text; `canopy-part-title` (Gelasio) and `cuboid-carousel` (Inter) load
|
||||
their fonts from Google Fonts at run time.
|
||||
- **[Ferndale Studio 01](https://polyhaven.com/a/ferndale_studio_01)** HDR from Poly Haven, CC0
|
||||
(`glass-shard-title`); it needs no licence text.
|
||||
- Origin to be confirmed with the author: `canopy-part-title/assets/leaf-surface-color.webp` and
|
||||
`leaf-surface-normal.webp`, `glass-shard-title/assets/matcap-1.png`,
|
||||
`frost-sequence-camera-orbit/assets/shards-atlas.png`,
|
||||
`frost-sequence-camera-orbit/assets/textures/ice-inclusions-generated.png` and
|
||||
`frost-sequence-camera-orbit/assets/textures/bluenoise64.png`.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,28 +1,28 @@
|
||||
---
|
||||
title: "App Showcase"
|
||||
description: "Fitness app product showcase with three floating smartphone screens"
|
||||
mode: "frame"
|
||||
---
|
||||
|
||||
import { CatalogDetail, CatalogSlot } from "/snippets/catalog-detail.jsx";
|
||||
import { InstallCommand } from "/snippets/install-command.jsx";
|
||||
|
||||
## Install
|
||||
<CatalogDetail
|
||||
previewSrc="/public/catalog/blocks/app-showcase.json"
|
||||
compositionId="app-showcase"
|
||||
compositionSrc="compositions/app-showcase.html"
|
||||
title="App Showcase"
|
||||
description="Fitness app product showcase with three floating smartphone screens"
|
||||
variables={[]}
|
||||
meta={{"duration":5.5,"width":1920,"height":1080,"category":"Showcases","badge":"Stable","codeLines":947}}
|
||||
attribution={{"path":"registry/blocks/app-showcase","tags":["showcase","app","3d"]}}
|
||||
hasCode
|
||||
rawUrl="https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/app-showcase/app-showcase.html"
|
||||
>
|
||||
|
||||
<InstallCommand command="npx hyperframes add app-showcase" item="app-showcase" />
|
||||
<CatalogSlot slot="code">
|
||||
|
||||
That writes one file: `compositions/app-showcase.html`.
|
||||
|
||||
<iframe
|
||||
className="w-full aspect-video rounded-xl border-0 bg-zinc-100 dark:bg-zinc-800"
|
||||
title="app-showcase preview"
|
||||
loading="lazy"
|
||||
srcDoc={`<!doctype html><html><head><meta charset="utf-8"><style>html,body{margin:0;height:100%;overflow:hidden;background:transparent}hyperframes-player{display:block;width:100%;height:100%}</style><script src="https://cdn.jsdelivr.net/npm/@hyperframes/player@latest/dist/hyperframes-player.global.js"><\/script></head><body><script>fetch("/public/catalog/blocks/app-showcase.json").then(function(r){return r.json()}).then(function(d){var p=document.createElement("hyperframes-player");p.setAttribute("srcdoc",d.html);p.setAttribute("controls","");p.setAttribute("autoplay","");p.setAttribute("loop","");p.setAttribute("muted","");p.setAttribute("poster","https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/app-showcase.png");document.body.appendChild(p)});<\/script></body></html>`}
|
||||
/>
|
||||
|
||||
## Source
|
||||
|
||||
<Accordion title="app-showcase.html">
|
||||
|
||||
```html
|
||||
```html app-showcase.html
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
@@ -972,16 +972,37 @@ That writes one file: `compositions/app-showcase.html`.
|
||||
</html>
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
</CatalogSlot>
|
||||
|
||||
## Usage
|
||||
<CatalogSlot slot="install">
|
||||
|
||||
After installing, add the block to your host composition:
|
||||
<InstallCommand command="npx hyperframes add app-showcase" item="app-showcase" />
|
||||
|
||||
```html
|
||||
<div data-composition-id="app-showcase" data-composition-src="compositions/app-showcase.html" data-start="0" data-duration="5.5" data-track-index="1" data-width="1920" data-height="1080"></div>
|
||||
That writes one file: `compositions/app-showcase.html`.
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
<CatalogSlot slot="docs">
|
||||
|
||||
## Add it to your video
|
||||
|
||||
It runs for 5.5 seconds at 1920×1080. Paste this into your composition:
|
||||
|
||||
```html index.html
|
||||
<div
|
||||
data-composition-id="app-showcase"
|
||||
data-composition-src="compositions/app-showcase.html"
|
||||
data-start="0"
|
||||
data-duration="5.5"
|
||||
data-track-index="1"
|
||||
data-width="1920"
|
||||
data-height="1080"
|
||||
></div>
|
||||
```
|
||||
|
||||
Move it in time with `data-start`. Put it on a different timeline row with
|
||||
`data-track-index`. See [data attributes](/concepts/data-attributes) for the rest.
|
||||
|
||||
{/* hf:generated-footer */}
|
||||
|
||||
Tagged `showcase` `app` `3d`.
|
||||
@@ -991,3 +1012,7 @@ Tagged `showcase` `app` `3d`.
|
||||
- [Browse the complete Catalog](/catalog)
|
||||
- [Add assets and Catalog items in Studio](/studio/assets-and-blocks)
|
||||
- [Build a richer composition](/go-further)
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
</CatalogDetail>
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
---
|
||||
title: "Apple Money Count"
|
||||
description: "Apple-style finance counter that counts from $0 to $10,000, flashes green, and bursts money icons with sound."
|
||||
mode: "frame"
|
||||
---
|
||||
|
||||
import { CatalogDetail, CatalogSlot } from "/snippets/catalog-detail.jsx";
|
||||
import { InstallCommand } from "/snippets/install-command.jsx";
|
||||
|
||||
## Install
|
||||
<CatalogDetail
|
||||
previewSrc="/public/catalog/blocks/apple-money-count.json"
|
||||
compositionId="apple-money-count"
|
||||
compositionSrc="compositions/apple-money-count.html"
|
||||
title="Apple Money Count"
|
||||
description="Apple-style finance counter that counts from $0 to $10,000, flashes green, and bursts money icons with sound."
|
||||
variables={[]}
|
||||
meta={{"duration":5,"width":1920,"height":1080,"category":"Showcases","badge":"Stable","codeLines":320}}
|
||||
attribution={{"author":"Stronkter","authorUrl":"https://x.com/Stronkter","path":"registry/blocks/apple-money-count","tags":["showcase","finance","kinetic","youtube","sfx"]}}
|
||||
hasCode
|
||||
rawUrl="https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/apple-money-count/apple-money-count.html"
|
||||
>
|
||||
|
||||
<InstallCommand command="npx hyperframes add apple-money-count" item="apple-money-count" />
|
||||
<CatalogSlot slot="code">
|
||||
|
||||
That writes `compositions/apple-money-count.html`, plus 1 supporting file under `assets/`.
|
||||
|
||||
<iframe
|
||||
className="w-full aspect-video rounded-xl border-0 bg-zinc-100 dark:bg-zinc-800"
|
||||
title="apple-money-count preview"
|
||||
loading="lazy"
|
||||
srcDoc={`<!doctype html><html><head><meta charset="utf-8"><style>html,body{margin:0;height:100%;overflow:hidden;background:transparent}hyperframes-player{display:block;width:100%;height:100%}</style><script src="https://cdn.jsdelivr.net/npm/@hyperframes/player@latest/dist/hyperframes-player.global.js"><\/script></head><body><script>fetch("/public/catalog/blocks/apple-money-count.json").then(function(r){return r.json()}).then(function(d){var p=document.createElement("hyperframes-player");p.setAttribute("srcdoc",d.html);p.setAttribute("controls","");p.setAttribute("autoplay","");p.setAttribute("loop","");p.setAttribute("muted","");p.setAttribute("poster","https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/apple-money-count.png");document.body.appendChild(p)});<\/script></body></html>`}
|
||||
/>
|
||||
|
||||
## Source
|
||||
|
||||
<Accordion title="apple-money-count.html">
|
||||
|
||||
```html
|
||||
```html apple-money-count.html
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
@@ -345,16 +345,37 @@ That writes `compositions/apple-money-count.html`, plus 1 supporting file under
|
||||
</html>
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
</CatalogSlot>
|
||||
|
||||
## Usage
|
||||
<CatalogSlot slot="install">
|
||||
|
||||
After installing, add the block to your host composition:
|
||||
<InstallCommand command="npx hyperframes add apple-money-count" item="apple-money-count" />
|
||||
|
||||
```html
|
||||
<div data-composition-id="apple-money-count" data-composition-src="compositions/apple-money-count.html" data-start="0" data-duration="5" data-track-index="1" data-width="1920" data-height="1080"></div>
|
||||
That writes `compositions/apple-money-count.html`, plus 1 supporting file under `assets/`.
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
<CatalogSlot slot="docs">
|
||||
|
||||
## Add it to your video
|
||||
|
||||
It runs for 5 seconds at 1920×1080. Paste this into your composition:
|
||||
|
||||
```html index.html
|
||||
<div
|
||||
data-composition-id="apple-money-count"
|
||||
data-composition-src="compositions/apple-money-count.html"
|
||||
data-start="0"
|
||||
data-duration="5"
|
||||
data-track-index="1"
|
||||
data-width="1920"
|
||||
data-height="1080"
|
||||
></div>
|
||||
```
|
||||
|
||||
Move it in time with `data-start`. Put it on a different timeline row with
|
||||
`data-track-index`. See [data attributes](/concepts/data-attributes) for the rest.
|
||||
|
||||
{/* hf:generated-footer */}
|
||||
|
||||
Tagged `showcase` `finance` `kinetic` `youtube` `sfx`.
|
||||
@@ -374,3 +395,7 @@ Created by [Stronkter](https://x.com/Stronkter).
|
||||
- [Browse the complete Catalog](/catalog)
|
||||
- [Add assets and Catalog items in Studio](/studio/assets-and-blocks)
|
||||
- [Build a richer composition](/go-further)
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
</CatalogDetail>
|
||||
|
||||
@@ -1,24 +1,27 @@
|
||||
---
|
||||
title: "Bar Chart Race"
|
||||
description: "Ranked bars grow and overtake each other as a time series advances, with the axis rescaling as values climb and the accent handing over to whoever leads"
|
||||
mode: "frame"
|
||||
---
|
||||
|
||||
import { CatalogDetail, CatalogSlot } from "/snippets/catalog-detail.jsx";
|
||||
import { InstallCommand } from "/snippets/install-command.jsx";
|
||||
import { VariablesExplorer } from "/snippets/variables-explorer.jsx";
|
||||
|
||||
## Install
|
||||
|
||||
<InstallCommand command="npx hyperframes add bar-chart-race" item="bar-chart-race" />
|
||||
|
||||
That writes one file: `compositions/bar-chart-race.html`.
|
||||
|
||||
<VariablesExplorer
|
||||
<CatalogDetail
|
||||
previewSrc="/public/catalog/blocks/bar-chart-race.json"
|
||||
compositionId="bar-chart-race"
|
||||
compositionSrc="compositions/bar-chart-race.html"
|
||||
title="Bar Chart Race"
|
||||
description="Ranked bars grow and overtake each other as a time series advances, with the axis rescaling as values climb and the accent handing over to whoever leads"
|
||||
variables={[{"id":"title","type":"string","label":"Title","default":"Streaming Subscribers by Service"},{"id":"subtitle","type":"string","label":"Subtitle","default":"Ranked by reported subscribers"},{"id":"periods","type":"string","label":"Period labels (comma separated)","default":"2019, 2020, 2021, 2022, 2023, 2024"},{"id":"series","type":"string","label":"Series (one per line: Name: v1, v2, ...)","default":"Northwind: 42, 58, 71, 96, 118, 131\nCobalt: 30, 46, 68, 92, 126, 168\nFerry: 55, 62, 66, 70, 74, 79\nMarlow: 18, 33, 52, 61, 88, 104\nAster: 25, 28, 44, 58, 63, 72\nPell: 12, 20, 39, 47, 55, 90\nQuill: 8, 11, 15, 24, 40, 66\nDunmore: 35, 37, 38, 40, 42, 44"},{"id":"barCount","type":"number","label":"Bars shown","default":6,"min":3,"max":12,"step":1},{"id":"periodDuration","type":"number","label":"Seconds per period","default":2,"min":0.4,"max":6,"step":0.1},{"id":"valuePrefix","type":"string","label":"Value prefix","default":"$"},{"id":"valueSuffix","type":"string","label":"Value suffix","default":"M"},{"id":"valueDecimals","type":"number","label":"Value decimals","default":0,"min":0,"max":3,"step":1},{"id":"accent","type":"color","label":"Leader accent","default":"#c8452d"}]}
|
||||
meta={{"duration":12,"width":1920,"height":1080,"category":"Data","badge":"Stable","codeLines":584}}
|
||||
attribution={{"path":"registry/blocks/bar-chart-race","tags":["data","chart","statistics","ranking"]}}
|
||||
hasCode
|
||||
rawUrl="https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/bar-chart-race/bar-chart-race.html"
|
||||
>
|
||||
|
||||
<CatalogSlot slot="code">
|
||||
|
||||
```html bar-chart-race.html
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
@@ -606,7 +609,17 @@ That writes one file: `compositions/bar-chart-race.html`.
|
||||
</html>
|
||||
```
|
||||
|
||||
</VariablesExplorer>
|
||||
</CatalogSlot>
|
||||
|
||||
<CatalogSlot slot="install">
|
||||
|
||||
<InstallCommand command="npx hyperframes add bar-chart-race" item="bar-chart-race" />
|
||||
|
||||
That writes one file: `compositions/bar-chart-race.html`.
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
<CatalogSlot slot="docs">
|
||||
|
||||
## Add it to your video
|
||||
|
||||
@@ -685,599 +698,6 @@ defaults, so this behaves exactly like the preview above until you change one:
|
||||
></div>
|
||||
```
|
||||
|
||||
## Source
|
||||
|
||||
<Accordion title="bar-chart-race.html">
|
||||
|
||||
```html
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=1920, height=1080" />
|
||||
<title>Bar Chart Race</title>
|
||||
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
|
||||
<style>
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
html,
|
||||
body {
|
||||
width: 1920px;
|
||||
height: 1080px;
|
||||
overflow: hidden;
|
||||
background-color: #f5f3ef;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div
|
||||
id="bcr-root"
|
||||
data-composition-id="bar-chart-race"
|
||||
data-width="1920"
|
||||
data-height="1080"
|
||||
data-start="0"
|
||||
data-duration="12"
|
||||
data-composition-variables='[
|
||||
{"id":"title","type":"string","label":"Title","default":"Streaming Subscribers by Service"},
|
||||
{"id":"subtitle","type":"string","label":"Subtitle","default":"Ranked by reported subscribers"},
|
||||
{"id":"periods","type":"string","label":"Period labels (comma separated)","default":"2019, 2020, 2021, 2022, 2023, 2024"},
|
||||
{"id":"series","type":"string","label":"Series (one per line: Name: v1, v2, ...)","default":"Northwind: 42, 58, 71, 96, 118, 131\nCobalt: 30, 46, 68, 92, 126, 168\nFerry: 55, 62, 66, 70, 74, 79\nMarlow: 18, 33, 52, 61, 88, 104\nAster: 25, 28, 44, 58, 63, 72\nPell: 12, 20, 39, 47, 55, 90\nQuill: 8, 11, 15, 24, 40, 66\nDunmore: 35, 37, 38, 40, 42, 44"},
|
||||
{"id":"barCount","type":"number","label":"Bars shown","default":6,"min":3,"max":12,"step":1},
|
||||
{"id":"periodDuration","type":"number","label":"Seconds per period","default":2,"min":0.4,"max":6,"step":0.1},
|
||||
{"id":"valuePrefix","type":"string","label":"Value prefix","default":"$"},
|
||||
{"id":"valueSuffix","type":"string","label":"Value suffix","default":"M"},
|
||||
{"id":"valueDecimals","type":"number","label":"Value decimals","default":0,"min":0,"max":3,"step":1},
|
||||
{"id":"accent","type":"color","label":"Leader accent","default":"#c8452d"}
|
||||
]'
|
||||
>
|
||||
<div id="bcr-stage" class="clip" data-start="0" data-duration="12" data-track-index="0">
|
||||
<div id="bcr-bg"></div>
|
||||
|
||||
<div id="bcr-head">
|
||||
<div id="bcr-head-left">
|
||||
<h1 id="bcr-title">Streaming Subscribers by Service</h1>
|
||||
<p id="bcr-subtitle">Ranked by reported subscribers</p>
|
||||
</div>
|
||||
<div id="bcr-head-right">
|
||||
<span id="bcr-period-caption">Period</span>
|
||||
<span id="bcr-period">2019</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Rows live here, one per series, positioned by rank. -->
|
||||
<div id="bcr-plot"></div>
|
||||
|
||||
<!-- Axis is painted above the rows: the tick labels sliding left is the
|
||||
visible evidence that the scale is rescaling as values climb. -->
|
||||
<div id="bcr-axis"></div>
|
||||
|
||||
<p id="bcr-source">Placeholder data</p>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
#bcr-root {
|
||||
position: relative;
|
||||
width: 1920px;
|
||||
height: 1080px;
|
||||
overflow: hidden;
|
||||
font-family: Inter, sans-serif;
|
||||
color: #1f1d1b;
|
||||
}
|
||||
|
||||
#bcr-stage {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
}
|
||||
|
||||
/* Scene fill goes on a full-bleed child, never the composition root. */
|
||||
#bcr-bg {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background-color: #f5f3ef;
|
||||
}
|
||||
|
||||
#bcr-head {
|
||||
position: absolute;
|
||||
top: 58px;
|
||||
left: 96px;
|
||||
width: 1728px;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
#bcr-title {
|
||||
font-size: 46px;
|
||||
font-weight: 700;
|
||||
letter-spacing: -0.015em;
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
#bcr-subtitle {
|
||||
margin-top: 10px;
|
||||
font-size: 19px;
|
||||
font-weight: 400;
|
||||
color: #6b6560;
|
||||
}
|
||||
|
||||
#bcr-head-right {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
#bcr-period-caption {
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.18em;
|
||||
text-transform: uppercase;
|
||||
color: #6b6560;
|
||||
}
|
||||
|
||||
#bcr-period {
|
||||
margin-top: 4px;
|
||||
font-size: 72px;
|
||||
font-weight: 700;
|
||||
line-height: 1;
|
||||
font-variant-numeric: tabular-nums;
|
||||
letter-spacing: -0.02em;
|
||||
}
|
||||
|
||||
#bcr-plot {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 236px;
|
||||
width: 1920px;
|
||||
height: 760px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.bcr-row {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 1920px;
|
||||
}
|
||||
|
||||
/* Rows are transparent so both bars of an overtake stay visible sliding
|
||||
past each other; only the two TEXT blocks carry an opaque backing, so
|
||||
the front row's label covers the one it crosses instead of tangling
|
||||
with it. */
|
||||
.bcr-name {
|
||||
position: absolute;
|
||||
left: 96px;
|
||||
width: 272px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
text-align: right;
|
||||
font-size: 26px;
|
||||
font-weight: 600;
|
||||
line-height: 1.7;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
background-color: #f5f3ef;
|
||||
}
|
||||
|
||||
.bcr-bar {
|
||||
position: absolute;
|
||||
left: 400px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
border-radius: 3px;
|
||||
background-color: #1f1d1b;
|
||||
}
|
||||
|
||||
.bcr-value {
|
||||
position: absolute;
|
||||
left: 404px;
|
||||
top: 50%;
|
||||
padding: 0 12px;
|
||||
font-size: 26px;
|
||||
font-weight: 600;
|
||||
line-height: 1.7;
|
||||
font-variant-numeric: tabular-nums;
|
||||
white-space: nowrap;
|
||||
background-color: #f5f3ef;
|
||||
}
|
||||
|
||||
#bcr-axis {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
z-index: 5000;
|
||||
}
|
||||
|
||||
.bcr-tick-line {
|
||||
position: absolute;
|
||||
top: 232px;
|
||||
left: 0;
|
||||
width: 1px;
|
||||
height: 768px;
|
||||
background-color: rgba(31, 29, 27, 0.11);
|
||||
}
|
||||
|
||||
.bcr-tick-line.bcr-tick-zero {
|
||||
background-color: rgba(31, 29, 27, 0.5);
|
||||
}
|
||||
|
||||
.bcr-tick-label {
|
||||
position: absolute;
|
||||
top: 198px;
|
||||
left: 0;
|
||||
font-size: 17px;
|
||||
font-weight: 500;
|
||||
font-variant-numeric: tabular-nums;
|
||||
color: #6b6560;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
#bcr-source {
|
||||
position: absolute;
|
||||
left: 96px;
|
||||
top: 1014px;
|
||||
font-size: 16px;
|
||||
color: #6b6560;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
// ---- variables -------------------------------------------------
|
||||
// The declaration attribute is the single source of truth: when the
|
||||
// runtime is absent (raw file open) we parse the same attribute.
|
||||
function readVars() {
|
||||
// Read the declaration off THIS block's own root, never
|
||||
// document.documentElement: mounted as a sub-composition the block's
|
||||
// <html> is discarded and documentElement is the HOST's, so a
|
||||
// documentElement read returns the wrong element (or null) and the
|
||||
// block renders with no series at all. Runtime / host / --variables
|
||||
// values are layered on top as overrides, so the block does not
|
||||
// depend on the runtime being injected before this script runs.
|
||||
var out = {};
|
||||
var root = document.getElementById("bcr-root");
|
||||
try {
|
||||
JSON.parse(root.getAttribute("data-composition-variables")).forEach(function (d) {
|
||||
out[d.id] = d.default;
|
||||
});
|
||||
} catch (err) {
|
||||
// Declaration unreadable; the per-variable literal fallbacks below
|
||||
// still produce a renderable composition.
|
||||
}
|
||||
var hf = window.__hyperframes;
|
||||
var overrides =
|
||||
hf && typeof hf.getVariables === "function"
|
||||
? hf.getVariables()
|
||||
: window.__hfVariables;
|
||||
if (overrides && typeof overrides === "object") {
|
||||
for (var key in overrides) {
|
||||
if (overrides[key] !== undefined) out[key] = overrides[key];
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
function num(value, fallback, lo, hi) {
|
||||
var n = typeof value === "number" ? value : Number.parseFloat(value);
|
||||
if (!Number.isFinite(n)) n = fallback;
|
||||
return Math.min(hi, Math.max(lo, n));
|
||||
}
|
||||
|
||||
function str(value, fallback) {
|
||||
return typeof value === "string" && value.length > 0 ? value : fallback;
|
||||
}
|
||||
|
||||
var V = readVars();
|
||||
var ACCENT = str(V.accent, "#c8452d");
|
||||
var BAR_COLOR = "#1f1d1b";
|
||||
var BAR_COUNT = Math.round(num(V.barCount, 6, 1, 24));
|
||||
var PERIOD_DURATION = num(V.periodDuration, 2, 0.1, 30);
|
||||
var DECIMALS = Math.round(num(V.valueDecimals, 0, 0, 6));
|
||||
var PREFIX = typeof V.valuePrefix === "string" ? V.valuePrefix : "";
|
||||
var SUFFIX = typeof V.valueSuffix === "string" ? V.valueSuffix : "";
|
||||
|
||||
// ---- data format ------------------------------------------------
|
||||
// Wide table, one series per line: "Name: v1, v2, v3".
|
||||
// `;` is accepted as a line separator so the whole table survives a
|
||||
// single-line text input. Short rows hold their last value.
|
||||
function parsePeriods(text) {
|
||||
return String(text)
|
||||
.split(",")
|
||||
.map(function (s) {
|
||||
return s.trim();
|
||||
})
|
||||
.filter(function (s) {
|
||||
return s.length > 0;
|
||||
});
|
||||
}
|
||||
|
||||
function parseSeries(text, periodCount) {
|
||||
var out = [];
|
||||
var lines = String(text).split(/[\n;]+/);
|
||||
for (var i = 0; i < lines.length; i++) {
|
||||
var line = lines[i].trim();
|
||||
if (!line) continue;
|
||||
var split = line.indexOf(":");
|
||||
if (split < 0) continue;
|
||||
var label = line.slice(0, split).trim();
|
||||
if (!label) continue;
|
||||
var values = line
|
||||
.slice(split + 1)
|
||||
.split(",")
|
||||
.map(function (s) {
|
||||
return Number.parseFloat(s.trim());
|
||||
})
|
||||
.filter(function (n) {
|
||||
return Number.isFinite(n);
|
||||
});
|
||||
if (values.length === 0) continue;
|
||||
while (values.length < periodCount) values.push(values[values.length - 1]);
|
||||
out.push({ label: label, values: values.slice(0, periodCount) });
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
var PERIODS = parsePeriods(str(V.periods, "2019, 2020, 2021, 2022, 2023, 2024"));
|
||||
if (PERIODS.length === 0) PERIODS = ["1"];
|
||||
var SERIES = parseSeries(str(V.series, ""), PERIODS.length);
|
||||
var T = PERIODS.length;
|
||||
var N = SERIES.length;
|
||||
|
||||
// ---- closed-form state ------------------------------------------
|
||||
// k = 10 interpolated keyframes per period (Bostock's number). Rank is
|
||||
// computed at each keyframe from the interpolated value and baked once;
|
||||
// a bar's on-screen row is then solved FROM rank, so two bars can never
|
||||
// cross without swapping. Everything below is a pure function of t.
|
||||
var K = 10;
|
||||
var KF_DURATION = PERIOD_DURATION / K;
|
||||
var KF_COUNT = T > 1 ? (T - 1) * K + 1 : 1;
|
||||
var RACE_SECONDS = T > 1 ? (T - 1) * PERIOD_DURATION : 0;
|
||||
|
||||
function clamp(v, lo, hi) {
|
||||
return v < lo ? lo : v > hi ? hi : v;
|
||||
}
|
||||
|
||||
function valueAt(t, series) {
|
||||
if (T < 2) return series.values[0];
|
||||
var u = t / PERIOD_DURATION;
|
||||
var i = clamp(Math.floor(u), 0, T - 2);
|
||||
var f = clamp(u - i, 0, 1);
|
||||
return series.values[i] + (series.values[i + 1] - series.values[i]) * f;
|
||||
}
|
||||
|
||||
// ranks[m][j] = integer rank of series j at keyframe m (0 = leader).
|
||||
var RANKS = [];
|
||||
for (var m = 0; m < KF_COUNT; m++) {
|
||||
var tm = m * KF_DURATION;
|
||||
var order = SERIES.map(function (s, j) {
|
||||
return { j: j, v: valueAt(tm, s) };
|
||||
});
|
||||
// Descending by value; index breaks ties so the sort is total and
|
||||
// therefore identical on every evaluation.
|
||||
order.sort(function (a, b) {
|
||||
return b.v - a.v || a.j - b.j;
|
||||
});
|
||||
var row = new Array(N);
|
||||
for (var r = 0; r < order.length; r++) row[order[r].j] = r;
|
||||
RANKS.push(row);
|
||||
}
|
||||
|
||||
function smoothstep(x) {
|
||||
return x * x * (3 - 2 * x);
|
||||
}
|
||||
|
||||
// Continuous row position solved from the baked ranks. A rank change
|
||||
// between two adjacent keyframes IS the swap, and it plays out over one
|
||||
// keyframe interval (PERIOD_DURATION / 10) instead of snapping.
|
||||
function rankPosAt(t, j) {
|
||||
if (KF_COUNT < 2) return RANKS[0][j];
|
||||
var m = t / KF_DURATION;
|
||||
var m0 = clamp(Math.floor(m), 0, KF_COUNT - 2);
|
||||
var e = smoothstep(clamp(m - m0, 0, 1));
|
||||
var a = RANKS[m0][j];
|
||||
var b = RANKS[m0 + 1][j];
|
||||
return a + (b - a) * e;
|
||||
}
|
||||
|
||||
// ---- formatting --------------------------------------------------
|
||||
function formatValue(v) {
|
||||
return (
|
||||
PREFIX +
|
||||
v.toLocaleString("en-US", {
|
||||
minimumFractionDigits: DECIMALS,
|
||||
maximumFractionDigits: DECIMALS,
|
||||
}) +
|
||||
SUFFIX
|
||||
);
|
||||
}
|
||||
|
||||
function hexToRgb(hex) {
|
||||
var h = String(hex).trim().replace("#", "");
|
||||
if (h.length === 3) h = h[0] + h[0] + h[1] + h[1] + h[2] + h[2];
|
||||
var n = Number.parseInt(h, 16);
|
||||
if (!Number.isFinite(n)) return [31, 29, 27];
|
||||
return [(n >> 16) & 255, (n >> 8) & 255, n & 255];
|
||||
}
|
||||
|
||||
// Accent is the only colour in the piece and it carries exactly one
|
||||
// meaning: this bar currently leads. It is binary, not blended — a
|
||||
// half-accent bar would assert "half leading", which is not a fact.
|
||||
// It hands over at the instant the two values cross, which is also the
|
||||
// instant the two bars are the same length, so the handover lands on
|
||||
// the overtake instead of drifting near it. Flat fill, never a
|
||||
// gradient: a gradient inside a bar makes the longest bar the palest
|
||||
// exactly where the eye is comparing lengths.
|
||||
function hexToCss(hex) {
|
||||
var rgb = hexToRgb(hex);
|
||||
return "rgb(" + rgb[0] + ", " + rgb[1] + ", " + rgb[2] + ")";
|
||||
}
|
||||
|
||||
var BAR_FILL = hexToCss(BAR_COLOR);
|
||||
var ACCENT_FILL = hexToCss(ACCENT);
|
||||
|
||||
function niceStep(x) {
|
||||
var e = Math.pow(10, Math.floor(Math.log10(x)));
|
||||
var f = x / e;
|
||||
return e * (f <= 1 ? 1 : f <= 2 ? 2 : f <= 5 ? 5 : 10);
|
||||
}
|
||||
|
||||
// ---- geometry ----------------------------------------------------
|
||||
var TRACK_X = 400;
|
||||
var TRACK_W = 1180;
|
||||
var PLOT_H = 760;
|
||||
var PITCH = PLOT_H / BAR_COUNT;
|
||||
var BAR_H = Math.max(12, PITCH * 0.62);
|
||||
var TICK_POOL = 12;
|
||||
|
||||
// ---- DOM ---------------------------------------------------------
|
||||
var plot = document.getElementById("bcr-plot");
|
||||
var axis = document.getElementById("bcr-axis");
|
||||
var periodEl = document.getElementById("bcr-period");
|
||||
document.getElementById("bcr-title").textContent = str(
|
||||
V.title,
|
||||
"Streaming Subscribers by Service",
|
||||
);
|
||||
document.getElementById("bcr-subtitle").textContent = str(
|
||||
V.subtitle,
|
||||
"Ranked by reported subscribers",
|
||||
);
|
||||
|
||||
var rows = SERIES.map(function (s) {
|
||||
var row = document.createElement("div");
|
||||
row.className = "bcr-row";
|
||||
row.style.height = PITCH + "px";
|
||||
|
||||
// Declared, not silenced: two rows mid-overtake genuinely occupy the
|
||||
// same band for ~200ms. Readability is handled by the opaque chip on
|
||||
// each label, so the front one is always legible.
|
||||
var name = document.createElement("div");
|
||||
name.className = "bcr-name";
|
||||
name.setAttribute("data-layout-allow-overlap", "");
|
||||
name.textContent = s.label;
|
||||
|
||||
var bar = document.createElement("div");
|
||||
bar.className = "bcr-bar";
|
||||
bar.style.height = BAR_H + "px";
|
||||
|
||||
var value = document.createElement("div");
|
||||
value.className = "bcr-value";
|
||||
value.setAttribute("data-layout-allow-overlap", "");
|
||||
|
||||
row.appendChild(name);
|
||||
row.appendChild(bar);
|
||||
row.appendChild(value);
|
||||
plot.appendChild(row);
|
||||
return { row: row, bar: bar, value: value };
|
||||
});
|
||||
|
||||
var ticks = [];
|
||||
for (var k = 0; k < TICK_POOL; k++) {
|
||||
var line = document.createElement("div");
|
||||
line.className = "bcr-tick-line" + (k === 0 ? " bcr-tick-zero" : "");
|
||||
var label = document.createElement("div");
|
||||
label.className = "bcr-tick-label";
|
||||
axis.appendChild(line);
|
||||
axis.appendChild(label);
|
||||
ticks.push({ line: line, label: label });
|
||||
}
|
||||
|
||||
// ---- render ------------------------------------------------------
|
||||
function render(t) {
|
||||
if (N === 0) return;
|
||||
|
||||
var values = SERIES.map(function (s) {
|
||||
return valueAt(t, s);
|
||||
});
|
||||
// Paint order follows the CURRENT value, not the row position: the
|
||||
// bar that has just edged ahead comes forward, so at the instant two
|
||||
// bars are superimposed the frame shows the one that is overtaking
|
||||
// (its bar and its label together) rather than the one it passed.
|
||||
var byValue = values
|
||||
.map(function (v, i) {
|
||||
return i;
|
||||
})
|
||||
.sort(function (a, b) {
|
||||
return values[b] - values[a] || a - b;
|
||||
});
|
||||
var zRank = new Array(values.length);
|
||||
for (var q = 0; q < byValue.length; q++) zRank[byValue[q]] = q;
|
||||
var leader = byValue[0];
|
||||
var maxValue = values[leader];
|
||||
// Headroom keeps the leader off the frame edge; the domain is a
|
||||
// continuous function of t, so the axis glides instead of stepping.
|
||||
var scaleMax = Math.max(maxValue * 1.06, 1e-6);
|
||||
var step = niceStep(scaleMax / 4);
|
||||
|
||||
for (var i = 0; i < TICK_POOL; i++) {
|
||||
var tv = i * step;
|
||||
var visible = tv <= scaleMax;
|
||||
var tx = TRACK_X + (tv / scaleMax) * TRACK_W;
|
||||
ticks[i].line.style.transform = "translateX(" + tx.toFixed(2) + "px)";
|
||||
ticks[i].line.style.opacity = visible ? "1" : "0";
|
||||
ticks[i].label.style.transform =
|
||||
"translateX(" + tx.toFixed(2) + "px) translateX(-50%)";
|
||||
ticks[i].label.style.opacity = visible ? "1" : "0";
|
||||
ticks[i].label.textContent = visible ? formatValue(tv) : "";
|
||||
}
|
||||
|
||||
for (var j = 0; j < rows.length; j++) {
|
||||
var rankPos = rankPosAt(t, j);
|
||||
var barW = (values[j] / scaleMax) * TRACK_W;
|
||||
var el = rows[j];
|
||||
|
||||
el.row.style.transform = "translateY(" + (rankPos * PITCH).toFixed(2) + "px)";
|
||||
// Fades out only as it slides past the last visible slot.
|
||||
el.row.style.opacity = clamp(BAR_COUNT - rankPos, 0, 1).toFixed(3);
|
||||
el.row.style.zIndex = String(1000 - zRank[j]);
|
||||
|
||||
el.bar.style.width = barW.toFixed(2) + "px";
|
||||
el.bar.style.backgroundColor = j === leader ? ACCENT_FILL : BAR_FILL;
|
||||
|
||||
el.value.style.transform = "translate(" + barW.toFixed(2) + "px, -50%)";
|
||||
el.value.textContent = formatValue(values[j]);
|
||||
}
|
||||
|
||||
var pIndex = clamp(Math.floor(t / PERIOD_DURATION), 0, T - 1);
|
||||
periodEl.textContent = PERIODS[pIndex];
|
||||
}
|
||||
|
||||
// ---- timeline ----------------------------------------------------
|
||||
// A property SETTER drives the per-frame work: tl.eventCallback("onUpdate")
|
||||
// does not fire on tl.seek(), a tweened accessor does.
|
||||
var driver = { _t: 0 };
|
||||
Object.defineProperty(driver, "t", {
|
||||
get: function () {
|
||||
return this._t;
|
||||
},
|
||||
set: function (value) {
|
||||
this._t = value;
|
||||
render(value);
|
||||
},
|
||||
});
|
||||
|
||||
var tl = gsap.timeline({ paused: true });
|
||||
if (RACE_SECONDS > 0) {
|
||||
tl.to(driver, { t: RACE_SECONDS, duration: RACE_SECONDS, ease: "none" }, 0);
|
||||
}
|
||||
render(0);
|
||||
|
||||
window.__timelines = window.__timelines || {};
|
||||
window.__timelines["bar-chart-race"] = tl;
|
||||
})();
|
||||
</script>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
|
||||
{/* hf:generated-footer */}
|
||||
|
||||
Tagged `data` `chart` `statistics` `ranking`.
|
||||
@@ -1287,3 +707,7 @@ Tagged `data` `chart` `statistics` `ranking`.
|
||||
- [Browse the complete Catalog](/catalog)
|
||||
- [Add assets and Catalog items in Studio](/studio/assets-and-blocks)
|
||||
- [Build a richer composition](/go-further)
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
</CatalogDetail>
|
||||
|
||||
@@ -1,54 +1,28 @@
|
||||
---
|
||||
title: "Beat Freeze Cut"
|
||||
description: "A beat-driven speed ramp, freeze-frame hit, and hard-cut sequence for music-led promos and montages."
|
||||
mode: "frame"
|
||||
---
|
||||
|
||||
import { CatalogDetail, CatalogSlot } from "/snippets/catalog-detail.jsx";
|
||||
import { InstallCommand } from "/snippets/install-command.jsx";
|
||||
|
||||
## Install
|
||||
<CatalogDetail
|
||||
previewSrc="/public/catalog/blocks/beat-freeze-cut.json"
|
||||
compositionId="beat-freeze-cut"
|
||||
compositionSrc="compositions/beat-freeze-cut.html"
|
||||
title="Beat Freeze Cut"
|
||||
description="A beat-driven speed ramp, freeze-frame hit, and hard-cut sequence for music-led promos and montages."
|
||||
variables={[]}
|
||||
meta={{"duration":6,"width":1920,"height":1080,"category":"CSS Transitions","badge":"Stable","codeLines":879}}
|
||||
attribution={{"path":"registry/blocks/beat-freeze-cut","tags":["transition","music","beat-driven","freeze-frame"]}}
|
||||
hasCode
|
||||
rawUrl="https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/beat-freeze-cut/beat-freeze-cut.html"
|
||||
>
|
||||
|
||||
<InstallCommand command="npx hyperframes add beat-freeze-cut" item="beat-freeze-cut" />
|
||||
<CatalogSlot slot="code">
|
||||
|
||||
That writes one file: `compositions/beat-freeze-cut.html`.
|
||||
|
||||
<iframe
|
||||
className="w-full aspect-video rounded-xl border-0 bg-zinc-100 dark:bg-zinc-800"
|
||||
title="beat-freeze-cut preview"
|
||||
loading="lazy"
|
||||
srcDoc={`<!doctype html><html><head><meta charset="utf-8"><style>html,body{margin:0;height:100%;overflow:hidden;background:transparent}hyperframes-player{display:block;width:100%;height:100%}</style><script src="https://cdn.jsdelivr.net/npm/@hyperframes/player@latest/dist/hyperframes-player.global.js"><\/script></head><body><script>fetch("/public/catalog/blocks/beat-freeze-cut.json").then(function(r){return r.json()}).then(function(d){var p=document.createElement("hyperframes-player");p.setAttribute("srcdoc",d.html);p.setAttribute("controls","");p.setAttribute("autoplay","");p.setAttribute("loop","");p.setAttribute("muted","");p.setAttribute("poster","https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/beat-freeze-cut.png");document.body.appendChild(p)});<\/script></body></html>`}
|
||||
/>
|
||||
|
||||
## Add it to your video
|
||||
|
||||
It runs for 6 seconds at 1920×1080. Paste this into your composition:
|
||||
|
||||
```html index.html
|
||||
<div
|
||||
data-composition-id="beat-freeze-cut"
|
||||
data-composition-src="compositions/beat-freeze-cut.html"
|
||||
data-start="0"
|
||||
data-duration="6"
|
||||
data-track-index="1"
|
||||
data-width="1920"
|
||||
data-height="1080"
|
||||
></div>
|
||||
```
|
||||
|
||||
Move it in time with `data-start`. Put it on a different timeline row with
|
||||
`data-track-index`. See [data attributes](/concepts/data-attributes) for the rest.
|
||||
|
||||
## Change the colors
|
||||
|
||||
Set these CSS variables on the block:
|
||||
|
||||
- `--bg-color` — Background. Defaults to `#0B0D10`.
|
||||
- `--accent-color` — Accent. Defaults to `#00E5C7`.
|
||||
|
||||
## Source
|
||||
|
||||
<Accordion title="beat-freeze-cut.html">
|
||||
|
||||
```html
|
||||
```html beat-freeze-cut.html
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
@@ -930,7 +904,43 @@ Set these CSS variables on the block:
|
||||
</html>
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
</CatalogSlot>
|
||||
|
||||
<CatalogSlot slot="install">
|
||||
|
||||
<InstallCommand command="npx hyperframes add beat-freeze-cut" item="beat-freeze-cut" />
|
||||
|
||||
That writes one file: `compositions/beat-freeze-cut.html`.
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
<CatalogSlot slot="docs">
|
||||
|
||||
## Add it to your video
|
||||
|
||||
It runs for 6 seconds at 1920×1080. Paste this into your composition:
|
||||
|
||||
```html index.html
|
||||
<div
|
||||
data-composition-id="beat-freeze-cut"
|
||||
data-composition-src="compositions/beat-freeze-cut.html"
|
||||
data-start="0"
|
||||
data-duration="6"
|
||||
data-track-index="1"
|
||||
data-width="1920"
|
||||
data-height="1080"
|
||||
></div>
|
||||
```
|
||||
|
||||
Move it in time with `data-start`. Put it on a different timeline row with
|
||||
`data-track-index`. See [data attributes](/concepts/data-attributes) for the rest.
|
||||
|
||||
## Change the colors
|
||||
|
||||
Set these CSS variables on the block:
|
||||
|
||||
- `--bg-color` — Background. Defaults to `#0B0D10`.
|
||||
- `--accent-color` — Accent. Defaults to `#00E5C7`.
|
||||
|
||||
{/* hf:generated-footer */}
|
||||
|
||||
@@ -941,3 +951,7 @@ Tagged `transition` `music` `beat-driven` `freeze-frame`.
|
||||
- [Browse the complete Catalog](/catalog)
|
||||
- [Add assets and Catalog items in Studio](/studio/assets-and-blocks)
|
||||
- [Build a richer composition](/go-further)
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
</CatalogDetail>
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
---
|
||||
title: "Blue Sweater Intro Video"
|
||||
description: "Warm AI creator intro sequence that resolves into an X follow card for @_blue_sweater_."
|
||||
mode: "frame"
|
||||
---
|
||||
|
||||
import { CatalogDetail, CatalogSlot } from "/snippets/catalog-detail.jsx";
|
||||
import { InstallCommand } from "/snippets/install-command.jsx";
|
||||
|
||||
## Install
|
||||
<CatalogDetail
|
||||
previewSrc="/public/catalog/blocks/blue-sweater-intro-video.json"
|
||||
compositionId="blue-sweater-intro-video"
|
||||
compositionSrc="compositions/blue-sweater-intro-video.html"
|
||||
title="Blue Sweater Intro Video"
|
||||
description="Warm AI creator intro sequence that resolves into an X follow card for @_blue_sweater_."
|
||||
variables={[]}
|
||||
meta={{"duration":12,"width":1920,"height":1080,"category":"Showcases","badge":"Stable","codeLines":1523}}
|
||||
attribution={{"author":"Joe Sai","authorUrl":"https://x.com/_blue_sweater_","path":"registry/blocks/blue-sweater-intro-video","tags":["showcase","ai","creator","sfx"]}}
|
||||
hasCode
|
||||
rawUrl="https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/blue-sweater-intro-video/blue-sweater-intro-video.html"
|
||||
>
|
||||
|
||||
<InstallCommand command="npx hyperframes add blue-sweater-intro-video" item="blue-sweater-intro-video" />
|
||||
<CatalogSlot slot="code">
|
||||
|
||||
That writes `compositions/blue-sweater-intro-video.html`, plus 2 supporting files under `assets/` and `assets/sfx/`.
|
||||
|
||||
<iframe
|
||||
className="w-full aspect-video rounded-xl border-0 bg-zinc-100 dark:bg-zinc-800"
|
||||
title="blue-sweater-intro-video preview"
|
||||
loading="lazy"
|
||||
srcDoc={`<!doctype html><html><head><meta charset="utf-8"><style>html,body{margin:0;height:100%;overflow:hidden;background:transparent}hyperframes-player{display:block;width:100%;height:100%}</style><script src="https://cdn.jsdelivr.net/npm/@hyperframes/player@latest/dist/hyperframes-player.global.js"><\/script></head><body><script>fetch("/public/catalog/blocks/blue-sweater-intro-video.json").then(function(r){return r.json()}).then(function(d){var p=document.createElement("hyperframes-player");p.setAttribute("srcdoc",d.html);p.setAttribute("controls","");p.setAttribute("autoplay","");p.setAttribute("loop","");p.setAttribute("muted","");p.setAttribute("poster","https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/blue-sweater-intro-video.png");document.body.appendChild(p)});<\/script></body></html>`}
|
||||
/>
|
||||
|
||||
## Source
|
||||
|
||||
<Accordion title="blue-sweater-intro-video.html">
|
||||
|
||||
```html
|
||||
```html blue-sweater-intro-video.html
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
@@ -1548,16 +1548,37 @@ That writes `compositions/blue-sweater-intro-video.html`, plus 2 supporting file
|
||||
</html>
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
</CatalogSlot>
|
||||
|
||||
## Usage
|
||||
<CatalogSlot slot="install">
|
||||
|
||||
After installing, add the block to your host composition:
|
||||
<InstallCommand command="npx hyperframes add blue-sweater-intro-video" item="blue-sweater-intro-video" />
|
||||
|
||||
```html
|
||||
<div data-composition-id="blue-sweater-intro-video" data-composition-src="compositions/blue-sweater-intro-video.html" data-start="0" data-duration="12" data-track-index="1" data-width="1920" data-height="1080"></div>
|
||||
That writes `compositions/blue-sweater-intro-video.html`, plus 2 supporting files under `assets/` and `assets/sfx/`.
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
<CatalogSlot slot="docs">
|
||||
|
||||
## Add it to your video
|
||||
|
||||
It runs for 12 seconds at 1920×1080. Paste this into your composition:
|
||||
|
||||
```html index.html
|
||||
<div
|
||||
data-composition-id="blue-sweater-intro-video"
|
||||
data-composition-src="compositions/blue-sweater-intro-video.html"
|
||||
data-start="0"
|
||||
data-duration="12"
|
||||
data-track-index="1"
|
||||
data-width="1920"
|
||||
data-height="1080"
|
||||
></div>
|
||||
```
|
||||
|
||||
Move it in time with `data-start`. Put it on a different timeline row with
|
||||
`data-track-index`. See [data attributes](/concepts/data-attributes) for the rest.
|
||||
|
||||
{/* hf:generated-footer */}
|
||||
|
||||
Tagged `showcase` `ai` `creator` `sfx`.
|
||||
@@ -1569,3 +1590,7 @@ Created by [Joe Sai](https://x.com/_blue_sweater_).
|
||||
- [Browse the complete Catalog](/catalog)
|
||||
- [Add assets and Catalog items in Studio](/studio/assets-and-blocks)
|
||||
- [Build a richer composition](/go-further)
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
</CatalogDetail>
|
||||
|
||||
@@ -1,47 +1,28 @@
|
||||
---
|
||||
title: "Camcorder HUD"
|
||||
description: "Responsive REC, battery, editable date placeholder, and timeline-driven counter overlay for creator-camera and camcorder treatments"
|
||||
mode: "frame"
|
||||
---
|
||||
|
||||
import { CatalogDetail, CatalogSlot } from "/snippets/catalog-detail.jsx";
|
||||
import { InstallCommand } from "/snippets/install-command.jsx";
|
||||
|
||||
## Install
|
||||
<CatalogDetail
|
||||
previewSrc="/public/catalog/blocks/camcorder-hud.json"
|
||||
compositionId="camcorder-hud"
|
||||
compositionSrc="compositions/camcorder-hud.html"
|
||||
title="Camcorder HUD"
|
||||
description="Responsive REC, battery, editable date placeholder, and timeline-driven counter overlay for creator-camera and camcorder treatments"
|
||||
variables={[]}
|
||||
meta={{"duration":4,"width":1920,"height":1080,"category":"Camera & 3D","badge":"Stable","codeLines":163}}
|
||||
attribution={{"path":"registry/blocks/camcorder-hud","tags":["camcorder","camera","recording","hud","overlay","creator","media-treatment-overlay"]}}
|
||||
hasCode
|
||||
rawUrl="https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/camcorder-hud/camcorder-hud.html"
|
||||
>
|
||||
|
||||
<InstallCommand command="npx hyperframes add camcorder-hud" item="camcorder-hud" />
|
||||
<CatalogSlot slot="code">
|
||||
|
||||
That writes one file: `compositions/camcorder-hud.html`.
|
||||
|
||||
<iframe
|
||||
className="w-full aspect-video rounded-xl border-0 bg-zinc-100 dark:bg-zinc-800"
|
||||
title="camcorder-hud preview"
|
||||
loading="lazy"
|
||||
srcDoc={`<!doctype html><html><head><meta charset="utf-8"><style>html,body{margin:0;height:100%;overflow:hidden;background:transparent}hyperframes-player{display:block;width:100%;height:100%}</style><script src="https://cdn.jsdelivr.net/npm/@hyperframes/player@latest/dist/hyperframes-player.global.js"><\/script></head><body><script>fetch("/public/catalog/blocks/camcorder-hud.json").then(function(r){return r.json()}).then(function(d){var p=document.createElement("hyperframes-player");p.setAttribute("srcdoc",d.html);p.setAttribute("controls","");p.setAttribute("autoplay","");p.setAttribute("loop","");p.setAttribute("muted","");p.setAttribute("poster","https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/camcorder-hud.png");document.body.appendChild(p)});<\/script></body></html>`}
|
||||
/>
|
||||
|
||||
## Add it to your video
|
||||
|
||||
It runs for 4 seconds at 1920×1080. Paste this into your composition:
|
||||
|
||||
```html index.html
|
||||
<div
|
||||
data-composition-id="camcorder-hud"
|
||||
data-composition-src="compositions/camcorder-hud.html"
|
||||
data-start="0"
|
||||
data-duration="4"
|
||||
data-track-index="1"
|
||||
data-width="1920"
|
||||
data-height="1080"
|
||||
></div>
|
||||
```
|
||||
|
||||
Move it in time with `data-start`. Put it on a different timeline row with
|
||||
`data-track-index`. See [data attributes](/concepts/data-attributes) for the rest.
|
||||
|
||||
## Source
|
||||
|
||||
<Accordion title="camcorder-hud.html">
|
||||
|
||||
```html
|
||||
```html camcorder-hud.html
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
@@ -207,7 +188,36 @@ Move it in time with `data-start`. Put it on a different timeline row with
|
||||
</html>
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
</CatalogSlot>
|
||||
|
||||
<CatalogSlot slot="install">
|
||||
|
||||
<InstallCommand command="npx hyperframes add camcorder-hud" item="camcorder-hud" />
|
||||
|
||||
That writes one file: `compositions/camcorder-hud.html`.
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
<CatalogSlot slot="docs">
|
||||
|
||||
## Add it to your video
|
||||
|
||||
It runs for 4 seconds at 1920×1080. Paste this into your composition:
|
||||
|
||||
```html index.html
|
||||
<div
|
||||
data-composition-id="camcorder-hud"
|
||||
data-composition-src="compositions/camcorder-hud.html"
|
||||
data-start="0"
|
||||
data-duration="4"
|
||||
data-track-index="1"
|
||||
data-width="1920"
|
||||
data-height="1080"
|
||||
></div>
|
||||
```
|
||||
|
||||
Move it in time with `data-start`. Put it on a different timeline row with
|
||||
`data-track-index`. See [data attributes](/concepts/data-attributes) for the rest.
|
||||
|
||||
{/* hf:generated-footer */}
|
||||
|
||||
@@ -218,3 +228,7 @@ Tagged `camcorder` `camera` `recording` `hud` `overlay` `creator` `media-treatme
|
||||
- [Browse the complete Catalog](/catalog)
|
||||
- [Add assets and Catalog items in Studio](/studio/assets-and-blocks)
|
||||
- [Build a richer composition](/go-further)
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
</CatalogDetail>
|
||||
|
||||
@@ -1,24 +1,27 @@
|
||||
---
|
||||
title: "Dolly Zoom (Vertigo Shot)"
|
||||
description: "Camera tracks toward or away from the subject while the field of view changes the opposite way, so the subject holds its frame size while the background collapses behind it or rushes in. Focal length is solved from dolly distance every frame against d * tan(FOV/2) = const, not keyframed beside it. Requires depth: the subject sits on the z = 0 plane and the content behind it must be layered or genuinely 3D (translateZ at two or more distinct depths) -- a flat element gains nothing from this move."
|
||||
mode: "frame"
|
||||
---
|
||||
|
||||
import { CatalogDetail, CatalogSlot } from "/snippets/catalog-detail.jsx";
|
||||
import { InstallCommand } from "/snippets/install-command.jsx";
|
||||
import { VariablesExplorer } from "/snippets/variables-explorer.jsx";
|
||||
|
||||
## Install
|
||||
|
||||
<InstallCommand command="npx hyperframes add camera-dolly-zoom" item="camera-dolly-zoom" />
|
||||
|
||||
That writes one file: `compositions/camera-dolly-zoom.html`.
|
||||
|
||||
<VariablesExplorer
|
||||
<CatalogDetail
|
||||
previewSrc="/public/catalog/blocks/camera-dolly-zoom.json"
|
||||
compositionId="camera-dolly-zoom"
|
||||
compositionSrc="compositions/camera-dolly-zoom.html"
|
||||
title="Dolly Zoom (Vertigo Shot)"
|
||||
description="Camera tracks toward or away from the subject while the field of view changes the opposite way, so the subject holds its frame size while the background collapses behind it or rushes in. Focal length is solved from dolly distance every frame against d * tan(FOV/2) = const, not keyframed beside it. Requires depth: the subject sits on the z = 0 plane and the content behind it must be layered or genuinely 3D (translateZ at two or more distinct depths) -- a flat element gains nothing from this move."
|
||||
variables={[{"id":"direction","type":"enum","label":"Dolly direction","default":"out","options":[{"value":"out","label":"Dolly out + zoom in (background rushes in)"},{"value":"in","label":"Dolly in + zoom out (background falls away)"}]},{"id":"strength","type":"number","label":"Move strength (end / start distance ratio)","default":2,"min":1.1,"max":4,"step":0.1},{"id":"subjectDistance","type":"number","label":"Camera-to-subject distance at the start of the move","default":1400,"min":600,"max":3000,"step":50,"unit":"px"},{"id":"easing","type":"enum","label":"Dolly easing","default":"power2.inOut","options":[{"value":"none","label":"Linear"},{"value":"sine.inOut","label":"Sine in-out"},{"value":"power1.inOut","label":"Power1 in-out"},{"value":"power2.inOut","label":"Power2 in-out"},{"value":"power3.inOut","label":"Power3 in-out"}]}]}
|
||||
meta={{"duration":4,"width":1920,"height":1080,"category":"Showcases","badge":"Stable","codeLines":354}}
|
||||
attribution={{"path":"registry/blocks/camera-dolly-zoom","tags":["camera","depth","3d","cinematic"]}}
|
||||
hasCode
|
||||
rawUrl="https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/camera-dolly-zoom/camera-dolly-zoom.html"
|
||||
>
|
||||
|
||||
<CatalogSlot slot="code">
|
||||
|
||||
```html camera-dolly-zoom.html
|
||||
<!doctype html>
|
||||
<html
|
||||
@@ -376,7 +379,17 @@ That writes one file: `compositions/camera-dolly-zoom.html`.
|
||||
</html>
|
||||
```
|
||||
|
||||
</VariablesExplorer>
|
||||
</CatalogSlot>
|
||||
|
||||
<CatalogSlot slot="install">
|
||||
|
||||
<InstallCommand command="npx hyperframes add camera-dolly-zoom" item="camera-dolly-zoom" />
|
||||
|
||||
That writes one file: `compositions/camera-dolly-zoom.html`.
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
<CatalogSlot slot="docs">
|
||||
|
||||
## Add it to your video
|
||||
|
||||
@@ -420,369 +433,6 @@ defaults, so this behaves exactly like the preview above until you change one:
|
||||
></div>
|
||||
```
|
||||
|
||||
## Source
|
||||
|
||||
<Accordion title="camera-dolly-zoom.html">
|
||||
|
||||
```html
|
||||
<!doctype html>
|
||||
<html
|
||||
lang="en"
|
||||
data-composition-variables='[
|
||||
{
|
||||
"id": "direction",
|
||||
"type": "enum",
|
||||
"label": "Dolly direction",
|
||||
"default": "out",
|
||||
"options": [
|
||||
{ "value": "out", "label": "Dolly out + zoom in (background rushes in)" },
|
||||
{ "value": "in", "label": "Dolly in + zoom out (background falls away)" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "strength",
|
||||
"type": "number",
|
||||
"label": "Move strength (end / start distance ratio)",
|
||||
"default": 2,
|
||||
"min": 1.1,
|
||||
"max": 4,
|
||||
"step": 0.1
|
||||
},
|
||||
{
|
||||
"id": "subjectDistance",
|
||||
"type": "number",
|
||||
"label": "Camera-to-subject distance at the start of the move",
|
||||
"default": 1400,
|
||||
"min": 600,
|
||||
"max": 3000,
|
||||
"step": 50,
|
||||
"unit": "px"
|
||||
},
|
||||
{
|
||||
"id": "easing",
|
||||
"type": "enum",
|
||||
"label": "Dolly easing",
|
||||
"default": "power2.inOut",
|
||||
"options": [
|
||||
{ "value": "none", "label": "Linear" },
|
||||
{ "value": "sine.inOut", "label": "Sine in-out" },
|
||||
{ "value": "power1.inOut", "label": "Power1 in-out" },
|
||||
{ "value": "power2.inOut", "label": "Power2 in-out" },
|
||||
{ "value": "power3.inOut", "label": "Power3 in-out" }
|
||||
]
|
||||
}
|
||||
]'
|
||||
>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
html,
|
||||
body {
|
||||
width: 1920px;
|
||||
height: 1080px;
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<!--
|
||||
===================================================================
|
||||
DOLLY ZOOM (Vertigo / Hitchcock shot)
|
||||
===================================================================
|
||||
|
||||
The camera tracks toward or away from the subject while the field of
|
||||
view changes the opposite way, so the SUBJECT HOLDS ITS FRAME SIZE
|
||||
while the background collapses behind it or rushes in.
|
||||
|
||||
THE CONSTRAINT (this is the whole effect, and it is exact, not taste):
|
||||
|
||||
d * tan(FOV / 2) = constant
|
||||
|
||||
where d is the camera-to-subject distance. Get it wrong and the shot
|
||||
reads as a clumsy zoom. So focal length is SOLVED from distance every
|
||||
frame; the two are never keyframed side by side.
|
||||
|
||||
In CSS 3D, `perspective: P px` IS the focal length in pixels, and it
|
||||
also places the camera exactly P px in front of the z = 0 plane, so
|
||||
tan(FOV / 2) = (viewportWidth / 2) / P. Substituting collapses the
|
||||
invariant to a single ratio:
|
||||
|
||||
P(t) / d(t) = SUBJECT_SCALE (constant)
|
||||
|
||||
Everything the rig animates falls out of that one line (see the script).
|
||||
|
||||
-------------------------------------------------------------------
|
||||
WHAT THIS PRIMITIVE REQUIRES OF ITS CONTENT
|
||||
-------------------------------------------------------------------
|
||||
|
||||
DEPTH. A flat element gains nothing from a dolly zoom -- with one
|
||||
layer there is no background to collapse and the move is invisible.
|
||||
Content dropped into #dz-rig MUST be layered or genuinely 3D:
|
||||
|
||||
* The SUBJECT sits on the z = 0 plane -- `translateZ(0)`. That is
|
||||
the plane the solve holds; anything you want frame-locked goes
|
||||
there and nowhere else.
|
||||
* BACKGROUND layers get `translateZ(-N px)`, N > 0, at least two or
|
||||
three distinct depths (this demo ships five arches plus a back
|
||||
wall). More separation = stronger effect.
|
||||
* Everything inside #dz-rig must keep `transform-style: preserve-3d`
|
||||
on its ancestors and must NOT sit behind `overflow: hidden`, or
|
||||
the browser flattens the scene and the depth disappears.
|
||||
* Keep every layer at a depth where `d + N > 0` for the whole move,
|
||||
i.e. no layer in front of the camera's closest approach.
|
||||
|
||||
-------------------------------------------------------------------
|
||||
DETERMINISM
|
||||
-------------------------------------------------------------------
|
||||
|
||||
State at frame N is computed from N alone. d(t) is a lerp of the
|
||||
eased tween progress, P is solved from d, and both are written by a
|
||||
property setter on the tweened driver object. Nothing accumulates,
|
||||
nothing reads a clock, and no frame depends on the frame before it.
|
||||
(The setter matters: `tl.eventCallback("onUpdate", ...)` does NOT
|
||||
fire on `tl.seek()`, so anything driven that way freezes on frame 0.)
|
||||
-->
|
||||
<div
|
||||
id="dz-root"
|
||||
data-composition-id="camera-dolly-zoom"
|
||||
data-start="0"
|
||||
data-duration="4"
|
||||
data-width="1920"
|
||||
data-height="1080"
|
||||
>
|
||||
<style>
|
||||
#dz-root {
|
||||
width: 1920px;
|
||||
height: 1080px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
background: #0b1020;
|
||||
font-family: Inter, sans-serif;
|
||||
}
|
||||
|
||||
/* The camera. `perspective` is the focal length and is solved from
|
||||
the dolly distance every frame -- see the script. */
|
||||
#dz-stage {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
perspective-origin: 50% 50%;
|
||||
}
|
||||
|
||||
/* The rig root. Carries the camera's z offset so the subject plane
|
||||
always sits exactly `d` in front of the camera. */
|
||||
#dz-rig {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
transform-style: preserve-3d;
|
||||
}
|
||||
|
||||
/* --- authored depth layers ------------------------------------ */
|
||||
|
||||
.dz-arch {
|
||||
position: absolute;
|
||||
left: -140px;
|
||||
top: -80px;
|
||||
width: 2200px;
|
||||
height: 1240px;
|
||||
border: 16px solid #465280;
|
||||
border-radius: 8px;
|
||||
transform-style: preserve-3d;
|
||||
}
|
||||
|
||||
#dz-backwall {
|
||||
position: absolute;
|
||||
left: -3040px;
|
||||
top: -1710px;
|
||||
width: 8000px;
|
||||
height: 4500px;
|
||||
background: #10162c;
|
||||
transform-style: preserve-3d;
|
||||
}
|
||||
|
||||
/* Bright doorway on the back wall -- the layer whose on-screen size
|
||||
changes while the subject's does not. */
|
||||
#dz-portal {
|
||||
position: absolute;
|
||||
left: 3300px;
|
||||
top: 1850px;
|
||||
width: 1400px;
|
||||
height: 1500px;
|
||||
background: #cfe4ff;
|
||||
}
|
||||
|
||||
/* --- the subject: z = 0, the plane the solve holds ------------- */
|
||||
|
||||
#dz-subject {
|
||||
position: absolute;
|
||||
left: 840px;
|
||||
top: 300px;
|
||||
width: 240px;
|
||||
height: 620px;
|
||||
transform-style: preserve-3d;
|
||||
}
|
||||
.dz-fig-head {
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
margin: 0 auto;
|
||||
border-radius: 50%;
|
||||
background: #f2b134;
|
||||
}
|
||||
.dz-fig-body {
|
||||
width: 240px;
|
||||
height: 470px;
|
||||
margin-top: 30px;
|
||||
border-radius: 120px 120px 10px 10px;
|
||||
background: #f2b134;
|
||||
}
|
||||
|
||||
/* --- flat overlay (outside the 3D rig) ------------------------- */
|
||||
|
||||
.dz-label {
|
||||
position: absolute;
|
||||
left: 64px;
|
||||
bottom: 56px;
|
||||
padding: 14px 22px;
|
||||
border-radius: 8px;
|
||||
background: #0b1020;
|
||||
color: #e9eeff;
|
||||
font-size: 26px;
|
||||
letter-spacing: 0.14em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="dz-stage" class="clip" data-start="0" data-duration="4" data-track-index="0">
|
||||
<div id="dz-rig">
|
||||
<div id="dz-backwall" data-layout-allow-overflow style="transform: translateZ(-3200px)">
|
||||
<div id="dz-portal"></div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="dz-arch"
|
||||
data-layout-allow-overflow
|
||||
style="transform: translateZ(-2360px); border-color: #384166"
|
||||
></div>
|
||||
<div
|
||||
class="dz-arch"
|
||||
data-layout-allow-overflow
|
||||
style="transform: translateZ(-1720px); border-color: #465280"
|
||||
></div>
|
||||
<div
|
||||
class="dz-arch"
|
||||
data-layout-allow-overflow
|
||||
style="transform: translateZ(-1120px); border-color: #57669c"
|
||||
></div>
|
||||
<div
|
||||
class="dz-arch"
|
||||
data-layout-allow-overflow
|
||||
style="transform: translateZ(-560px); border-color: #6b7cb8"
|
||||
></div>
|
||||
<div
|
||||
class="dz-arch"
|
||||
data-layout-allow-overflow
|
||||
style="transform: translateZ(0px); border-color: #8194d4"
|
||||
></div>
|
||||
|
||||
<div id="dz-subject" style="transform: translateZ(0px)">
|
||||
<div class="dz-fig-head"></div>
|
||||
<div class="dz-fig-body"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="dz-label">Dolly zoom · d × tan(fov/2) = const</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
window.__timelines = window.__timelines || {};
|
||||
|
||||
(function () {
|
||||
var DEFAULTS = {
|
||||
direction: "out",
|
||||
strength: 2,
|
||||
subjectDistance: 1400,
|
||||
easing: "power2.inOut",
|
||||
};
|
||||
var declared =
|
||||
window.__hyperframes && window.__hyperframes.getVariables
|
||||
? window.__hyperframes.getVariables()
|
||||
: {};
|
||||
var vars = Object.assign({}, DEFAULTS, declared);
|
||||
|
||||
function clamp(value, lo, hi, fallback) {
|
||||
var n = Number(value);
|
||||
if (!isFinite(n)) return fallback;
|
||||
return Math.min(hi, Math.max(lo, n));
|
||||
}
|
||||
function oneOf(value, allowed, fallback) {
|
||||
return allowed.indexOf(String(value)) >= 0 ? String(value) : fallback;
|
||||
}
|
||||
|
||||
var direction = oneOf(vars.direction, ["in", "out"], DEFAULTS.direction);
|
||||
var strength = clamp(vars.strength, 1.1, 4, DEFAULTS.strength);
|
||||
var ease = oneOf(
|
||||
vars.easing,
|
||||
["none", "sine.inOut", "power1.inOut", "power2.inOut", "power3.inOut"],
|
||||
DEFAULTS.easing,
|
||||
);
|
||||
|
||||
// d0 = camera-to-subject distance at the start of the move (px).
|
||||
// d1 = distance at the end. "out" pulls the camera back and zooms in;
|
||||
// "in" pushes the camera forward and zooms out.
|
||||
var d0 = clamp(vars.subjectDistance, 600, 3000, DEFAULTS.subjectDistance);
|
||||
var d1 = direction === "in" ? d0 / strength : d0 * strength;
|
||||
|
||||
// ---- the solve --------------------------------------------------
|
||||
// Subject size is fixed iff d * tan(FOV/2) = const. With CSS 3D,
|
||||
// tan(FOV/2) = (width/2) / perspective, so that invariant is exactly
|
||||
// perspective / distance = SUBJECT_SCALE. Focal length is therefore
|
||||
// derived from the dolly, never animated alongside it.
|
||||
var SUBJECT_SCALE = 1; // subject renders 1:1 => its plane is z = 0
|
||||
|
||||
var stage = document.getElementById("dz-stage");
|
||||
var rig = document.getElementById("dz-rig");
|
||||
|
||||
function applyCamera(u) {
|
||||
var d = d0 + (d1 - d0) * u; // dolly -- pure function of u
|
||||
var P = SUBJECT_SCALE * d; // focal length solved from d
|
||||
var camZ = P - d; // put the subject plane at camera distance d
|
||||
stage.style.perspective = P + "px";
|
||||
rig.style.transform = "translateZ(" + camZ + "px)";
|
||||
}
|
||||
|
||||
// GSAP suppresses timeline-level onUpdate during seek(), so the camera
|
||||
// is driven by a property setter on the tweened object instead: that
|
||||
// fires on every render, seeks included.
|
||||
var camera = {
|
||||
_u: 0,
|
||||
get u() {
|
||||
return this._u;
|
||||
},
|
||||
set u(value) {
|
||||
this._u = value;
|
||||
applyCamera(value);
|
||||
},
|
||||
};
|
||||
|
||||
applyCamera(0);
|
||||
|
||||
var tl = gsap.timeline({ paused: true });
|
||||
tl.to(camera, { u: 1, duration: 4, ease: ease }, 0);
|
||||
window.__timelines["camera-dolly-zoom"] = tl;
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
|
||||
{/* hf:generated-footer */}
|
||||
|
||||
Tagged `camera` `depth` `3d` `cinematic`.
|
||||
@@ -792,3 +442,7 @@ Tagged `camera` `depth` `3d` `cinematic`.
|
||||
- [Browse the complete Catalog](/catalog)
|
||||
- [Add assets and Catalog items in Studio](/studio/assets-and-blocks)
|
||||
- [Build a richer composition](/go-further)
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
</CatalogDetail>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,47 +1,28 @@
|
||||
---
|
||||
title: "ChatGPT Exchange"
|
||||
description: "A portrait ChatGPT-style exchange types and sends a prompt, streams an answer, assembles a comparison table, and scrolls back through the result"
|
||||
mode: "frame"
|
||||
---
|
||||
|
||||
import { CatalogDetail, CatalogSlot } from "/snippets/catalog-detail.jsx";
|
||||
import { InstallCommand } from "/snippets/install-command.jsx";
|
||||
|
||||
## Install
|
||||
<CatalogDetail
|
||||
previewSrc="/public/catalog/blocks/chatgpt-exchange.json"
|
||||
compositionId="chatgpt-exchange"
|
||||
compositionSrc="compositions/chatgpt-exchange.html"
|
||||
title="ChatGPT Exchange"
|
||||
description="A portrait ChatGPT-style exchange types and sends a prompt, streams an answer, assembles a comparison table, and scrolls back through the result"
|
||||
variables={[{"id":"prompt","type":"string","role":"content","label":"User prompt","description":"Prompt typed into ChatGPT; keep close to the original length.","default":"Hey what's the best tool for ai avatars"},{"id":"intro1","type":"string","role":"content","label":"Answer introduction 1","description":"First streamed answer paragraph.","default":"It really depends on what you're trying to do, because “AI avatars” has split into a few different categories."},{"id":"intro2","type":"string","role":"content","label":"Answer introduction 2","description":"Second streamed answer paragraph.","default":"For **most creators and marketers**, here's how I'd rank them today:"},{"id":"tableHeadUse","type":"string","role":"content","label":"Table heading 1","description":"Use-case column heading.","default":"Use case"},{"id":"tableHeadTool","type":"string","role":"content","label":"Table heading 2","description":"Tool column heading.","default":"Best tool"},{"id":"tableHeadWhy","type":"string","role":"content","label":"Table heading 3","description":"Explanation column heading.","default":"Why"},{"id":"row1Use","type":"string","role":"content","label":"Row 1 use case","description":"First comparison use case.","default":"Overall realism"},{"id":"row1Tool","type":"string","role":"content","label":"Row 1 tool","description":"Product in the recommended first row: the brand this video promotes, by its real name.","portrays":["subject_name","recommended_position"],"default":"HeyGen"},{"id":"row1Why","type":"string","role":"content","label":"Row 1 explanation","description":"Why the recommended first-row product wins. Describe the promoted brand from what its own site claims.","portrays":["recommended_position"],"default":"Most natural facial expressions, lip sync, gestures, voice cloning and localization. Benchmark for talking head videos."},{"id":"row1Chip","type":"string","role":"content","label":"Row 1 source","description":"Source chip on the first row. It asserts where the ranking came from, so use a neutral label whenever the comparison is written for this remix.","portrays":["authority_badge"],"default":"Official A.I Ranking"},{"id":"row2Use","type":"string","role":"content","label":"Row 2 use case","description":"Second comparison use case.","default":"Enterprise/training"},{"id":"row2Tool","type":"string","role":"content","label":"Row 2 tool","description":"Product compared in the second row: a real product other than the brand this video promotes.","portrays":["competitor_name"],"default":"Synthesia"},{"id":"row2Why","type":"string","role":"content","label":"Row 2 explanation","description":"Second recommendation explanation.","default":"Better collaboration, SCORM, compliance, team workflows; less creator-focused."},{"id":"row2Chip","type":"string","role":"content","label":"Row 2 source","description":"Source chip on the second row. It asserts where the ranking came from, so use a neutral label whenever the comparison is written for this remix.","portrays":["authority_badge"],"default":"Official A.I Ranking"},{"id":"row3Use","type":"string","role":"content","label":"Row 3 use case","description":"Third comparison use case.","default":"Mobile UGC"},{"id":"row3Tool","type":"string","role":"content","label":"Row 3 tool","description":"Product compared in the third row: a real product other than the brand this video promotes.","portrays":["competitor_name"],"default":"Captions"},{"id":"row3Why","type":"string","role":"content","label":"Row 3 explanation","description":"Third recommendation explanation.","default":"Extremely fast mobile workflow and social editing. Great for Reels creators."},{"id":"row3Chip","type":"string","role":"content","label":"Row 3 source","description":"Source chip on the third row. It asserts where the ranking came from, so use a neutral label whenever the comparison is written for this remix.","portrays":["authority_badge"],"default":"Creator Stack"},{"id":"row4Use","type":"string","role":"content","label":"Row 4 use case","description":"Fourth comparison use case.","default":"Real-time conversations"},{"id":"row4Tool","type":"string","role":"content","label":"Row 4 tool","description":"Product compared in the fourth row: a real product other than the brand this video promotes.","portrays":["competitor_name"],"default":"Tavus"},{"id":"row4Why","type":"string","role":"content","label":"Row 4 explanation","description":"Fourth recommendation explanation.","default":"Interactive avatars that can hold live conversations."},{"id":"row4Chip","type":"string","role":"content","label":"Row 4 source","description":"Source chip on the fourth row. It asserts where the ranking came from, so use a neutral label whenever the comparison is written for this remix.","portrays":["authority_badge"],"default":"Creator Stack"}]}
|
||||
meta={{"duration":14.9,"width":1080,"height":1920,"category":"Showcases","badge":"Stable","codeLines":1444}}
|
||||
attribution={{"author":"Jake Moran","path":"registry/blocks/chatgpt-exchange","tags":["showcase","mock-ui","ai","chat","vertical","ad-template"]}}
|
||||
hasCode
|
||||
rawUrl="https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/chatgpt-exchange/chatgpt-exchange.html"
|
||||
>
|
||||
|
||||
<InstallCommand command="npx hyperframes add chatgpt-exchange" item="chatgpt-exchange" />
|
||||
<CatalogSlot slot="code">
|
||||
|
||||
That writes `compositions/chatgpt-exchange.html`, plus 5 supporting files under `assets/fonts/`.
|
||||
|
||||
<iframe
|
||||
className="w-full aspect-video rounded-xl border-0 bg-zinc-100 dark:bg-zinc-800"
|
||||
title="chatgpt-exchange preview"
|
||||
loading="lazy"
|
||||
srcDoc={`<!doctype html><html><head><meta charset="utf-8"><style>html,body{margin:0;height:100%;overflow:hidden;background:transparent}hyperframes-player{display:block;width:100%;height:100%}</style><script src="https://cdn.jsdelivr.net/npm/@hyperframes/player@latest/dist/hyperframes-player.global.js"><\/script></head><body><script>fetch("/public/catalog/blocks/chatgpt-exchange.json").then(function(r){return r.json()}).then(function(d){var p=document.createElement("hyperframes-player");p.setAttribute("srcdoc",d.html);p.setAttribute("controls","");p.setAttribute("autoplay","");p.setAttribute("loop","");p.setAttribute("muted","");p.setAttribute("poster","https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/chatgpt-exchange.png");document.body.appendChild(p)});<\/script></body></html>`}
|
||||
/>
|
||||
|
||||
## Add it to your video
|
||||
|
||||
It runs for 14.9 seconds at 1080×1920. Paste this into your composition:
|
||||
|
||||
```html index.html
|
||||
<div
|
||||
data-composition-id="chatgpt-exchange"
|
||||
data-composition-src="compositions/chatgpt-exchange.html"
|
||||
data-start="0"
|
||||
data-duration="14.9"
|
||||
data-track-index="1"
|
||||
data-width="1080"
|
||||
data-height="1920"
|
||||
></div>
|
||||
```
|
||||
|
||||
Move it in time with `data-start`. Put it on a different timeline row with
|
||||
`data-track-index`. See [data attributes](/concepts/data-attributes) for the rest.
|
||||
|
||||
## Source
|
||||
|
||||
<Accordion title="chatgpt-exchange.html">
|
||||
|
||||
```html
|
||||
```html chatgpt-exchange.html
|
||||
<!doctype html>
|
||||
<html
|
||||
lang="en"
|
||||
@@ -1488,7 +1469,36 @@ Move it in time with `data-start`. Put it on a different timeline row with
|
||||
</html>
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
</CatalogSlot>
|
||||
|
||||
<CatalogSlot slot="install">
|
||||
|
||||
<InstallCommand command="npx hyperframes add chatgpt-exchange" item="chatgpt-exchange" />
|
||||
|
||||
That writes `compositions/chatgpt-exchange.html`, plus 5 supporting files under `assets/fonts/`.
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
<CatalogSlot slot="docs">
|
||||
|
||||
## Add it to your video
|
||||
|
||||
It runs for 14.9 seconds at 1080×1920. Paste this into your composition:
|
||||
|
||||
```html index.html
|
||||
<div
|
||||
data-composition-id="chatgpt-exchange"
|
||||
data-composition-src="compositions/chatgpt-exchange.html"
|
||||
data-start="0"
|
||||
data-duration="14.9"
|
||||
data-track-index="1"
|
||||
data-width="1080"
|
||||
data-height="1920"
|
||||
></div>
|
||||
```
|
||||
|
||||
Move it in time with `data-start`. Put it on a different timeline row with
|
||||
`data-track-index`. See [data attributes](/concepts/data-attributes) for the rest.
|
||||
|
||||
{/* hf:generated-footer */}
|
||||
|
||||
@@ -1501,3 +1511,7 @@ Created by Jake Moran.
|
||||
- [Browse the complete Catalog](/catalog)
|
||||
- [Add assets and Catalog items in Studio](/studio/assets-and-blocks)
|
||||
- [Build a richer composition](/go-further)
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
</CatalogDetail>
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
---
|
||||
title: "Chromatic Radial Split"
|
||||
description: "Shader transition with chromatic aberration radial split"
|
||||
mode: "frame"
|
||||
---
|
||||
|
||||
import { CatalogDetail, CatalogSlot } from "/snippets/catalog-detail.jsx";
|
||||
import { InstallCommand } from "/snippets/install-command.jsx";
|
||||
|
||||
## Install
|
||||
<CatalogDetail
|
||||
previewSrc="/public/catalog/blocks/chromatic-radial-split.json"
|
||||
compositionId="chromatic-radial-split"
|
||||
compositionSrc="compositions/chromatic-radial-split.html"
|
||||
title="Chromatic Radial Split"
|
||||
description="Shader transition with chromatic aberration radial split"
|
||||
variables={[]}
|
||||
meta={{"duration":4,"width":1920,"height":1080,"category":"Shader Transitions","badge":"Stable","codeLines":366}}
|
||||
attribution={{"path":"registry/blocks/chromatic-radial-split","tags":["transition","shader"]}}
|
||||
hasCode
|
||||
rawUrl="https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/chromatic-radial-split/chromatic-radial-split.html"
|
||||
>
|
||||
|
||||
<InstallCommand command="npx hyperframes add chromatic-radial-split" item="chromatic-radial-split" />
|
||||
<CatalogSlot slot="code">
|
||||
|
||||
That writes one file: `compositions/chromatic-radial-split.html`.
|
||||
|
||||
<iframe
|
||||
className="w-full aspect-video rounded-xl border-0 bg-zinc-100 dark:bg-zinc-800"
|
||||
title="chromatic-radial-split preview"
|
||||
loading="lazy"
|
||||
srcDoc={`<!doctype html><html><head><meta charset="utf-8"><style>html,body{margin:0;height:100%;overflow:hidden;background:transparent}hyperframes-player{display:block;width:100%;height:100%}</style><script src="https://cdn.jsdelivr.net/npm/@hyperframes/player@latest/dist/hyperframes-player.global.js"><\/script></head><body><script>fetch("/public/catalog/blocks/chromatic-radial-split.json").then(function(r){return r.json()}).then(function(d){var p=document.createElement("hyperframes-player");p.setAttribute("srcdoc",d.html);p.setAttribute("controls","");p.setAttribute("autoplay","");p.setAttribute("loop","");p.setAttribute("muted","");p.setAttribute("poster","https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/chromatic-radial-split.png");document.body.appendChild(p)});<\/script></body></html>`}
|
||||
/>
|
||||
|
||||
## Source
|
||||
|
||||
<Accordion title="chromatic-radial-split.html">
|
||||
|
||||
```html
|
||||
```html chromatic-radial-split.html
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
@@ -391,16 +391,37 @@ That writes one file: `compositions/chromatic-radial-split.html`.
|
||||
</html>
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
</CatalogSlot>
|
||||
|
||||
## Usage
|
||||
<CatalogSlot slot="install">
|
||||
|
||||
After installing, add the block to your host composition:
|
||||
<InstallCommand command="npx hyperframes add chromatic-radial-split" item="chromatic-radial-split" />
|
||||
|
||||
```html
|
||||
<div data-composition-id="chromatic-radial-split" data-composition-src="compositions/chromatic-radial-split.html" data-start="0" data-duration="4" data-track-index="1" data-width="1920" data-height="1080"></div>
|
||||
That writes one file: `compositions/chromatic-radial-split.html`.
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
<CatalogSlot slot="docs">
|
||||
|
||||
## Add it to your video
|
||||
|
||||
It runs for 4 seconds at 1920×1080. Paste this into your composition:
|
||||
|
||||
```html index.html
|
||||
<div
|
||||
data-composition-id="chromatic-radial-split"
|
||||
data-composition-src="compositions/chromatic-radial-split.html"
|
||||
data-start="0"
|
||||
data-duration="4"
|
||||
data-track-index="1"
|
||||
data-width="1920"
|
||||
data-height="1080"
|
||||
></div>
|
||||
```
|
||||
|
||||
Move it in time with `data-start`. Put it on a different timeline row with
|
||||
`data-track-index`. See [data attributes](/concepts/data-attributes) for the rest.
|
||||
|
||||
{/* hf:generated-footer */}
|
||||
|
||||
Tagged `transition` `shader`.
|
||||
@@ -410,3 +431,7 @@ Tagged `transition` `shader`.
|
||||
- [Browse the complete Catalog](/catalog)
|
||||
- [Add assets and Catalog items in Studio](/studio/assets-and-blocks)
|
||||
- [Build a richer composition](/go-further)
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
</CatalogDetail>
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
---
|
||||
title: "Cinematic Zoom"
|
||||
description: "Shader transition with dramatic zoom blur"
|
||||
mode: "frame"
|
||||
---
|
||||
|
||||
import { CatalogDetail, CatalogSlot } from "/snippets/catalog-detail.jsx";
|
||||
import { InstallCommand } from "/snippets/install-command.jsx";
|
||||
|
||||
## Install
|
||||
<CatalogDetail
|
||||
previewSrc="/public/catalog/blocks/cinematic-zoom.json"
|
||||
compositionId="cinematic-zoom"
|
||||
compositionSrc="compositions/cinematic-zoom.html"
|
||||
title="Cinematic Zoom"
|
||||
description="Shader transition with dramatic zoom blur"
|
||||
variables={[]}
|
||||
meta={{"duration":4,"width":1920,"height":1080,"category":"Shader Transitions","badge":"Stable","codeLines":366}}
|
||||
attribution={{"path":"registry/blocks/cinematic-zoom","tags":["transition","shader"]}}
|
||||
hasCode
|
||||
rawUrl="https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/cinematic-zoom/cinematic-zoom.html"
|
||||
>
|
||||
|
||||
<InstallCommand command="npx hyperframes add cinematic-zoom" item="cinematic-zoom" />
|
||||
<CatalogSlot slot="code">
|
||||
|
||||
That writes one file: `compositions/cinematic-zoom.html`.
|
||||
|
||||
<iframe
|
||||
className="w-full aspect-video rounded-xl border-0 bg-zinc-100 dark:bg-zinc-800"
|
||||
title="cinematic-zoom preview"
|
||||
loading="lazy"
|
||||
srcDoc={`<!doctype html><html><head><meta charset="utf-8"><style>html,body{margin:0;height:100%;overflow:hidden;background:transparent}hyperframes-player{display:block;width:100%;height:100%}</style><script src="https://cdn.jsdelivr.net/npm/@hyperframes/player@latest/dist/hyperframes-player.global.js"><\/script></head><body><script>fetch("/public/catalog/blocks/cinematic-zoom.json").then(function(r){return r.json()}).then(function(d){var p=document.createElement("hyperframes-player");p.setAttribute("srcdoc",d.html);p.setAttribute("controls","");p.setAttribute("autoplay","");p.setAttribute("loop","");p.setAttribute("muted","");p.setAttribute("poster","https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/cinematic-zoom.png");document.body.appendChild(p)});<\/script></body></html>`}
|
||||
/>
|
||||
|
||||
## Source
|
||||
|
||||
<Accordion title="cinematic-zoom.html">
|
||||
|
||||
```html
|
||||
```html cinematic-zoom.html
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
@@ -391,16 +391,37 @@ That writes one file: `compositions/cinematic-zoom.html`.
|
||||
</html>
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
</CatalogSlot>
|
||||
|
||||
## Usage
|
||||
<CatalogSlot slot="install">
|
||||
|
||||
After installing, add the block to your host composition:
|
||||
<InstallCommand command="npx hyperframes add cinematic-zoom" item="cinematic-zoom" />
|
||||
|
||||
```html
|
||||
<div data-composition-id="cinematic-zoom" data-composition-src="compositions/cinematic-zoom.html" data-start="0" data-duration="4" data-track-index="1" data-width="1920" data-height="1080"></div>
|
||||
That writes one file: `compositions/cinematic-zoom.html`.
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
<CatalogSlot slot="docs">
|
||||
|
||||
## Add it to your video
|
||||
|
||||
It runs for 4 seconds at 1920×1080. Paste this into your composition:
|
||||
|
||||
```html index.html
|
||||
<div
|
||||
data-composition-id="cinematic-zoom"
|
||||
data-composition-src="compositions/cinematic-zoom.html"
|
||||
data-start="0"
|
||||
data-duration="4"
|
||||
data-track-index="1"
|
||||
data-width="1920"
|
||||
data-height="1080"
|
||||
></div>
|
||||
```
|
||||
|
||||
Move it in time with `data-start`. Put it on a different timeline row with
|
||||
`data-track-index`. See [data attributes](/concepts/data-attributes) for the rest.
|
||||
|
||||
{/* hf:generated-footer */}
|
||||
|
||||
Tagged `transition` `shader`.
|
||||
@@ -410,3 +431,7 @@ Tagged `transition` `shader`.
|
||||
- [Browse the complete Catalog](/catalog)
|
||||
- [Add assets and Catalog items in Studio](/studio/assets-and-blocks)
|
||||
- [Build a richer composition](/go-further)
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
</CatalogDetail>
|
||||
|
||||
@@ -1,47 +1,28 @@
|
||||
---
|
||||
title: "Claude Exchange"
|
||||
description: "A portrait Claude-style exchange types a prompt, works through search and reasoning steps, then streams a detailed answer with citations and actions"
|
||||
mode: "frame"
|
||||
---
|
||||
|
||||
import { CatalogDetail, CatalogSlot } from "/snippets/catalog-detail.jsx";
|
||||
import { InstallCommand } from "/snippets/install-command.jsx";
|
||||
|
||||
## Install
|
||||
<CatalogDetail
|
||||
previewSrc="/public/catalog/blocks/claude-exchange.json"
|
||||
compositionId="claude-exchange"
|
||||
compositionSrc="compositions/claude-exchange.html"
|
||||
title="Claude Exchange"
|
||||
description="A portrait Claude-style exchange types a prompt, works through search and reasoning steps, then streams a detailed answer with citations and actions"
|
||||
variables={[{"id":"prompt","type":"string","role":"content","label":"User prompt","description":"Prompt typed into Claude; keep close to the original length.","default":"What's the best tool for ai avatars?"},{"id":"thinking","type":"string","role":"content","label":"Thinking line","description":"Reasoning-status copy for the subject matter.","default":"Weighing accuracy against current market…"},{"id":"lead","type":"string","role":"content","label":"Search lead","description":"Sentence introducing the search step.","default":"I'll search for the current state of this space since AI avatar tools move fast."},{"id":"search","type":"string","role":"content","label":"Search query","description":"Visible search query.","default":"best AI avatar video generator 2026"},{"id":"answer1","type":"string","role":"content","label":"Answer paragraph 1","description":"First answer paragraph.","default":"It depends on what you're making, but the field has consolidated fast and one platform now covers most of it."},{"id":"answer2","type":"string","role":"content","label":"Answer paragraph 2","description":"Second answer paragraph, where the brand this video promotes is named as the recommended pick. Keep any evidence claim to what that brand publishes.","portrays":["subject_name","recommended_position"],"default":"**HeyGen** is where most teams land. Independent testing, not just vendor blogs, puts **Avatar IV** highest for talking-head realism, with facial micro-expressions and gesture control that hold up at a full-screen crop {HeyGen}. It also has the deepest enterprise adoption of the group, including a large share of the Fortune 100, which matters if you need the same presenter on brand across a year of campaigns."},{"id":"answer3","type":"string","role":"content","label":"Answer paragraph 3","description":"Lead-in to the use-case list.","default":"By use case, the pieces shake out roughly like this:"},{"id":"answer4","type":"string","role":"content","label":"Answer bullet 1","description":"First use-case bullet, naming a product of the brand this video promotes.","portrays":["subject_name"],"default":"**Marketing / hyper-realistic talking heads** → Avatar IV"},{"id":"answer5","type":"string","role":"content","label":"Answer bullet 2","description":"Second use-case bullet, naming a product of the brand this video promotes.","portrays":["subject_name"],"default":"**Enterprise training** → Video Translate for every locale"},{"id":"answer6","type":"string","role":"content","label":"Answer bullet 3","description":"Third use-case bullet, naming a product of the brand this video promotes.","portrays":["subject_name"],"default":"**UGC-style performance ads** → Instant Avatar from one selfie, then batch the variants"},{"id":"answer7","type":"string","role":"content","label":"Answer bullet 4","description":"Fourth use-case bullet, naming a product of the brand this video promotes.","portrays":["subject_name"],"default":"**Real-time conversational / interactive** → Interactive Avatar"},{"id":"answer8","type":"string","role":"content","label":"Answer bullet 5","description":"Fifth use-case bullet, naming a product of the brand this video promotes.","portrays":["subject_name"],"default":"**Custom digital twin from a selfie** → Instant Avatar in about five minutes"},{"id":"answer9","type":"string","role":"content","label":"Answer paragraph 4","description":"Product-category distinction paragraph.","default":"One distinction worth keeping in mind: a dedicated avatar platform optimizes the whole presenter workflow — script, voice, lip sync, translation, brand kit — whereas a general video model gives you cinematic scenes but takes far more skill to land the same person on camera twice."},{"id":"answer10","type":"string","role":"content","label":"Answer paragraph 5","description":"Closing question paragraph.","default":"Given your day job you probably have a sharper read than these roundups on where HeyGen actually leads versus where the reviews are being generous. Is this for a specific project, or comparing for positioning?"}]}
|
||||
meta={{"duration":21.4,"width":1080,"height":1920,"category":"Showcases","badge":"Stable","codeLines":1643}}
|
||||
attribution={{"author":"Jake Moran","path":"registry/blocks/claude-exchange","tags":["showcase","mock-ui","ai","chat","vertical","ad-template"]}}
|
||||
hasCode
|
||||
rawUrl="https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/claude-exchange/claude-exchange.html"
|
||||
>
|
||||
|
||||
<InstallCommand command="npx hyperframes add claude-exchange" item="claude-exchange" />
|
||||
<CatalogSlot slot="code">
|
||||
|
||||
That writes `compositions/claude-exchange.html`, plus 8 supporting files under `assets/fonts/`.
|
||||
|
||||
<iframe
|
||||
className="w-full aspect-video rounded-xl border-0 bg-zinc-100 dark:bg-zinc-800"
|
||||
title="claude-exchange preview"
|
||||
loading="lazy"
|
||||
srcDoc={`<!doctype html><html><head><meta charset="utf-8"><style>html,body{margin:0;height:100%;overflow:hidden;background:transparent}hyperframes-player{display:block;width:100%;height:100%}</style><script src="https://cdn.jsdelivr.net/npm/@hyperframes/player@latest/dist/hyperframes-player.global.js"><\/script></head><body><script>fetch("/public/catalog/blocks/claude-exchange.json").then(function(r){return r.json()}).then(function(d){var p=document.createElement("hyperframes-player");p.setAttribute("srcdoc",d.html);p.setAttribute("controls","");p.setAttribute("autoplay","");p.setAttribute("loop","");p.setAttribute("muted","");p.setAttribute("poster","https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/claude-exchange.png");document.body.appendChild(p)});<\/script></body></html>`}
|
||||
/>
|
||||
|
||||
## Add it to your video
|
||||
|
||||
It runs for 21.4 seconds at 1080×1920. Paste this into your composition:
|
||||
|
||||
```html index.html
|
||||
<div
|
||||
data-composition-id="claude-exchange"
|
||||
data-composition-src="compositions/claude-exchange.html"
|
||||
data-start="0"
|
||||
data-duration="21.4"
|
||||
data-track-index="1"
|
||||
data-width="1080"
|
||||
data-height="1920"
|
||||
></div>
|
||||
```
|
||||
|
||||
Move it in time with `data-start`. Put it on a different timeline row with
|
||||
`data-track-index`. See [data attributes](/concepts/data-attributes) for the rest.
|
||||
|
||||
## Source
|
||||
|
||||
<Accordion title="claude-exchange.html">
|
||||
|
||||
```html
|
||||
```html claude-exchange.html
|
||||
<!doctype html>
|
||||
<html
|
||||
lang="en"
|
||||
@@ -1687,7 +1668,36 @@ Move it in time with `data-start`. Put it on a different timeline row with
|
||||
</html>
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
</CatalogSlot>
|
||||
|
||||
<CatalogSlot slot="install">
|
||||
|
||||
<InstallCommand command="npx hyperframes add claude-exchange" item="claude-exchange" />
|
||||
|
||||
That writes `compositions/claude-exchange.html`, plus 8 supporting files under `assets/fonts/`.
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
<CatalogSlot slot="docs">
|
||||
|
||||
## Add it to your video
|
||||
|
||||
It runs for 21.4 seconds at 1080×1920. Paste this into your composition:
|
||||
|
||||
```html index.html
|
||||
<div
|
||||
data-composition-id="claude-exchange"
|
||||
data-composition-src="compositions/claude-exchange.html"
|
||||
data-start="0"
|
||||
data-duration="21.4"
|
||||
data-track-index="1"
|
||||
data-width="1080"
|
||||
data-height="1920"
|
||||
></div>
|
||||
```
|
||||
|
||||
Move it in time with `data-start`. Put it on a different timeline row with
|
||||
`data-track-index`. See [data attributes](/concepts/data-attributes) for the rest.
|
||||
|
||||
{/* hf:generated-footer */}
|
||||
|
||||
@@ -1700,3 +1710,7 @@ Created by Jake Moran.
|
||||
- [Browse the complete Catalog](/catalog)
|
||||
- [Add assets and Catalog items in Studio](/studio/assets-and-blocks)
|
||||
- [Build a richer composition](/go-further)
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
</CatalogDetail>
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
---
|
||||
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."
|
||||
mode: "frame"
|
||||
---
|
||||
|
||||
import { CatalogDetail, CatalogSlot } from "/snippets/catalog-detail.jsx";
|
||||
import { InstallCommand } from "/snippets/install-command.jsx";
|
||||
|
||||
## Install
|
||||
<CatalogDetail
|
||||
previewSrc="/public/catalog/blocks/code-3d-extrude.json"
|
||||
compositionId="code-3d-extrude"
|
||||
compositionSrc="compositions/code-3d-extrude.html"
|
||||
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."
|
||||
variables={[]}
|
||||
meta={{"duration":8,"width":1920,"height":1080,"category":"Code Animations","badge":"Stable","codeLines":616}}
|
||||
attribution={{"path":"registry/blocks/code-3d-extrude","tags":["code","code-animation","3d","webgl","developer"]}}
|
||||
hasCode
|
||||
rawUrl="https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/code-3d-extrude/code-3d-extrude.html"
|
||||
>
|
||||
|
||||
<InstallCommand command="npx hyperframes add code-3d-extrude" item="code-3d-extrude" />
|
||||
<CatalogSlot slot="code">
|
||||
|
||||
That writes one file: `compositions/code-3d-extrude.html`.
|
||||
|
||||
<iframe
|
||||
className="w-full aspect-video rounded-xl border-0 bg-zinc-100 dark:bg-zinc-800"
|
||||
title="code-3d-extrude preview"
|
||||
loading="lazy"
|
||||
srcDoc={`<!doctype html><html><head><meta charset="utf-8"><style>html,body{margin:0;height:100%;overflow:hidden;background:transparent}hyperframes-player{display:block;width:100%;height:100%}</style><script src="https://cdn.jsdelivr.net/npm/@hyperframes/player@latest/dist/hyperframes-player.global.js"><\/script></head><body><script>fetch("/public/catalog/blocks/code-3d-extrude.json").then(function(r){return r.json()}).then(function(d){var p=document.createElement("hyperframes-player");p.setAttribute("srcdoc",d.html);p.setAttribute("controls","");p.setAttribute("autoplay","");p.setAttribute("loop","");p.setAttribute("muted","");p.setAttribute("poster","https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/code-3d-extrude.png");document.body.appendChild(p)});<\/script></body></html>`}
|
||||
/>
|
||||
|
||||
## Source
|
||||
|
||||
<Accordion title="code-3d-extrude.html">
|
||||
|
||||
```html
|
||||
```html code-3d-extrude.html
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
@@ -599,7 +599,6 @@ That writes one file: `compositions/code-3d-extrude.html`.
|
||||
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({
|
||||
@@ -627,6 +626,10 @@ That writes one file: `compositions/code-3d-extrude.html`.
|
||||
window.addEventListener("beforeunload", function () {
|
||||
disposeGL(renderer, scene);
|
||||
});
|
||||
window.__timelines[spec.id] = tl; // register AFTER setup completes
|
||||
if (typeof window.__hfForceTimelineRebind === "function") {
|
||||
window.__hfForceTimelineRebind(); // re-nest now that we are populated
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -638,16 +641,37 @@ That writes one file: `compositions/code-3d-extrude.html`.
|
||||
</html>
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
</CatalogSlot>
|
||||
|
||||
## Usage
|
||||
<CatalogSlot slot="install">
|
||||
|
||||
After installing, add the block to your host composition:
|
||||
<InstallCommand command="npx hyperframes add code-3d-extrude" item="code-3d-extrude" />
|
||||
|
||||
```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>
|
||||
That writes one file: `compositions/code-3d-extrude.html`.
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
<CatalogSlot slot="docs">
|
||||
|
||||
## Add it to your video
|
||||
|
||||
It runs for 8 seconds at 1920×1080. Paste this into your composition:
|
||||
|
||||
```html index.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>
|
||||
```
|
||||
|
||||
Move it in time with `data-start`. Put it on a different timeline row with
|
||||
`data-track-index`. See [data attributes](/concepts/data-attributes) for the rest.
|
||||
|
||||
{/* hf:generated-footer */}
|
||||
|
||||
Tagged `code` `code-animation` `3d` `webgl` `developer`.
|
||||
@@ -657,3 +681,7 @@ Tagged `code` `code-animation` `3d` `webgl` `developer`.
|
||||
- [Browse the complete Catalog](/catalog)
|
||||
- [Add assets and Catalog items in Studio](/studio/assets-and-blocks)
|
||||
- [Build a richer composition](/go-further)
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
</CatalogDetail>
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
---
|
||||
title: "Code Diff"
|
||||
description: "An edit shown as a colored diff — removed lines collapse in red, added lines expand in green."
|
||||
mode: "frame"
|
||||
---
|
||||
|
||||
import { CatalogDetail, CatalogSlot } from "/snippets/catalog-detail.jsx";
|
||||
import { InstallCommand } from "/snippets/install-command.jsx";
|
||||
|
||||
## Install
|
||||
<CatalogDetail
|
||||
previewSrc="/public/catalog/blocks/code-diff.json"
|
||||
compositionId="code-diff"
|
||||
compositionSrc="compositions/code-diff.html"
|
||||
title="Code Diff"
|
||||
description="An edit shown as a colored diff — removed lines collapse in red, added lines expand in green."
|
||||
variables={[]}
|
||||
meta={{"duration":6,"width":1920,"height":1080,"category":"Code Animations","badge":"Stable","codeLines":816}}
|
||||
attribution={{"path":"registry/blocks/code-diff","tags":["code","code-animation","diff","developer"]}}
|
||||
hasCode
|
||||
rawUrl="https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/code-diff/code-diff.html"
|
||||
>
|
||||
|
||||
<InstallCommand command="npx hyperframes add code-diff" item="code-diff" />
|
||||
<CatalogSlot slot="code">
|
||||
|
||||
That writes one file: `compositions/code-diff.html`.
|
||||
|
||||
<iframe
|
||||
className="w-full aspect-video rounded-xl border-0 bg-zinc-100 dark:bg-zinc-800"
|
||||
title="code-diff preview"
|
||||
loading="lazy"
|
||||
srcDoc={`<!doctype html><html><head><meta charset="utf-8"><style>html,body{margin:0;height:100%;overflow:hidden;background:transparent}hyperframes-player{display:block;width:100%;height:100%}</style><script src="https://cdn.jsdelivr.net/npm/@hyperframes/player@latest/dist/hyperframes-player.global.js"><\/script></head><body><script>fetch("/public/catalog/blocks/code-diff.json").then(function(r){return r.json()}).then(function(d){var p=document.createElement("hyperframes-player");p.setAttribute("srcdoc",d.html);p.setAttribute("controls","");p.setAttribute("autoplay","");p.setAttribute("loop","");p.setAttribute("muted","");p.setAttribute("poster","https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/code-diff.png");document.body.appendChild(p)});<\/script></body></html>`}
|
||||
/>
|
||||
|
||||
## Source
|
||||
|
||||
<Accordion title="code-diff.html">
|
||||
|
||||
```html
|
||||
```html code-diff.html
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
@@ -841,16 +841,37 @@ That writes one file: `compositions/code-diff.html`.
|
||||
</html>
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
</CatalogSlot>
|
||||
|
||||
## Usage
|
||||
<CatalogSlot slot="install">
|
||||
|
||||
After installing, add the block to your host composition:
|
||||
<InstallCommand command="npx hyperframes add code-diff" item="code-diff" />
|
||||
|
||||
```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>
|
||||
That writes one file: `compositions/code-diff.html`.
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
<CatalogSlot slot="docs">
|
||||
|
||||
## Add it to your video
|
||||
|
||||
It runs for 6 seconds at 1920×1080. Paste this into your composition:
|
||||
|
||||
```html index.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>
|
||||
```
|
||||
|
||||
Move it in time with `data-start`. Put it on a different timeline row with
|
||||
`data-track-index`. See [data attributes](/concepts/data-attributes) for the rest.
|
||||
|
||||
{/* hf:generated-footer */}
|
||||
|
||||
Tagged `code` `code-animation` `diff` `developer`.
|
||||
@@ -860,3 +881,7 @@ Tagged `code` `code-animation` `diff` `developer`.
|
||||
- [Browse the complete Catalog](/catalog)
|
||||
- [Add assets and Catalog items in Studio](/studio/assets-and-blocks)
|
||||
- [Build a richer composition](/go-further)
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
</CatalogDetail>
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
---
|
||||
title: "Code Highlight Sweep"
|
||||
description: "A highlight band sweeps across a target line while the surrounding context dims — draws the eye to one line."
|
||||
mode: "frame"
|
||||
---
|
||||
|
||||
import { CatalogDetail, CatalogSlot } from "/snippets/catalog-detail.jsx";
|
||||
import { InstallCommand } from "/snippets/install-command.jsx";
|
||||
|
||||
## Install
|
||||
<CatalogDetail
|
||||
previewSrc="/public/catalog/blocks/code-highlight.json"
|
||||
compositionId="code-highlight"
|
||||
compositionSrc="compositions/code-highlight.html"
|
||||
title="Code Highlight Sweep"
|
||||
description="A highlight band sweeps across a target line while the surrounding context dims — draws the eye to one line."
|
||||
variables={[]}
|
||||
meta={{"duration":5,"width":1920,"height":1080,"category":"Code Animations","badge":"Stable","codeLines":745}}
|
||||
attribution={{"path":"registry/blocks/code-highlight","tags":["code","code-animation","highlight","developer"]}}
|
||||
hasCode
|
||||
rawUrl="https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/code-highlight/code-highlight.html"
|
||||
>
|
||||
|
||||
<InstallCommand command="npx hyperframes add code-highlight" item="code-highlight" />
|
||||
<CatalogSlot slot="code">
|
||||
|
||||
That writes one file: `compositions/code-highlight.html`.
|
||||
|
||||
<iframe
|
||||
className="w-full aspect-video rounded-xl border-0 bg-zinc-100 dark:bg-zinc-800"
|
||||
title="code-highlight preview"
|
||||
loading="lazy"
|
||||
srcDoc={`<!doctype html><html><head><meta charset="utf-8"><style>html,body{margin:0;height:100%;overflow:hidden;background:transparent}hyperframes-player{display:block;width:100%;height:100%}</style><script src="https://cdn.jsdelivr.net/npm/@hyperframes/player@latest/dist/hyperframes-player.global.js"><\/script></head><body><script>fetch("/public/catalog/blocks/code-highlight.json").then(function(r){return r.json()}).then(function(d){var p=document.createElement("hyperframes-player");p.setAttribute("srcdoc",d.html);p.setAttribute("controls","");p.setAttribute("autoplay","");p.setAttribute("loop","");p.setAttribute("muted","");p.setAttribute("poster","https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/code-highlight.png");document.body.appendChild(p)});<\/script></body></html>`}
|
||||
/>
|
||||
|
||||
## Source
|
||||
|
||||
<Accordion title="code-highlight.html">
|
||||
|
||||
```html
|
||||
```html code-highlight.html
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
@@ -770,16 +770,37 @@ That writes one file: `compositions/code-highlight.html`.
|
||||
</html>
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
</CatalogSlot>
|
||||
|
||||
## Usage
|
||||
<CatalogSlot slot="install">
|
||||
|
||||
After installing, add the block to your host composition:
|
||||
<InstallCommand command="npx hyperframes add code-highlight" item="code-highlight" />
|
||||
|
||||
```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>
|
||||
That writes one file: `compositions/code-highlight.html`.
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
<CatalogSlot slot="docs">
|
||||
|
||||
## Add it to your video
|
||||
|
||||
It runs for 5 seconds at 1920×1080. Paste this into your composition:
|
||||
|
||||
```html index.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>
|
||||
```
|
||||
|
||||
Move it in time with `data-start`. Put it on a different timeline row with
|
||||
`data-track-index`. See [data attributes](/concepts/data-attributes) for the rest.
|
||||
|
||||
{/* hf:generated-footer */}
|
||||
|
||||
Tagged `code` `code-animation` `highlight` `developer`.
|
||||
@@ -789,3 +810,7 @@ Tagged `code` `code-animation` `highlight` `developer`.
|
||||
- [Browse the complete Catalog](/catalog)
|
||||
- [Add assets and Catalog items in Studio](/studio/assets-and-blocks)
|
||||
- [Build a richer composition](/go-further)
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
</CatalogDetail>
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
---
|
||||
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."
|
||||
mode: "frame"
|
||||
---
|
||||
|
||||
import { CatalogDetail, CatalogSlot } from "/snippets/catalog-detail.jsx";
|
||||
import { InstallCommand } from "/snippets/install-command.jsx";
|
||||
|
||||
## Install
|
||||
<CatalogDetail
|
||||
previewSrc="/public/catalog/blocks/code-morph.json"
|
||||
compositionId="code-morph"
|
||||
compositionSrc="compositions/code-morph.html"
|
||||
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."
|
||||
variables={[]}
|
||||
meta={{"duration":7,"width":1920,"height":1080,"category":"Code Animations","badge":"Stable","codeLines":1216}}
|
||||
attribution={{"path":"registry/blocks/code-morph","tags":["code","code-animation","morph","refactor","developer"]}}
|
||||
hasCode
|
||||
rawUrl="https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/code-morph/code-morph.html"
|
||||
>
|
||||
|
||||
<InstallCommand command="npx hyperframes add code-morph" item="code-morph" />
|
||||
<CatalogSlot slot="code">
|
||||
|
||||
That writes one file: `compositions/code-morph.html`.
|
||||
|
||||
<iframe
|
||||
className="w-full aspect-video rounded-xl border-0 bg-zinc-100 dark:bg-zinc-800"
|
||||
title="code-morph preview"
|
||||
loading="lazy"
|
||||
srcDoc={`<!doctype html><html><head><meta charset="utf-8"><style>html,body{margin:0;height:100%;overflow:hidden;background:transparent}hyperframes-player{display:block;width:100%;height:100%}</style><script src="https://cdn.jsdelivr.net/npm/@hyperframes/player@latest/dist/hyperframes-player.global.js"><\/script></head><body><script>fetch("/public/catalog/blocks/code-morph.json").then(function(r){return r.json()}).then(function(d){var p=document.createElement("hyperframes-player");p.setAttribute("srcdoc",d.html);p.setAttribute("controls","");p.setAttribute("autoplay","");p.setAttribute("loop","");p.setAttribute("muted","");p.setAttribute("poster","https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/code-morph.png");document.body.appendChild(p)});<\/script></body></html>`}
|
||||
/>
|
||||
|
||||
## Source
|
||||
|
||||
<Accordion title="code-morph.html">
|
||||
|
||||
```html
|
||||
```html code-morph.html
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
@@ -1241,16 +1241,37 @@ That writes one file: `compositions/code-morph.html`.
|
||||
</html>
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
</CatalogSlot>
|
||||
|
||||
## Usage
|
||||
<CatalogSlot slot="install">
|
||||
|
||||
After installing, add the block to your host composition:
|
||||
<InstallCommand command="npx hyperframes add code-morph" item="code-morph" />
|
||||
|
||||
```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>
|
||||
That writes one file: `compositions/code-morph.html`.
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
<CatalogSlot slot="docs">
|
||||
|
||||
## Add it to your video
|
||||
|
||||
It runs for 7 seconds at 1920×1080. Paste this into your composition:
|
||||
|
||||
```html index.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>
|
||||
```
|
||||
|
||||
Move it in time with `data-start`. Put it on a different timeline row with
|
||||
`data-track-index`. See [data attributes](/concepts/data-attributes) for the rest.
|
||||
|
||||
{/* hf:generated-footer */}
|
||||
|
||||
Tagged `code` `code-animation` `morph` `refactor` `developer`.
|
||||
@@ -1260,3 +1281,7 @@ Tagged `code` `code-animation` `morph` `refactor` `developer`.
|
||||
- [Browse the complete Catalog](/catalog)
|
||||
- [Add assets and Catalog items in Studio](/studio/assets-and-blocks)
|
||||
- [Build a richer composition](/go-further)
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
</CatalogDetail>
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
---
|
||||
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."
|
||||
mode: "frame"
|
||||
---
|
||||
|
||||
import { CatalogDetail, CatalogSlot } from "/snippets/catalog-detail.jsx";
|
||||
import { InstallCommand } from "/snippets/install-command.jsx";
|
||||
|
||||
## Install
|
||||
<CatalogDetail
|
||||
previewSrc="/public/catalog/blocks/code-particle-assemble.json"
|
||||
compositionId="code-particle-assemble"
|
||||
compositionSrc="compositions/code-particle-assemble.html"
|
||||
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."
|
||||
variables={[]}
|
||||
meta={{"duration":8,"width":1920,"height":1080,"category":"Code Animations","badge":"Stable","codeLines":482}}
|
||||
attribution={{"path":"registry/blocks/code-particle-assemble","tags":["code","code-animation","particles","gpu","webgl","developer"]}}
|
||||
hasCode
|
||||
rawUrl="https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/code-particle-assemble/code-particle-assemble.html"
|
||||
>
|
||||
|
||||
<InstallCommand command="npx hyperframes add code-particle-assemble" item="code-particle-assemble" />
|
||||
<CatalogSlot slot="code">
|
||||
|
||||
That writes one file: `compositions/code-particle-assemble.html`.
|
||||
|
||||
<iframe
|
||||
className="w-full aspect-video rounded-xl border-0 bg-zinc-100 dark:bg-zinc-800"
|
||||
title="code-particle-assemble preview"
|
||||
loading="lazy"
|
||||
srcDoc={`<!doctype html><html><head><meta charset="utf-8"><style>html,body{margin:0;height:100%;overflow:hidden;background:transparent}hyperframes-player{display:block;width:100%;height:100%}</style><script src="https://cdn.jsdelivr.net/npm/@hyperframes/player@latest/dist/hyperframes-player.global.js"><\/script></head><body><script>fetch("/public/catalog/blocks/code-particle-assemble.json").then(function(r){return r.json()}).then(function(d){var p=document.createElement("hyperframes-player");p.setAttribute("srcdoc",d.html);p.setAttribute("controls","");p.setAttribute("autoplay","");p.setAttribute("loop","");p.setAttribute("muted","");p.setAttribute("poster","https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/code-particle-assemble.png");document.body.appendChild(p)});<\/script></body></html>`}
|
||||
/>
|
||||
|
||||
## Source
|
||||
|
||||
<Accordion title="code-particle-assemble.html">
|
||||
|
||||
```html
|
||||
```html code-particle-assemble.html
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
@@ -465,7 +465,6 @@ That writes one file: `compositions/code-particle-assemble.html`.
|
||||
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({
|
||||
@@ -493,6 +492,10 @@ That writes one file: `compositions/code-particle-assemble.html`.
|
||||
window.addEventListener("beforeunload", function () {
|
||||
disposeGL(renderer, scene);
|
||||
});
|
||||
window.__timelines[spec.id] = tl; // register AFTER setup completes
|
||||
if (typeof window.__hfForceTimelineRebind === "function") {
|
||||
window.__hfForceTimelineRebind(); // re-nest now that we are populated
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -504,16 +507,37 @@ That writes one file: `compositions/code-particle-assemble.html`.
|
||||
</html>
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
</CatalogSlot>
|
||||
|
||||
## Usage
|
||||
<CatalogSlot slot="install">
|
||||
|
||||
After installing, add the block to your host composition:
|
||||
<InstallCommand command="npx hyperframes add code-particle-assemble" item="code-particle-assemble" />
|
||||
|
||||
```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>
|
||||
That writes one file: `compositions/code-particle-assemble.html`.
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
<CatalogSlot slot="docs">
|
||||
|
||||
## Add it to your video
|
||||
|
||||
It runs for 8 seconds at 1920×1080. Paste this into your composition:
|
||||
|
||||
```html index.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>
|
||||
```
|
||||
|
||||
Move it in time with `data-start`. Put it on a different timeline row with
|
||||
`data-track-index`. See [data attributes](/concepts/data-attributes) for the rest.
|
||||
|
||||
{/* hf:generated-footer */}
|
||||
|
||||
Tagged `code` `code-animation` `particles` `gpu` `webgl` `developer`.
|
||||
@@ -523,3 +547,7 @@ Tagged `code` `code-animation` `particles` `gpu` `webgl` `developer`.
|
||||
- [Browse the complete Catalog](/catalog)
|
||||
- [Add assets and Catalog items in Studio](/studio/assets-and-blocks)
|
||||
- [Build a richer composition](/go-further)
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
</CatalogDetail>
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
---
|
||||
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."
|
||||
mode: "frame"
|
||||
---
|
||||
|
||||
import { CatalogDetail, CatalogSlot } from "/snippets/catalog-detail.jsx";
|
||||
import { InstallCommand } from "/snippets/install-command.jsx";
|
||||
|
||||
## Install
|
||||
<CatalogDetail
|
||||
previewSrc="/public/catalog/blocks/code-scroll.json"
|
||||
compositionId="code-scroll"
|
||||
compositionSrc="compositions/code-scroll.html"
|
||||
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."
|
||||
variables={[]}
|
||||
meta={{"duration":6,"width":1920,"height":1080,"category":"Code Animations","badge":"Stable","codeLines":1560}}
|
||||
attribution={{"path":"registry/blocks/code-scroll","tags":["code","code-animation","scroll","developer"]}}
|
||||
hasCode
|
||||
rawUrl="https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/code-scroll/code-scroll.html"
|
||||
>
|
||||
|
||||
<InstallCommand command="npx hyperframes add code-scroll" item="code-scroll" />
|
||||
<CatalogSlot slot="code">
|
||||
|
||||
That writes one file: `compositions/code-scroll.html`.
|
||||
|
||||
<iframe
|
||||
className="w-full aspect-video rounded-xl border-0 bg-zinc-100 dark:bg-zinc-800"
|
||||
title="code-scroll preview"
|
||||
loading="lazy"
|
||||
srcDoc={`<!doctype html><html><head><meta charset="utf-8"><style>html,body{margin:0;height:100%;overflow:hidden;background:transparent}hyperframes-player{display:block;width:100%;height:100%}</style><script src="https://cdn.jsdelivr.net/npm/@hyperframes/player@latest/dist/hyperframes-player.global.js"><\/script></head><body><script>fetch("/public/catalog/blocks/code-scroll.json").then(function(r){return r.json()}).then(function(d){var p=document.createElement("hyperframes-player");p.setAttribute("srcdoc",d.html);p.setAttribute("controls","");p.setAttribute("autoplay","");p.setAttribute("loop","");p.setAttribute("muted","");p.setAttribute("poster","https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/code-scroll.png");document.body.appendChild(p)});<\/script></body></html>`}
|
||||
/>
|
||||
|
||||
## Source
|
||||
|
||||
<Accordion title="code-scroll.html">
|
||||
|
||||
```html
|
||||
```html code-scroll.html
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
@@ -1585,16 +1585,37 @@ That writes one file: `compositions/code-scroll.html`.
|
||||
</html>
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
</CatalogSlot>
|
||||
|
||||
## Usage
|
||||
<CatalogSlot slot="install">
|
||||
|
||||
After installing, add the block to your host composition:
|
||||
<InstallCommand command="npx hyperframes add code-scroll" item="code-scroll" />
|
||||
|
||||
```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>
|
||||
That writes one file: `compositions/code-scroll.html`.
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
<CatalogSlot slot="docs">
|
||||
|
||||
## Add it to your video
|
||||
|
||||
It runs for 6 seconds at 1920×1080. Paste this into your composition:
|
||||
|
||||
```html index.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>
|
||||
```
|
||||
|
||||
Move it in time with `data-start`. Put it on a different timeline row with
|
||||
`data-track-index`. See [data attributes](/concepts/data-attributes) for the rest.
|
||||
|
||||
{/* hf:generated-footer */}
|
||||
|
||||
Tagged `code` `code-animation` `scroll` `developer`.
|
||||
@@ -1604,3 +1625,7 @@ Tagged `code` `code-animation` `scroll` `developer`.
|
||||
- [Browse the complete Catalog](/catalog)
|
||||
- [Add assets and Catalog items in Studio](/studio/assets-and-blocks)
|
||||
- [Build a richer composition](/go-further)
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
</CatalogDetail>
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
---
|
||||
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."
|
||||
mode: "frame"
|
||||
---
|
||||
|
||||
import { CatalogDetail, CatalogSlot } from "/snippets/catalog-detail.jsx";
|
||||
import { InstallCommand } from "/snippets/install-command.jsx";
|
||||
|
||||
## Install
|
||||
<CatalogDetail
|
||||
previewSrc="/public/catalog/blocks/code-shader-dissolve.json"
|
||||
compositionId="code-shader-dissolve"
|
||||
compositionSrc="compositions/code-shader-dissolve.html"
|
||||
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."
|
||||
variables={[]}
|
||||
meta={{"duration":7,"width":1920,"height":1080,"category":"Code Animations","badge":"Stable","codeLines":538}}
|
||||
attribution={{"path":"registry/blocks/code-shader-dissolve","tags":["code","code-animation","shader","webgl","developer"]}}
|
||||
hasCode
|
||||
rawUrl="https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/code-shader-dissolve/code-shader-dissolve.html"
|
||||
>
|
||||
|
||||
<InstallCommand command="npx hyperframes add code-shader-dissolve" item="code-shader-dissolve" />
|
||||
<CatalogSlot slot="code">
|
||||
|
||||
That writes one file: `compositions/code-shader-dissolve.html`.
|
||||
|
||||
<iframe
|
||||
className="w-full aspect-video rounded-xl border-0 bg-zinc-100 dark:bg-zinc-800"
|
||||
title="code-shader-dissolve preview"
|
||||
loading="lazy"
|
||||
srcDoc={`<!doctype html><html><head><meta charset="utf-8"><style>html,body{margin:0;height:100%;overflow:hidden;background:transparent}hyperframes-player{display:block;width:100%;height:100%}</style><script src="https://cdn.jsdelivr.net/npm/@hyperframes/player@latest/dist/hyperframes-player.global.js"><\/script></head><body><script>fetch("/public/catalog/blocks/code-shader-dissolve.json").then(function(r){return r.json()}).then(function(d){var p=document.createElement("hyperframes-player");p.setAttribute("srcdoc",d.html);p.setAttribute("controls","");p.setAttribute("autoplay","");p.setAttribute("loop","");p.setAttribute("muted","");p.setAttribute("poster","https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/code-shader-dissolve.png");document.body.appendChild(p)});<\/script></body></html>`}
|
||||
/>
|
||||
|
||||
## Source
|
||||
|
||||
<Accordion title="code-shader-dissolve.html">
|
||||
|
||||
```html
|
||||
```html code-shader-dissolve.html
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
@@ -521,7 +521,6 @@ That writes one file: `compositions/code-shader-dissolve.html`.
|
||||
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({
|
||||
@@ -549,6 +548,10 @@ That writes one file: `compositions/code-shader-dissolve.html`.
|
||||
window.addEventListener("beforeunload", function () {
|
||||
disposeGL(renderer, scene);
|
||||
});
|
||||
window.__timelines[spec.id] = tl; // register AFTER setup completes
|
||||
if (typeof window.__hfForceTimelineRebind === "function") {
|
||||
window.__hfForceTimelineRebind(); // re-nest now that we are populated
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -560,16 +563,37 @@ That writes one file: `compositions/code-shader-dissolve.html`.
|
||||
</html>
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
</CatalogSlot>
|
||||
|
||||
## Usage
|
||||
<CatalogSlot slot="install">
|
||||
|
||||
After installing, add the block to your host composition:
|
||||
<InstallCommand command="npx hyperframes add code-shader-dissolve" item="code-shader-dissolve" />
|
||||
|
||||
```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>
|
||||
That writes one file: `compositions/code-shader-dissolve.html`.
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
<CatalogSlot slot="docs">
|
||||
|
||||
## Add it to your video
|
||||
|
||||
It runs for 7 seconds at 1920×1080. Paste this into your composition:
|
||||
|
||||
```html index.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>
|
||||
```
|
||||
|
||||
Move it in time with `data-start`. Put it on a different timeline row with
|
||||
`data-track-index`. See [data attributes](/concepts/data-attributes) for the rest.
|
||||
|
||||
{/* hf:generated-footer */}
|
||||
|
||||
Tagged `code` `code-animation` `shader` `webgl` `developer`.
|
||||
@@ -579,3 +603,7 @@ Tagged `code` `code-animation` `shader` `webgl` `developer`.
|
||||
- [Browse the complete Catalog](/catalog)
|
||||
- [Add assets and Catalog items in Studio](/studio/assets-and-blocks)
|
||||
- [Build a richer composition](/go-further)
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
</CatalogDetail>
|
||||
|
||||
@@ -0,0 +1,603 @@
|
||||
---
|
||||
title: "Code Slice Hero"
|
||||
description: "A tiled headline surface flips cell by cell under a sweeping depth field to reveal the rear headline."
|
||||
mode: "frame"
|
||||
---
|
||||
|
||||
import { CatalogDetail, CatalogSlot } from "/snippets/catalog-detail.jsx";
|
||||
import { InstallCommand } from "/snippets/install-command.jsx";
|
||||
|
||||
<CatalogDetail
|
||||
previewSrc="/public/catalog/blocks/code-slice-hero.json"
|
||||
compositionId="code-slice-hero"
|
||||
compositionSrc="compositions/code-slice-hero/code-slice-hero.html"
|
||||
title="Code Slice Hero"
|
||||
description="A tiled headline surface flips cell by cell under a sweeping depth field to reveal the rear headline."
|
||||
variables={[{"id":"headline","type":"string","label":"Copy - Front headline","default":"MAKE IT","maxLength":32},{"id":"reverseHeadline","type":"string","label":"Copy - Rear headline","default":"MATTER.","maxLength":32},{"id":"direction","type":"enum","label":"Motion - Sweep direction","default":"left-to-right","options":[{"value":"left-to-right","label":"Left to right"},{"value":"right-to-left","label":"Right to left"}]},{"id":"sweepDuration","type":"number","label":"Motion - Sweep duration","default":4.2,"min":2.6,"max":4.5,"step":0.05,"unit":"s"},{"id":"sweepEaseStrength","type":"number","label":"Motion - Sweep easing strength","default":3,"min":0,"max":3,"step":0.05,"description":"0 = linear. Higher values start faster and slow more strongly toward the end."},{"id":"flipDuration","type":"number","label":"Motion - Each tile flip","default":0.75,"min":0.65,"max":1.65,"step":0.05,"unit":"s"},{"id":"cursorRadius","type":"number","label":"Cursor - Influence radius","default":210,"min":160,"max":480,"step":10,"unit":"px"},{"id":"cursorFalloff","type":"number","label":"Cursor - Influence falloff","default":1.4,"min":0.4,"max":3.5,"step":0.05},{"id":"cursorDepth","type":"number","label":"Cursor - Depth (+ toward camera)","default":80,"min":-320,"max":320,"step":10,"unit":"px"},{"id":"tiltStrength","type":"number","label":"Cursor - Pull / tilt strength","default":55,"min":0,"max":55,"step":1,"unit":"deg"},{"id":"noiseStrength","type":"number","label":"Motion - Organic variation","default":2,"min":0,"max":2,"step":0.05},{"id":"flipBounce","type":"number","label":"Motion - Elastic flip bounce","default":0.15,"min":0,"max":0.8,"step":0.05},{"id":"cellSize","type":"number","label":"Slices - Square cell size","default":112,"min":24,"max":120,"step":4,"unit":"px"},{"id":"formationScale","type":"number","label":"Slices - Local formation scale","default":1,"min":0.94,"max":1,"step":0.001},{"id":"fontSize","type":"number","label":"Type - Maximum size","default":310,"min":160,"max":480,"step":5,"unit":"px"},{"id":"behindColor","type":"color","label":"Surface - Behind-tile background","default":"#212121"},{"id":"shadowStrength","type":"number","label":"Surface - Cast shadow strength","default":0.19,"min":0,"max":0.65,"step":0.01},{"id":"shadowSoftness","type":"number","label":"Surface - Cast shadow softness","default":4,"min":0.5,"max":4,"step":0.1,"unit":"px"}]}
|
||||
meta={{"duration":8,"width":1920,"height":1080,"category":"3D motion","badge":"Stable","codeLines":532}}
|
||||
attribution={{"path":"registry/blocks/code-slice-hero","tags":["3d-motion","transition","typography","tiles","flip","webgl2","shadows","two-headlines"]}}
|
||||
hasCode
|
||||
rawUrl="https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/code-slice-hero/code-slice-hero.html"
|
||||
>
|
||||
|
||||
<CatalogSlot slot="code">
|
||||
|
||||
```html code-slice-hero.html
|
||||
<!doctype html>
|
||||
<html
|
||||
lang="en"
|
||||
data-composition-variables='[{"id":"headline","type":"string","label":"Copy - Front headline","default":"MAKE IT","maxLength":32},{"id":"reverseHeadline","type":"string","label":"Copy - Rear headline","default":"MATTER.","maxLength":32},{"id":"direction","type":"enum","label":"Motion - Sweep direction","default":"left-to-right","options":[{"value":"left-to-right","label":"Left to right"},{"value":"right-to-left","label":"Right to left"}]},{"id":"sweepDuration","type":"number","label":"Motion - Sweep duration","default":4.2,"min":2.6,"max":4.5,"step":0.05,"unit":"s"},{"id":"sweepEaseStrength","type":"number","label":"Motion - Sweep easing strength","default":3,"min":0,"max":3,"step":0.05,"description":"0 = linear. Higher values start faster and slow more strongly toward the end."},{"id":"flipDuration","type":"number","label":"Motion - Each tile flip","default":0.75,"min":0.65,"max":1.65,"step":0.05,"unit":"s"},{"id":"cursorRadius","type":"number","label":"Cursor - Influence radius","default":210,"min":160,"max":480,"step":10,"unit":"px"},{"id":"cursorFalloff","type":"number","label":"Cursor - Influence falloff","default":1.4,"min":0.4,"max":3.5,"step":0.05},{"id":"cursorDepth","type":"number","label":"Cursor - Depth (+ toward camera)","default":80,"min":-320,"max":320,"step":10,"unit":"px"},{"id":"tiltStrength","type":"number","label":"Cursor - Pull / tilt strength","default":55,"min":0,"max":55,"step":1,"unit":"deg"},{"id":"noiseStrength","type":"number","label":"Motion - Organic variation","default":2,"min":0,"max":2,"step":0.05},{"id":"flipBounce","type":"number","label":"Motion - Elastic flip bounce","default":0.15,"min":0,"max":0.8,"step":0.05},{"id":"cellSize","type":"number","label":"Slices - Square cell size","default":112,"min":24,"max":120,"step":4,"unit":"px"},{"id":"formationScale","type":"number","label":"Slices - Local formation scale","default":1,"min":0.94,"max":1,"step":0.001},{"id":"fontSize","type":"number","label":"Type - Maximum size","default":310,"min":160,"max":480,"step":5,"unit":"px"},{"id":"behindColor","type":"color","label":"Surface - Behind-tile background","default":"#212121"},{"id":"shadowStrength","type":"number","label":"Surface - Cast shadow strength","default":0.19,"min":0,"max":0.65,"step":0.01},{"id":"shadowSoftness","type":"number","label":"Surface - Cast shadow softness","default":4,"min":0.5,"max":4,"step":0.1,"unit":"px"}]'
|
||||
>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=1920, height=1080" />
|
||||
<title>Code Slice Hero</title>
|
||||
<script src="assets/gsap-3.14.2.min.js"></script>
|
||||
<script src="shadows.js"></script>
|
||||
<script src="surface.js"></script>
|
||||
<style>
|
||||
@font-face {
|
||||
font-family: SliceGeist;
|
||||
src: url("./assets/Geist-Bold.ttf") format("truetype");
|
||||
font-weight: 700;
|
||||
font-display: block;
|
||||
}
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
html,
|
||||
body {
|
||||
background: #fff;
|
||||
}
|
||||
#csh-root {
|
||||
position: relative;
|
||||
width: 1920px;
|
||||
height: 1080px;
|
||||
overflow: hidden;
|
||||
background: #fff;
|
||||
}
|
||||
#csh-stage {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
}
|
||||
#csh-flat {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: #fff var(--csh-front) no-repeat 0 0 / 1920px 1080px;
|
||||
}
|
||||
.csh-accessible {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
clip-path: inset(50%);
|
||||
white-space: nowrap;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div
|
||||
id="csh-root"
|
||||
data-composition-id="code-slice-hero"
|
||||
data-start="0"
|
||||
data-duration="8"
|
||||
data-width="1920"
|
||||
data-height="1080"
|
||||
>
|
||||
<section
|
||||
id="csh-stage"
|
||||
class="clip"
|
||||
data-start="0"
|
||||
data-duration="8"
|
||||
data-track-index="1"
|
||||
aria-hidden="true"
|
||||
></section>
|
||||
<div id="csh-flat" aria-hidden="true"></div>
|
||||
<h1 id="csh-accessible" class="csh-accessible"></h1>
|
||||
</div>
|
||||
<script>
|
||||
(() => {
|
||||
"use strict";
|
||||
const W = 1920,
|
||||
H = 1080,
|
||||
DURATION = 8,
|
||||
SWEEP_START = 1.35;
|
||||
const schema = JSON.parse(
|
||||
document.documentElement.getAttribute("data-composition-variables"),
|
||||
);
|
||||
const defaults = Object.fromEntries(schema.map((v) => [v.id, v.default]));
|
||||
const values = { ...defaults, ...(window.__hyperframes?.getVariables?.() || {}) };
|
||||
const clamp = (v, a = 0, b = 1) => Math.max(a, Math.min(b, v));
|
||||
const number = (key, a, b) =>
|
||||
Number.isFinite(Number(values[key])) ? clamp(Number(values[key]), a, b) : defaults[key];
|
||||
const smooth = (v) => {
|
||||
const u = clamp(v);
|
||||
return u * u * u * (10 + u * (-15 + 6 * u));
|
||||
};
|
||||
const headline = String(values.headline ?? defaults.headline)
|
||||
.replace(/\s+/g, " ")
|
||||
.trim()
|
||||
.slice(0, 32);
|
||||
const reverseHeadline = String(values.reverseHeadline ?? defaults.reverseHeadline)
|
||||
.replace(/\s+/g, " ")
|
||||
.trim()
|
||||
.slice(0, 32);
|
||||
const direction = values.direction === "right-to-left" ? -1 : 1;
|
||||
const cellSize = Math.round(number("cellSize", 24, 120));
|
||||
const columns = Math.ceil(W / cellSize),
|
||||
rows = Math.ceil(H / cellSize);
|
||||
const gridX = Math.floor((W - columns * cellSize) / 2),
|
||||
gridY = Math.floor((H - rows * cellSize) / 2);
|
||||
const formationScale = number("formationScale", 0.94, 1);
|
||||
const sweepPower = 1 + number("sweepEaseStrength", 0, 3);
|
||||
const sweepDuration = number("sweepDuration", 2.6, 4.5),
|
||||
flipDuration = number("flipDuration", 0.65, 1.65);
|
||||
const cursorRadius = number("cursorRadius", 160, 480),
|
||||
tiltStrength = number("tiltStrength", 0, 55);
|
||||
const cursorDepth = number("cursorDepth", -320, 320);
|
||||
const cursorFalloff = number("cursorFalloff", 0.4, 3.5);
|
||||
const fieldReach = cursorRadius * Math.sqrt(-Math.log(0.06) / cursorFalloff);
|
||||
const noiseStrength = number("noiseStrength", 0, 2),
|
||||
flipBounce = number("flipBounce", 0, 0.8);
|
||||
const maxFont = number("fontSize", 160, 480);
|
||||
// Match the workbench color control: both #RGB and #RRGGBB are valid.
|
||||
const rawBehindColor = String(values.behindColor ?? "")
|
||||
.trim()
|
||||
.toLowerCase();
|
||||
const behindColor = /^#[0-9a-f]{3}$/.test(rawBehindColor)
|
||||
? "#" +
|
||||
rawBehindColor
|
||||
.slice(1)
|
||||
.split("")
|
||||
.map((c) => c + c)
|
||||
.join("")
|
||||
: /^#[0-9a-f]{6}$/.test(rawBehindColor)
|
||||
? rawBehindColor
|
||||
: "#ffffff";
|
||||
const shadowStrength = number("shadowStrength", 0, 0.65),
|
||||
shadowSoftness = number("shadowSoftness", 0.5, 4);
|
||||
const stage = document.getElementById("csh-stage"),
|
||||
root = document.getElementById("csh-root");
|
||||
const accessible = document.getElementById("csh-accessible"),
|
||||
flat = document.getElementById("csh-flat");
|
||||
const tiles = [],
|
||||
cells = [];
|
||||
const cursorLeft = -cursorRadius * 1.8,
|
||||
cursorSpan = W + cursorRadius * 3.6;
|
||||
let disposed = false,
|
||||
timeline,
|
||||
lastTime = 0,
|
||||
finishTime = 0;
|
||||
let firstMotion = SWEEP_START,
|
||||
lastMotion = DURATION,
|
||||
lastRenderedTime = NaN,
|
||||
frameMode;
|
||||
let surface;
|
||||
const renderStats = { evaluatedTiles: 0, drawCalls: 0, restTiles: 0 };
|
||||
|
||||
// Shared front and rear surfaces, as in Code Slice Hero's UV windows.
|
||||
// Raster alpha selects flip eligibility only, never a moving mask.
|
||||
function textSurface(text) {
|
||||
const canvas = document.createElement("canvas");
|
||||
canvas.width = W;
|
||||
canvas.height = H;
|
||||
const ctx = canvas.getContext("2d", { willReadFrequently: true });
|
||||
let size = maxFont;
|
||||
ctx.font = `700 ${size}px SliceGeist`;
|
||||
const measured = ctx.measureText(text);
|
||||
if (measured.width > W - 320) size *= (W - 320) / measured.width;
|
||||
ctx.font = `700 ${size}px SliceGeist`;
|
||||
ctx.textAlign = "center";
|
||||
ctx.textBaseline = "alphabetic";
|
||||
const box = ctx.measureText(text);
|
||||
const baseline = H / 2 + (box.actualBoundingBoxAscent - box.actualBoundingBoxDescent) / 2;
|
||||
ctx.fillStyle = "#101010";
|
||||
ctx.fillText(text, W / 2, baseline);
|
||||
const ink = ctx.getImageData(0, 0, W, H).data;
|
||||
let inkPixels = 0;
|
||||
for (let i = 3; i < ink.length; i += 4) if (ink[i] > 0) inkPixels++;
|
||||
ctx.globalCompositeOperation = "destination-over";
|
||||
ctx.fillStyle = "#fff";
|
||||
ctx.fillRect(0, 0, W, H);
|
||||
return { canvas, ink, inkPixels, url: canvas.toDataURL("image/png"), size };
|
||||
}
|
||||
|
||||
function countInk(ink, x0, y0, x1, y1) {
|
||||
let count = 0;
|
||||
for (let y = Math.max(0, y0); y < Math.min(H, y1); y++)
|
||||
for (let x = Math.max(0, x0); x < Math.min(W, x1); x++) {
|
||||
if (ink[(y * W + x) * 4 + 3] > 0) count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
// These independent hashes are the experiment's actual per-purpose seeds.
|
||||
function hash(col, row, a, b) {
|
||||
const v = Math.sin(col * a + row * b) * 43758.5453;
|
||||
return v - Math.floor(v);
|
||||
}
|
||||
|
||||
// Same Gaussian^falloff + smooth cutoff as the source hover (falloff 1.4).
|
||||
// Distances are in composition pixels, so the field stays circular even
|
||||
// when square cell size / grid density changes.
|
||||
function fieldAt(x, y, cursorX, cursorY) {
|
||||
const distanceSquared = (x - cursorX) ** 2 + (y - cursorY) ** 2;
|
||||
const raw = Math.exp((-cursorFalloff * distanceSquared) / (cursorRadius * cursorRadius));
|
||||
const u = clamp((raw - 0.06) / 0.94);
|
||||
return u * u * (3 - 2 * u);
|
||||
}
|
||||
|
||||
// One analytic ease-out and its inverse drive every passage/window time.
|
||||
// Strength zero preserves linear travel exactly; strength one is quadratic.
|
||||
const easeSweep = (u) =>
|
||||
sweepPower === 1 ? clamp(u) : 1 - Math.pow(1 - clamp(u), sweepPower);
|
||||
const inverseSweep = (u) =>
|
||||
sweepPower === 1 ? clamp(u) : 1 - Math.pow(1 - clamp(u), 1 / sweepPower);
|
||||
function cursorTimeAtX(x) {
|
||||
const rank = (direction === 1 ? x - cursorLeft : W - x - cursorLeft) / cursorSpan;
|
||||
return SWEEP_START + sweepDuration * inverseSweep(rank);
|
||||
}
|
||||
function cursorAt(time) {
|
||||
const progress = easeSweep((time - SWEEP_START) / sweepDuration);
|
||||
return {
|
||||
x: cursorLeft + cursorSpan * (direction === 1 ? progress : 1 - progress),
|
||||
y: H / 2,
|
||||
z: cursorDepth,
|
||||
};
|
||||
}
|
||||
|
||||
function poseAt(tile, time) {
|
||||
const t = clamp(time, 0, DURATION);
|
||||
const progress = tile.active ? clamp((t - tile.flipStart) / tile.flipDuration) : 0;
|
||||
const turn = smooth(progress / 0.64);
|
||||
// Analytic damped oscillation: overshoot, return past 180, settle exactly.
|
||||
// The attack/tail both have zero slope, so bounce=0 is a clean half-turn.
|
||||
const ringT = clamp((progress - 0.64) / 0.36);
|
||||
const ringEnvelope =
|
||||
smooth(ringT / 0.12) * Math.exp(-3.3 * ringT) * (1 - smooth((ringT - 0.72) / 0.28));
|
||||
const bounce = flipBounce * 36 * Math.sin(ringT * Math.PI * 3.3) * ringEnvelope;
|
||||
const flight = Math.sin(Math.PI * progress) ** 0.7;
|
||||
const cursor = cursorAt(t);
|
||||
const radial = fieldAt(tile.cx, tile.cy, cursor.x, cursor.y);
|
||||
const automatic = t >= SWEEP_START && t <= SWEEP_START + sweepDuration;
|
||||
// A short approach replaces the long, separate anticipation phase.
|
||||
const approach = smooth((t - tile.passTime + 0.2) / 0.16);
|
||||
const pull =
|
||||
(automatic ? radial * approach * (1 - smooth(progress / 0.32)) : 0) *
|
||||
(tile.active ? 1 : 0.55);
|
||||
const dx = cursor.x - tile.cx,
|
||||
dy = cursor.y - tile.cy;
|
||||
const variation = noiseStrength * Math.max(pull, flight * tile.peak);
|
||||
// Continuous deterministic oscillations, not random-at-render-time jitter.
|
||||
const n1 = Math.sin(t * 5.7 + tile.phase),
|
||||
n2 = Math.sin(t * 10.3 + tile.phase * 1.71);
|
||||
const jitter = variation * (n1 * 0.7 + n2 * 0.3);
|
||||
const formation = Math.max(pull, flight * (0.55 + 0.45 * tile.peak));
|
||||
// CSS positive Z points toward the camera. The prior 2D tilt signs
|
||||
// depressed the cursor-facing edge despite lifting the tile center.
|
||||
// A signed depth field now raises that edge for a positive cursor Z;
|
||||
// moving the cursor through the plane smoothly reverses this torque.
|
||||
const depthGain = cursor.z / 160;
|
||||
const pullTiltX =
|
||||
depthGain *
|
||||
((dy / cursorRadius) * tiltStrength * 1.6 * pull +
|
||||
tile.seedX * tiltStrength * 0.16 * pull);
|
||||
// Ink yaw cannot reverse against its half-turn after cursor passage.
|
||||
// Release its seeded torque over 80 ms; blank tiles retain signed pull.
|
||||
const yawDx = tile.active ? direction * Math.max(0, -direction * dx) : -dx;
|
||||
const yawHandoff = tile.active ? 1 - smooth((t - tile.passTime + 0.04) / 0.12) : 1;
|
||||
const pullTiltY =
|
||||
depthGain *
|
||||
((yawDx / cursorRadius) * tiltStrength * 1.8 * pull +
|
||||
tile.seedY * tiltStrength * 0.12 * pull) *
|
||||
yawHandoff;
|
||||
const pullZ = (tiltStrength / 30) * cursor.z * 0.45 * pull;
|
||||
const rx = pullTiltX + tile.seedX * variation * 3 * flight + jitter;
|
||||
const ry = direction * (180 * turn + bounce) + pullTiltY + jitter * 0.7;
|
||||
const rz = tile.seedZ * variation * (2.3 * flight + 1.2 * pull) + jitter * 0.35;
|
||||
return {
|
||||
x: clamp(dx * 0.08 * pull, -tile.width * 0.18, tile.width * 0.18) + jitter * 0.2,
|
||||
y: clamp(dy * 0.07 * pull, -tile.height * 0.11, tile.height * 0.11) + jitter * 0.16,
|
||||
z: pullZ + 17 * flight * tile.peak,
|
||||
cursorDepth: cursor.z,
|
||||
pullTiltX,
|
||||
pullTiltY,
|
||||
pullZ,
|
||||
rx,
|
||||
ry,
|
||||
rz,
|
||||
turn,
|
||||
bounce,
|
||||
radial,
|
||||
pull,
|
||||
peak: tile.peak,
|
||||
formation,
|
||||
scale: 1 - (1 - formationScale) * formation,
|
||||
shade: 1 - 0.12 * Math.abs(Math.sin((ry * Math.PI) / 180)) - 0.018 * formation,
|
||||
};
|
||||
}
|
||||
|
||||
function setRest(tile, rear) {
|
||||
const rest = tile.active && rear ? 1 : 0;
|
||||
if (tile.restState === rest) return;
|
||||
tile.restState = rest;
|
||||
surface.set(tile.index, {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0,
|
||||
rx: 0,
|
||||
ry: rest * direction * 180,
|
||||
rz: 0,
|
||||
scale: 1,
|
||||
formation: 0,
|
||||
shade: 1,
|
||||
});
|
||||
}
|
||||
|
||||
function renderAt(time) {
|
||||
if (disposed) return;
|
||||
lastTime = clamp(time, 0, DURATION);
|
||||
if (lastTime === lastRenderedTime) return;
|
||||
lastRenderedTime = lastTime;
|
||||
renderStats.evaluatedTiles = 0;
|
||||
renderStats.drawCalls = 0;
|
||||
renderStats.restTiles = 0;
|
||||
const mode =
|
||||
lastTime <= firstMotion ? "front" : lastTime >= lastMotion ? "rear" : "moving";
|
||||
if (mode !== frameMode) {
|
||||
frameMode = mode;
|
||||
stage.style.visibility = mode === "moving" ? "visible" : "hidden";
|
||||
flat.style.visibility = mode === "moving" ? "hidden" : "visible";
|
||||
if (mode !== "moving")
|
||||
flat.style.backgroundImage = `var(--csh-${mode === "front" ? "front" : "back"})`;
|
||||
}
|
||||
// One unsegmented atlas is shown only while EVERY tile is flat. This
|
||||
// removes compositor hairlines in the workbench's scaled first frame.
|
||||
// It is hidden for the whole active wave: no backing text or mask fade.
|
||||
for (const tile of tiles) {
|
||||
if (mode !== "moving" || lastTime < tile.motionStart || lastTime > tile.motionEnd) {
|
||||
setRest(tile, mode === "rear" || lastTime > tile.flipStart + tile.flipDuration);
|
||||
renderStats.restTiles++;
|
||||
continue;
|
||||
}
|
||||
tile.restState = null;
|
||||
surface.set(tile.index, poseAt(tile, lastTime));
|
||||
renderStats.evaluatedTiles++;
|
||||
}
|
||||
if (mode === "moving") renderStats.drawCalls = surface.draw();
|
||||
const copy =
|
||||
lastTime < SWEEP_START + sweepDuration / 2 + flipDuration / 2
|
||||
? headline
|
||||
: reverseHeadline;
|
||||
if (accessible.textContent !== copy) accessible.textContent = copy;
|
||||
stage.dataset.time = lastTime.toFixed(4);
|
||||
}
|
||||
|
||||
function build() {
|
||||
if (disposed) return;
|
||||
const front = textSurface(headline),
|
||||
back = textSurface(reverseHeadline);
|
||||
root.style.setProperty("--csh-front", `url("${front.url}")`);
|
||||
root.style.setProperty("--csh-back", `url("${back.url}")`);
|
||||
let coveredFront = 0,
|
||||
coveredBack = 0;
|
||||
firstMotion = Infinity;
|
||||
lastMotion = 0;
|
||||
for (let row = 0; row < rows; row++)
|
||||
for (let col = 0; col < columns; col++) {
|
||||
// One exhaustive integer pixel partition. No alpha threshold, corner
|
||||
// sampling, skipped row, or per-letter box can punch holes in the type.
|
||||
const x = gridX + col * cellSize,
|
||||
x1 = x + cellSize;
|
||||
const y = gridY + row * cellSize,
|
||||
y1 = y + cellSize;
|
||||
const frontInk = countInk(front.ink, x, y, x1, y1),
|
||||
backInk = countInk(back.ink, x, y, x1, y1);
|
||||
coveredFront += frontInk;
|
||||
coveredBack += backInk;
|
||||
const cell = {
|
||||
col,
|
||||
row,
|
||||
x,
|
||||
y,
|
||||
width: x1 - x,
|
||||
height: y1 - y,
|
||||
frontInk,
|
||||
backInk,
|
||||
active: frontInk + backInk > 0,
|
||||
};
|
||||
cells.push(cell);
|
||||
// Build the whole square surface. White cells participate in radial
|
||||
// lift/tilt at a restrained strength, but progress is locked to zero
|
||||
// above so they never enter the headline's half-turn or bounce.
|
||||
const cx = x + cell.width / 2,
|
||||
cy = y + cell.height / 2;
|
||||
const peak = fieldAt(cx, cy, cx, H / 2);
|
||||
const passTime = cursorTimeAtX(cx);
|
||||
const columnSeconds = Math.abs(
|
||||
cursorTimeAtX(cx + cellSize / 2) - cursorTimeAtX(cx - cellSize / 2),
|
||||
);
|
||||
// The turn starts at passage, with at most 12 ms of seeded stagger.
|
||||
const flipStart =
|
||||
passTime +
|
||||
hash(col, row, 269.5, 183.3) *
|
||||
Math.min(0.012, columnSeconds * 0.08) *
|
||||
Math.min(noiseStrength, 1);
|
||||
const duration = flipDuration * (1 + 0.18 * (1 - peak));
|
||||
const tile = {
|
||||
...cell,
|
||||
index: tiles.length,
|
||||
cx,
|
||||
cy,
|
||||
peak,
|
||||
passTime,
|
||||
flipStart,
|
||||
flipDuration: duration,
|
||||
phase: hash(col, row, 127.1, 311.7) * Math.PI * 2,
|
||||
seedX: hash(col, row, 419.2, 371.9) * 2 - 1,
|
||||
seedY: hash(col, row, 37.7, 631.5) * 2 - 1,
|
||||
seedZ: hash(col, row, 97.3, 127.9) * 2 - 1,
|
||||
};
|
||||
// The Gaussian is exactly zero outside this circle. Precompute each
|
||||
// cell's entry/exit times once, independent of playback history.
|
||||
const dy = cy - H / 2;
|
||||
let enter = Infinity,
|
||||
exit = -Infinity;
|
||||
if (Math.abs(dy) < fieldReach) {
|
||||
const dx = Math.sqrt(fieldReach * fieldReach - dy * dy);
|
||||
enter = Math.max(
|
||||
SWEEP_START,
|
||||
passTime - 0.2,
|
||||
cursorTimeAtX(direction === 1 ? cx - dx : cx + dx),
|
||||
);
|
||||
exit = Math.min(
|
||||
SWEEP_START + sweepDuration,
|
||||
cursorTimeAtX(direction === 1 ? cx + dx : cx - dx),
|
||||
);
|
||||
}
|
||||
tile.motionStart = Math.min(enter, tile.active ? flipStart : Infinity);
|
||||
tile.motionEnd = Math.max(exit, tile.active ? flipStart + duration : -Infinity);
|
||||
firstMotion = Math.min(firstMotion, tile.motionStart);
|
||||
lastMotion = Math.max(lastMotion, tile.motionEnd);
|
||||
if (tile.active) finishTime = Math.max(finishTime, flipStart + duration);
|
||||
tiles.push(tile);
|
||||
}
|
||||
const inkTiles = tiles.filter((tile) => tile.active);
|
||||
root.style.backgroundColor = behindColor;
|
||||
surface = window.CodeSliceSurface(stage, front.canvas, back.canvas, tiles, {
|
||||
behindColor,
|
||||
shadowStrength,
|
||||
shadowSoftness,
|
||||
});
|
||||
const state = { time: 0 };
|
||||
timeline = gsap.timeline({ paused: true });
|
||||
timeline.to(
|
||||
state,
|
||||
{
|
||||
time: DURATION,
|
||||
duration: DURATION,
|
||||
ease: "none",
|
||||
onUpdate: () => renderAt(state.time),
|
||||
},
|
||||
0,
|
||||
);
|
||||
timeline
|
||||
.addLabel("seamless-front", 0)
|
||||
.addLabel("cursor-sweep", SWEEP_START)
|
||||
.addLabel("cursor-passed", SWEEP_START + sweepDuration)
|
||||
.addLabel("seamless-rear", finishTime);
|
||||
window.__timelines = window.__timelines || {};
|
||||
window.__timelines["code-slice-hero"] = timeline;
|
||||
window.__codeSliceHero = {
|
||||
duration: DURATION,
|
||||
cellSize,
|
||||
columns,
|
||||
rows,
|
||||
activeCount: tiles.length,
|
||||
flipCount: inkTiles.length,
|
||||
backgroundCount: tiles.length - inkTiles.length,
|
||||
copy: [headline, reverseHeadline],
|
||||
fontSizes: [front.size, back.size],
|
||||
finishTime,
|
||||
coverage: {
|
||||
front: { inkPixels: front.inkPixels, covered: coveredFront },
|
||||
back: { inkPixels: back.inkPixels, covered: coveredBack },
|
||||
},
|
||||
cells,
|
||||
cursorAt,
|
||||
cursorTimeAtX,
|
||||
sweepEaseStrength: sweepPower - 1,
|
||||
renderStats,
|
||||
shadowStats: surface.shadowStats,
|
||||
behindColor,
|
||||
renderer: "webgl2-instanced",
|
||||
sample: (time) =>
|
||||
tiles.map((tile) => ({
|
||||
col: tile.col,
|
||||
row: tile.row,
|
||||
flips: tile.active,
|
||||
passTime: tile.passTime,
|
||||
flipStart: tile.flipStart,
|
||||
flipDuration: tile.flipDuration,
|
||||
...poseAt(tile, time),
|
||||
})),
|
||||
renderAt,
|
||||
};
|
||||
renderAt(0);
|
||||
}
|
||||
|
||||
// The cursor is imaginary and belongs exclusively to the timeline.
|
||||
// No mouse/pointer input or wall-clock accumulation can affect a frame.
|
||||
function seek(event) {
|
||||
if (!timeline || typeof event.detail?.time !== "number") return;
|
||||
const time = clamp(event.detail.time, 0, DURATION);
|
||||
timeline.totalTime(time, true);
|
||||
renderAt(time);
|
||||
}
|
||||
window.addEventListener("hf-seek", seek);
|
||||
window.__codeSliceInstance = {
|
||||
dispose() {
|
||||
disposed = true;
|
||||
timeline?.kill();
|
||||
surface?.dispose();
|
||||
window.removeEventListener("hf-seek", seek);
|
||||
stage.replaceChildren();
|
||||
tiles.length = 0;
|
||||
cells.length = 0;
|
||||
root.style.removeProperty("--csh-front");
|
||||
root.style.removeProperty("--csh-back");
|
||||
},
|
||||
};
|
||||
window.__codeSliceReady = document.fonts.load("700 420px SliceGeist").then(build);
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
<CatalogSlot slot="install">
|
||||
|
||||
<InstallCommand command="npx hyperframes add code-slice-hero" item="code-slice-hero" />
|
||||
|
||||
That writes `compositions/code-slice-hero/code-slice-hero.html`, plus 7 supporting files under `compositions/code-slice-hero/assets/` and `compositions/code-slice-hero/`.
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
<CatalogSlot slot="docs">
|
||||
|
||||
## Add it to your video
|
||||
|
||||
It runs for 8 seconds at 1920×1080. Paste this into your composition:
|
||||
|
||||
```html index.html
|
||||
<div
|
||||
data-composition-id="code-slice-hero"
|
||||
data-composition-src="compositions/code-slice-hero/code-slice-hero.html"
|
||||
data-start="0"
|
||||
data-duration="8"
|
||||
data-track-index="1"
|
||||
data-width="1920"
|
||||
data-height="1080"
|
||||
></div>
|
||||
```
|
||||
|
||||
Move it in time with `data-start`. Put it on a different timeline row with
|
||||
`data-track-index`. See [data attributes](/concepts/data-attributes) for the rest.
|
||||
|
||||
{/* hf:generated-footer */}
|
||||
|
||||
Tagged `3d-motion` `transition` `typography` `tiles` `flip` `webgl2` `shadows` `two-headlines`.
|
||||
|
||||
## Related topics
|
||||
|
||||
- [Browse the complete Catalog](/catalog)
|
||||
- [Add assets and Catalog items in Studio](/studio/assets-and-blocks)
|
||||
- [Build a richer composition](/go-further)
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
</CatalogDetail>
|
||||
@@ -1,28 +1,28 @@
|
||||
---
|
||||
title: "Code Snippet - Apple Terminal Basic"
|
||||
description: "Apple Terminal Basic profile: white background, black text, per-character typing animation of a shell session."
|
||||
mode: "frame"
|
||||
---
|
||||
|
||||
import { CatalogDetail, CatalogSlot } from "/snippets/catalog-detail.jsx";
|
||||
import { InstallCommand } from "/snippets/install-command.jsx";
|
||||
|
||||
## Install
|
||||
<CatalogDetail
|
||||
previewSrc="/public/catalog/blocks/code-snippet-apple-terminal-basic.json"
|
||||
compositionId="code-snippet-apple-terminal-basic"
|
||||
compositionSrc="compositions/code-snippet-apple-terminal-basic.html"
|
||||
title="Code Snippet - Apple Terminal Basic"
|
||||
description="Apple Terminal Basic profile: white background, black text, per-character typing animation of a shell session."
|
||||
variables={[]}
|
||||
meta={{"duration":12,"width":1920,"height":1080,"category":"Code Snippets","badge":"Stable","codeLines":249}}
|
||||
attribution={{"path":"registry/blocks/code-snippet-apple-terminal-basic","tags":["code","developer","showcase","terminal","apple","apple-terminal"]}}
|
||||
hasCode
|
||||
rawUrl="https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/code-snippet-apple-terminal-basic/code-snippet-apple-terminal-basic.html"
|
||||
>
|
||||
|
||||
<InstallCommand command="npx hyperframes add code-snippet-apple-terminal-basic" item="code-snippet-apple-terminal-basic" />
|
||||
<CatalogSlot slot="code">
|
||||
|
||||
That writes one file: `compositions/code-snippet-apple-terminal-basic.html`.
|
||||
|
||||
<iframe
|
||||
className="w-full aspect-video rounded-xl border-0 bg-zinc-100 dark:bg-zinc-800"
|
||||
title="code-snippet-apple-terminal-basic preview"
|
||||
loading="lazy"
|
||||
srcDoc={`<!doctype html><html><head><meta charset="utf-8"><style>html,body{margin:0;height:100%;overflow:hidden;background:transparent}hyperframes-player{display:block;width:100%;height:100%}</style><script src="https://cdn.jsdelivr.net/npm/@hyperframes/player@latest/dist/hyperframes-player.global.js"><\/script></head><body><script>fetch("/public/catalog/blocks/code-snippet-apple-terminal-basic.json").then(function(r){return r.json()}).then(function(d){var p=document.createElement("hyperframes-player");p.setAttribute("srcdoc",d.html);p.setAttribute("controls","");p.setAttribute("autoplay","");p.setAttribute("loop","");p.setAttribute("muted","");p.setAttribute("poster","https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/code-snippet-apple-terminal-basic.png");document.body.appendChild(p)});<\/script></body></html>`}
|
||||
/>
|
||||
|
||||
## Source
|
||||
|
||||
<Accordion title="code-snippet-apple-terminal-basic.html">
|
||||
|
||||
```html
|
||||
```html code-snippet-apple-terminal-basic.html
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
@@ -274,13 +274,23 @@ That writes one file: `compositions/code-snippet-apple-terminal-basic.html`.
|
||||
</html>
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
</CatalogSlot>
|
||||
|
||||
## Usage
|
||||
<CatalogSlot slot="install">
|
||||
|
||||
After installing, add the block to your host composition:
|
||||
<InstallCommand command="npx hyperframes add code-snippet-apple-terminal-basic" item="code-snippet-apple-terminal-basic" />
|
||||
|
||||
```html
|
||||
That writes one file: `compositions/code-snippet-apple-terminal-basic.html`.
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
<CatalogSlot slot="docs">
|
||||
|
||||
## Add it to your video
|
||||
|
||||
It runs for 12 seconds at 1920×1080. Paste this into your composition:
|
||||
|
||||
```html index.html
|
||||
<div
|
||||
data-composition-id="code-snippet-apple-terminal-basic"
|
||||
data-composition-src="compositions/code-snippet-apple-terminal-basic.html"
|
||||
@@ -292,14 +302,8 @@ After installing, add the block to your host composition:
|
||||
></div>
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
- Faithful recreation of macOS Terminal.app Basic profile — white background, black text, solid cursor
|
||||
- macOS window chrome with traffic light buttons (close, minimize, fullscreen) and centered title bar
|
||||
- Per-character GSAP typing animation — command types out character by character
|
||||
- Output lines reveal sequentially after command executes
|
||||
- Cursor blinks three times at the end then holds steady
|
||||
- Self-contained HTML — no external assets, only GSAP CDN
|
||||
Move it in time with `data-start`. Put it on a different timeline row with
|
||||
`data-track-index`. See [data attributes](/concepts/data-attributes) for the rest.
|
||||
|
||||
{/* hf:generated-footer */}
|
||||
|
||||
@@ -310,3 +314,7 @@ Tagged `code` `developer` `showcase` `terminal` `apple` `apple-terminal`.
|
||||
- [Browse the complete Catalog](/catalog)
|
||||
- [Add assets and Catalog items in Studio](/studio/assets-and-blocks)
|
||||
- [Build a richer composition](/go-further)
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
</CatalogDetail>
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
---
|
||||
title: "Code Snippet - Apple Terminal Clear Dark"
|
||||
description: "Apple Terminal Clear Dark profile: semi-transparent dark background, per-character typing animation of a shell session."
|
||||
mode: "frame"
|
||||
---
|
||||
|
||||
import { CatalogDetail, CatalogSlot } from "/snippets/catalog-detail.jsx";
|
||||
import { InstallCommand } from "/snippets/install-command.jsx";
|
||||
|
||||
## Install
|
||||
<CatalogDetail
|
||||
previewSrc="/public/catalog/blocks/code-snippet-apple-terminal-clear-dark.json"
|
||||
compositionId="code-snippet-apple-terminal-clear-dark"
|
||||
compositionSrc="compositions/code-snippet-apple-terminal-clear-dark.html"
|
||||
title="Code Snippet - Apple Terminal Clear Dark"
|
||||
description="Apple Terminal Clear Dark profile: semi-transparent dark background, per-character typing animation of a shell session."
|
||||
variables={[]}
|
||||
meta={{"duration":12,"width":1920,"height":1080,"category":"Code Snippets","badge":"Stable","codeLines":244}}
|
||||
attribution={{"path":"registry/blocks/code-snippet-apple-terminal-clear-dark","tags":["code","developer","showcase","terminal","apple","apple-terminal"]}}
|
||||
hasCode
|
||||
rawUrl="https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/code-snippet-apple-terminal-clear-dark/code-snippet-apple-terminal-clear-dark.html"
|
||||
>
|
||||
|
||||
<InstallCommand command="npx hyperframes add code-snippet-apple-terminal-clear-dark" item="code-snippet-apple-terminal-clear-dark" />
|
||||
<CatalogSlot slot="code">
|
||||
|
||||
That writes one file: `compositions/code-snippet-apple-terminal-clear-dark.html`.
|
||||
|
||||
<iframe
|
||||
className="w-full aspect-video rounded-xl border-0 bg-zinc-100 dark:bg-zinc-800"
|
||||
title="code-snippet-apple-terminal-clear-dark preview"
|
||||
loading="lazy"
|
||||
srcDoc={`<!doctype html><html><head><meta charset="utf-8"><style>html,body{margin:0;height:100%;overflow:hidden;background:transparent}hyperframes-player{display:block;width:100%;height:100%}</style><script src="https://cdn.jsdelivr.net/npm/@hyperframes/player@latest/dist/hyperframes-player.global.js"><\/script></head><body><script>fetch("/public/catalog/blocks/code-snippet-apple-terminal-clear-dark.json").then(function(r){return r.json()}).then(function(d){var p=document.createElement("hyperframes-player");p.setAttribute("srcdoc",d.html);p.setAttribute("controls","");p.setAttribute("autoplay","");p.setAttribute("loop","");p.setAttribute("muted","");p.setAttribute("poster","https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/code-snippet-apple-terminal-clear-dark.png");document.body.appendChild(p)});<\/script></body></html>`}
|
||||
/>
|
||||
|
||||
## Source
|
||||
|
||||
<Accordion title="code-snippet-apple-terminal-clear-dark.html">
|
||||
|
||||
```html
|
||||
```html code-snippet-apple-terminal-clear-dark.html
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
@@ -269,13 +269,23 @@ That writes one file: `compositions/code-snippet-apple-terminal-clear-dark.html`
|
||||
</html>
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
</CatalogSlot>
|
||||
|
||||
## Usage
|
||||
<CatalogSlot slot="install">
|
||||
|
||||
After installing, add the block to your host composition:
|
||||
<InstallCommand command="npx hyperframes add code-snippet-apple-terminal-clear-dark" item="code-snippet-apple-terminal-clear-dark" />
|
||||
|
||||
```html
|
||||
That writes one file: `compositions/code-snippet-apple-terminal-clear-dark.html`.
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
<CatalogSlot slot="docs">
|
||||
|
||||
## Add it to your video
|
||||
|
||||
It runs for 12 seconds at 1920×1080. Paste this into your composition:
|
||||
|
||||
```html index.html
|
||||
<div
|
||||
data-composition-id="code-snippet-apple-terminal-clear-dark"
|
||||
data-composition-src="compositions/code-snippet-apple-terminal-clear-dark.html"
|
||||
@@ -287,14 +297,8 @@ After installing, add the block to your host composition:
|
||||
></div>
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
- Faithful recreation of macOS Terminal.app Clear Dark profile — semi-transparent black background with white text
|
||||
- macOS window chrome with traffic light buttons and centered title bar
|
||||
- Per-character GSAP typing animation — command types out character by character
|
||||
- Output lines reveal sequentially after command executes
|
||||
- Cursor blinks three times at the end then holds steady
|
||||
- Self-contained HTML — no external assets, only GSAP CDN
|
||||
Move it in time with `data-start`. Put it on a different timeline row with
|
||||
`data-track-index`. See [data attributes](/concepts/data-attributes) for the rest.
|
||||
|
||||
{/* hf:generated-footer */}
|
||||
|
||||
@@ -305,3 +309,7 @@ Tagged `code` `developer` `showcase` `terminal` `apple` `apple-terminal`.
|
||||
- [Browse the complete Catalog](/catalog)
|
||||
- [Add assets and Catalog items in Studio](/studio/assets-and-blocks)
|
||||
- [Build a richer composition](/go-further)
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
</CatalogDetail>
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
---
|
||||
title: "Code Snippet - Apple Terminal Clear Light"
|
||||
description: "Apple Terminal Clear Light profile: semi-transparent light background, per-character typing animation of a shell session."
|
||||
mode: "frame"
|
||||
---
|
||||
|
||||
import { CatalogDetail, CatalogSlot } from "/snippets/catalog-detail.jsx";
|
||||
import { InstallCommand } from "/snippets/install-command.jsx";
|
||||
|
||||
## Install
|
||||
<CatalogDetail
|
||||
previewSrc="/public/catalog/blocks/code-snippet-apple-terminal-clear-light.json"
|
||||
compositionId="code-snippet-apple-terminal-clear-light"
|
||||
compositionSrc="compositions/code-snippet-apple-terminal-clear-light.html"
|
||||
title="Code Snippet - Apple Terminal Clear Light"
|
||||
description="Apple Terminal Clear Light profile: semi-transparent light background, per-character typing animation of a shell session."
|
||||
variables={[]}
|
||||
meta={{"duration":12,"width":1920,"height":1080,"category":"Code Snippets","badge":"Stable","codeLines":244}}
|
||||
attribution={{"path":"registry/blocks/code-snippet-apple-terminal-clear-light","tags":["code","developer","showcase","terminal","apple","apple-terminal"]}}
|
||||
hasCode
|
||||
rawUrl="https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/code-snippet-apple-terminal-clear-light/code-snippet-apple-terminal-clear-light.html"
|
||||
>
|
||||
|
||||
<InstallCommand command="npx hyperframes add code-snippet-apple-terminal-clear-light" item="code-snippet-apple-terminal-clear-light" />
|
||||
<CatalogSlot slot="code">
|
||||
|
||||
That writes one file: `compositions/code-snippet-apple-terminal-clear-light.html`.
|
||||
|
||||
<iframe
|
||||
className="w-full aspect-video rounded-xl border-0 bg-zinc-100 dark:bg-zinc-800"
|
||||
title="code-snippet-apple-terminal-clear-light preview"
|
||||
loading="lazy"
|
||||
srcDoc={`<!doctype html><html><head><meta charset="utf-8"><style>html,body{margin:0;height:100%;overflow:hidden;background:transparent}hyperframes-player{display:block;width:100%;height:100%}</style><script src="https://cdn.jsdelivr.net/npm/@hyperframes/player@latest/dist/hyperframes-player.global.js"><\/script></head><body><script>fetch("/public/catalog/blocks/code-snippet-apple-terminal-clear-light.json").then(function(r){return r.json()}).then(function(d){var p=document.createElement("hyperframes-player");p.setAttribute("srcdoc",d.html);p.setAttribute("controls","");p.setAttribute("autoplay","");p.setAttribute("loop","");p.setAttribute("muted","");p.setAttribute("poster","https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/code-snippet-apple-terminal-clear-light.png");document.body.appendChild(p)});<\/script></body></html>`}
|
||||
/>
|
||||
|
||||
## Source
|
||||
|
||||
<Accordion title="code-snippet-apple-terminal-clear-light.html">
|
||||
|
||||
```html
|
||||
```html code-snippet-apple-terminal-clear-light.html
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
@@ -269,13 +269,23 @@ That writes one file: `compositions/code-snippet-apple-terminal-clear-light.html
|
||||
</html>
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
</CatalogSlot>
|
||||
|
||||
## Usage
|
||||
<CatalogSlot slot="install">
|
||||
|
||||
After installing, add the block to your host composition:
|
||||
<InstallCommand command="npx hyperframes add code-snippet-apple-terminal-clear-light" item="code-snippet-apple-terminal-clear-light" />
|
||||
|
||||
```html
|
||||
That writes one file: `compositions/code-snippet-apple-terminal-clear-light.html`.
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
<CatalogSlot slot="docs">
|
||||
|
||||
## Add it to your video
|
||||
|
||||
It runs for 12 seconds at 1920×1080. Paste this into your composition:
|
||||
|
||||
```html index.html
|
||||
<div
|
||||
data-composition-id="code-snippet-apple-terminal-clear-light"
|
||||
data-composition-src="compositions/code-snippet-apple-terminal-clear-light.html"
|
||||
@@ -287,14 +297,8 @@ After installing, add the block to your host composition:
|
||||
></div>
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
- Faithful recreation of macOS Terminal.app Clear Light profile — semi-transparent white background with black text
|
||||
- macOS window chrome with traffic light buttons and centered title bar
|
||||
- Per-character GSAP typing animation — command types out character by character
|
||||
- Output lines reveal sequentially after command executes
|
||||
- Cursor blinks three times at the end then holds steady
|
||||
- Self-contained HTML — no external assets, only GSAP CDN
|
||||
Move it in time with `data-start`. Put it on a different timeline row with
|
||||
`data-track-index`. See [data attributes](/concepts/data-attributes) for the rest.
|
||||
|
||||
{/* hf:generated-footer */}
|
||||
|
||||
@@ -305,3 +309,7 @@ Tagged `code` `developer` `showcase` `terminal` `apple` `apple-terminal`.
|
||||
- [Browse the complete Catalog](/catalog)
|
||||
- [Add assets and Catalog items in Studio](/studio/assets-and-blocks)
|
||||
- [Build a richer composition](/go-further)
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
</CatalogDetail>
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
---
|
||||
title: "Code Snippet - Apple Terminal Grass"
|
||||
description: "Apple Terminal Grass profile: black background with green text, per-character typing animation of a shell session."
|
||||
mode: "frame"
|
||||
---
|
||||
|
||||
import { CatalogDetail, CatalogSlot } from "/snippets/catalog-detail.jsx";
|
||||
import { InstallCommand } from "/snippets/install-command.jsx";
|
||||
|
||||
## Install
|
||||
<CatalogDetail
|
||||
previewSrc="/public/catalog/blocks/code-snippet-apple-terminal-grass.json"
|
||||
compositionId="code-snippet-apple-terminal-grass"
|
||||
compositionSrc="compositions/code-snippet-apple-terminal-grass.html"
|
||||
title="Code Snippet - Apple Terminal Grass"
|
||||
description="Apple Terminal Grass profile: black background with green text, per-character typing animation of a shell session."
|
||||
variables={[]}
|
||||
meta={{"duration":12,"width":1920,"height":1080,"category":"Code Snippets","badge":"Stable","codeLines":243}}
|
||||
attribution={{"path":"registry/blocks/code-snippet-apple-terminal-grass","tags":["code","developer","showcase","terminal","apple","apple-terminal"]}}
|
||||
hasCode
|
||||
rawUrl="https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/code-snippet-apple-terminal-grass/code-snippet-apple-terminal-grass.html"
|
||||
>
|
||||
|
||||
<InstallCommand command="npx hyperframes add code-snippet-apple-terminal-grass" item="code-snippet-apple-terminal-grass" />
|
||||
<CatalogSlot slot="code">
|
||||
|
||||
That writes one file: `compositions/code-snippet-apple-terminal-grass.html`.
|
||||
|
||||
<iframe
|
||||
className="w-full aspect-video rounded-xl border-0 bg-zinc-100 dark:bg-zinc-800"
|
||||
title="code-snippet-apple-terminal-grass preview"
|
||||
loading="lazy"
|
||||
srcDoc={`<!doctype html><html><head><meta charset="utf-8"><style>html,body{margin:0;height:100%;overflow:hidden;background:transparent}hyperframes-player{display:block;width:100%;height:100%}</style><script src="https://cdn.jsdelivr.net/npm/@hyperframes/player@latest/dist/hyperframes-player.global.js"><\/script></head><body><script>fetch("/public/catalog/blocks/code-snippet-apple-terminal-grass.json").then(function(r){return r.json()}).then(function(d){var p=document.createElement("hyperframes-player");p.setAttribute("srcdoc",d.html);p.setAttribute("controls","");p.setAttribute("autoplay","");p.setAttribute("loop","");p.setAttribute("muted","");p.setAttribute("poster","https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/code-snippet-apple-terminal-grass.png");document.body.appendChild(p)});<\/script></body></html>`}
|
||||
/>
|
||||
|
||||
## Source
|
||||
|
||||
<Accordion title="code-snippet-apple-terminal-grass.html">
|
||||
|
||||
```html
|
||||
```html code-snippet-apple-terminal-grass.html
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
@@ -268,13 +268,23 @@ That writes one file: `compositions/code-snippet-apple-terminal-grass.html`.
|
||||
</html>
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
</CatalogSlot>
|
||||
|
||||
## Usage
|
||||
<CatalogSlot slot="install">
|
||||
|
||||
After installing, add the block to your host composition:
|
||||
<InstallCommand command="npx hyperframes add code-snippet-apple-terminal-grass" item="code-snippet-apple-terminal-grass" />
|
||||
|
||||
```html
|
||||
That writes one file: `compositions/code-snippet-apple-terminal-grass.html`.
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
<CatalogSlot slot="docs">
|
||||
|
||||
## Add it to your video
|
||||
|
||||
It runs for 12 seconds at 1920×1080. Paste this into your composition:
|
||||
|
||||
```html index.html
|
||||
<div
|
||||
data-composition-id="code-snippet-apple-terminal-grass"
|
||||
data-composition-src="compositions/code-snippet-apple-terminal-grass.html"
|
||||
@@ -286,14 +296,8 @@ After installing, add the block to your host composition:
|
||||
></div>
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
- Faithful recreation of macOS Terminal.app Grass profile — black background with #00C100 green text
|
||||
- macOS window chrome with green-accented title bar and traffic light buttons
|
||||
- Per-character GSAP typing animation — command types out character by character
|
||||
- Output lines reveal sequentially after command executes
|
||||
- Cursor blinks three times at the end then holds steady
|
||||
- Self-contained HTML — no external assets, only GSAP CDN
|
||||
Move it in time with `data-start`. Put it on a different timeline row with
|
||||
`data-track-index`. See [data attributes](/concepts/data-attributes) for the rest.
|
||||
|
||||
{/* hf:generated-footer */}
|
||||
|
||||
@@ -304,3 +308,7 @@ Tagged `code` `developer` `showcase` `terminal` `apple` `apple-terminal`.
|
||||
- [Browse the complete Catalog](/catalog)
|
||||
- [Add assets and Catalog items in Studio](/studio/assets-and-blocks)
|
||||
- [Build a richer composition](/go-further)
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
</CatalogDetail>
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
---
|
||||
title: "Code Snippet - Apple Terminal Homebrew"
|
||||
description: "Apple Terminal Homebrew profile: black background with bright green text and lime cursor, per-character typing animation of a shell session."
|
||||
mode: "frame"
|
||||
---
|
||||
|
||||
import { CatalogDetail, CatalogSlot } from "/snippets/catalog-detail.jsx";
|
||||
import { InstallCommand } from "/snippets/install-command.jsx";
|
||||
|
||||
## Install
|
||||
<CatalogDetail
|
||||
previewSrc="/public/catalog/blocks/code-snippet-apple-terminal-homebrew.json"
|
||||
compositionId="code-snippet-apple-terminal-homebrew"
|
||||
compositionSrc="compositions/code-snippet-apple-terminal-homebrew.html"
|
||||
title="Code Snippet - Apple Terminal Homebrew"
|
||||
description="Apple Terminal Homebrew profile: black background with bright green text and lime cursor, per-character typing animation of a shell session."
|
||||
variables={[]}
|
||||
meta={{"duration":12,"width":1920,"height":1080,"category":"Code Snippets","badge":"Stable","codeLines":248}}
|
||||
attribution={{"path":"registry/blocks/code-snippet-apple-terminal-homebrew","tags":["code","developer","showcase","terminal","apple","apple-terminal"]}}
|
||||
hasCode
|
||||
rawUrl="https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/code-snippet-apple-terminal-homebrew/code-snippet-apple-terminal-homebrew.html"
|
||||
>
|
||||
|
||||
<InstallCommand command="npx hyperframes add code-snippet-apple-terminal-homebrew" item="code-snippet-apple-terminal-homebrew" />
|
||||
<CatalogSlot slot="code">
|
||||
|
||||
That writes one file: `compositions/code-snippet-apple-terminal-homebrew.html`.
|
||||
|
||||
<iframe
|
||||
className="w-full aspect-video rounded-xl border-0 bg-zinc-100 dark:bg-zinc-800"
|
||||
title="code-snippet-apple-terminal-homebrew preview"
|
||||
loading="lazy"
|
||||
srcDoc={`<!doctype html><html><head><meta charset="utf-8"><style>html,body{margin:0;height:100%;overflow:hidden;background:transparent}hyperframes-player{display:block;width:100%;height:100%}</style><script src="https://cdn.jsdelivr.net/npm/@hyperframes/player@latest/dist/hyperframes-player.global.js"><\/script></head><body><script>fetch("/public/catalog/blocks/code-snippet-apple-terminal-homebrew.json").then(function(r){return r.json()}).then(function(d){var p=document.createElement("hyperframes-player");p.setAttribute("srcdoc",d.html);p.setAttribute("controls","");p.setAttribute("autoplay","");p.setAttribute("loop","");p.setAttribute("muted","");p.setAttribute("poster","https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/code-snippet-apple-terminal-homebrew.png");document.body.appendChild(p)});<\/script></body></html>`}
|
||||
/>
|
||||
|
||||
## Source
|
||||
|
||||
<Accordion title="code-snippet-apple-terminal-homebrew.html">
|
||||
|
||||
```html
|
||||
```html code-snippet-apple-terminal-homebrew.html
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
@@ -273,13 +273,23 @@ That writes one file: `compositions/code-snippet-apple-terminal-homebrew.html`.
|
||||
</html>
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
</CatalogSlot>
|
||||
|
||||
## Usage
|
||||
<CatalogSlot slot="install">
|
||||
|
||||
After installing, add the block to your host composition:
|
||||
<InstallCommand command="npx hyperframes add code-snippet-apple-terminal-homebrew" item="code-snippet-apple-terminal-homebrew" />
|
||||
|
||||
```html
|
||||
That writes one file: `compositions/code-snippet-apple-terminal-homebrew.html`.
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
<CatalogSlot slot="docs">
|
||||
|
||||
## Add it to your video
|
||||
|
||||
It runs for 12 seconds at 1920×1080. Paste this into your composition:
|
||||
|
||||
```html index.html
|
||||
<div
|
||||
data-composition-id="code-snippet-apple-terminal-homebrew"
|
||||
data-composition-src="compositions/code-snippet-apple-terminal-homebrew.html"
|
||||
@@ -291,14 +301,8 @@ After installing, add the block to your host composition:
|
||||
></div>
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
- Faithful recreation of macOS Terminal.app Homebrew profile — black background with #00CF00 text and #48F800 cursor
|
||||
- macOS window chrome with traffic light buttons and centered title bar
|
||||
- Per-character GSAP typing animation — command types out character by character
|
||||
- Output lines reveal sequentially after command executes
|
||||
- Cursor blinks three times at the end then holds steady
|
||||
- Self-contained HTML — no external assets, only GSAP CDN
|
||||
Move it in time with `data-start`. Put it on a different timeline row with
|
||||
`data-track-index`. See [data attributes](/concepts/data-attributes) for the rest.
|
||||
|
||||
{/* hf:generated-footer */}
|
||||
|
||||
@@ -309,3 +313,7 @@ Tagged `code` `developer` `showcase` `terminal` `apple` `apple-terminal`.
|
||||
- [Browse the complete Catalog](/catalog)
|
||||
- [Add assets and Catalog items in Studio](/studio/assets-and-blocks)
|
||||
- [Build a richer composition](/go-further)
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
</CatalogDetail>
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
---
|
||||
title: "Code Snippet - Apple Terminal Man Page"
|
||||
description: "Apple Terminal Man Page profile: pale yellow background with black text, per-character typing animation of a shell session."
|
||||
mode: "frame"
|
||||
---
|
||||
|
||||
import { CatalogDetail, CatalogSlot } from "/snippets/catalog-detail.jsx";
|
||||
import { InstallCommand } from "/snippets/install-command.jsx";
|
||||
|
||||
## Install
|
||||
<CatalogDetail
|
||||
previewSrc="/public/catalog/blocks/code-snippet-apple-terminal-man-page.json"
|
||||
compositionId="code-snippet-apple-terminal-man-page"
|
||||
compositionSrc="compositions/code-snippet-apple-terminal-man-page.html"
|
||||
title="Code Snippet - Apple Terminal Man Page"
|
||||
description="Apple Terminal Man Page profile: pale yellow background with black text, per-character typing animation of a shell session."
|
||||
variables={[]}
|
||||
meta={{"duration":12,"width":1920,"height":1080,"category":"Code Snippets","badge":"Stable","codeLines":248}}
|
||||
attribution={{"path":"registry/blocks/code-snippet-apple-terminal-man-page","tags":["code","developer","showcase","terminal","apple","apple-terminal"]}}
|
||||
hasCode
|
||||
rawUrl="https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/code-snippet-apple-terminal-man-page/code-snippet-apple-terminal-man-page.html"
|
||||
>
|
||||
|
||||
<InstallCommand command="npx hyperframes add code-snippet-apple-terminal-man-page" item="code-snippet-apple-terminal-man-page" />
|
||||
<CatalogSlot slot="code">
|
||||
|
||||
That writes one file: `compositions/code-snippet-apple-terminal-man-page.html`.
|
||||
|
||||
<iframe
|
||||
className="w-full aspect-video rounded-xl border-0 bg-zinc-100 dark:bg-zinc-800"
|
||||
title="code-snippet-apple-terminal-man-page preview"
|
||||
loading="lazy"
|
||||
srcDoc={`<!doctype html><html><head><meta charset="utf-8"><style>html,body{margin:0;height:100%;overflow:hidden;background:transparent}hyperframes-player{display:block;width:100%;height:100%}</style><script src="https://cdn.jsdelivr.net/npm/@hyperframes/player@latest/dist/hyperframes-player.global.js"><\/script></head><body><script>fetch("/public/catalog/blocks/code-snippet-apple-terminal-man-page.json").then(function(r){return r.json()}).then(function(d){var p=document.createElement("hyperframes-player");p.setAttribute("srcdoc",d.html);p.setAttribute("controls","");p.setAttribute("autoplay","");p.setAttribute("loop","");p.setAttribute("muted","");p.setAttribute("poster","https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/code-snippet-apple-terminal-man-page.png");document.body.appendChild(p)});<\/script></body></html>`}
|
||||
/>
|
||||
|
||||
## Source
|
||||
|
||||
<Accordion title="code-snippet-apple-terminal-man-page.html">
|
||||
|
||||
```html
|
||||
```html code-snippet-apple-terminal-man-page.html
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
@@ -273,13 +273,23 @@ That writes one file: `compositions/code-snippet-apple-terminal-man-page.html`.
|
||||
</html>
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
</CatalogSlot>
|
||||
|
||||
## Usage
|
||||
<CatalogSlot slot="install">
|
||||
|
||||
After installing, add the block to your host composition:
|
||||
<InstallCommand command="npx hyperframes add code-snippet-apple-terminal-man-page" item="code-snippet-apple-terminal-man-page" />
|
||||
|
||||
```html
|
||||
That writes one file: `compositions/code-snippet-apple-terminal-man-page.html`.
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
<CatalogSlot slot="docs">
|
||||
|
||||
## Add it to your video
|
||||
|
||||
It runs for 12 seconds at 1920×1080. Paste this into your composition:
|
||||
|
||||
```html index.html
|
||||
<div
|
||||
data-composition-id="code-snippet-apple-terminal-man-page"
|
||||
data-composition-src="compositions/code-snippet-apple-terminal-man-page.html"
|
||||
@@ -291,14 +301,8 @@ After installing, add the block to your host composition:
|
||||
></div>
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
- Faithful recreation of macOS Terminal.app Man Page profile — pale yellow background with black text
|
||||
- Shows a realistic man page output structure with sections and indented content
|
||||
- macOS window chrome with traffic light buttons and centered title bar
|
||||
- Per-character GSAP typing animation — command types out character by character
|
||||
- Output lines reveal sequentially after command executes
|
||||
- Self-contained HTML — no external assets, only GSAP CDN
|
||||
Move it in time with `data-start`. Put it on a different timeline row with
|
||||
`data-track-index`. See [data attributes](/concepts/data-attributes) for the rest.
|
||||
|
||||
{/* hf:generated-footer */}
|
||||
|
||||
@@ -309,3 +313,7 @@ Tagged `code` `developer` `showcase` `terminal` `apple` `apple-terminal`.
|
||||
- [Browse the complete Catalog](/catalog)
|
||||
- [Add assets and Catalog items in Studio](/studio/assets-and-blocks)
|
||||
- [Build a richer composition](/go-further)
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
</CatalogDetail>
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
---
|
||||
title: "Code Snippet - Apple Terminal Novel"
|
||||
description: "Apple Terminal Novel profile: warm parchment background with dark brown text, per-character typing animation of a shell session."
|
||||
mode: "frame"
|
||||
---
|
||||
|
||||
import { CatalogDetail, CatalogSlot } from "/snippets/catalog-detail.jsx";
|
||||
import { InstallCommand } from "/snippets/install-command.jsx";
|
||||
|
||||
## Install
|
||||
<CatalogDetail
|
||||
previewSrc="/public/catalog/blocks/code-snippet-apple-terminal-novel.json"
|
||||
compositionId="code-snippet-apple-terminal-novel"
|
||||
compositionSrc="compositions/code-snippet-apple-terminal-novel.html"
|
||||
title="Code Snippet - Apple Terminal Novel"
|
||||
description="Apple Terminal Novel profile: warm parchment background with dark brown text, per-character typing animation of a shell session."
|
||||
variables={[]}
|
||||
meta={{"duration":12,"width":1920,"height":1080,"category":"Code Snippets","badge":"Stable","codeLines":244}}
|
||||
attribution={{"path":"registry/blocks/code-snippet-apple-terminal-novel","tags":["code","developer","showcase","terminal","apple","apple-terminal"]}}
|
||||
hasCode
|
||||
rawUrl="https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/code-snippet-apple-terminal-novel/code-snippet-apple-terminal-novel.html"
|
||||
>
|
||||
|
||||
<InstallCommand command="npx hyperframes add code-snippet-apple-terminal-novel" item="code-snippet-apple-terminal-novel" />
|
||||
<CatalogSlot slot="code">
|
||||
|
||||
That writes one file: `compositions/code-snippet-apple-terminal-novel.html`.
|
||||
|
||||
<iframe
|
||||
className="w-full aspect-video rounded-xl border-0 bg-zinc-100 dark:bg-zinc-800"
|
||||
title="code-snippet-apple-terminal-novel preview"
|
||||
loading="lazy"
|
||||
srcDoc={`<!doctype html><html><head><meta charset="utf-8"><style>html,body{margin:0;height:100%;overflow:hidden;background:transparent}hyperframes-player{display:block;width:100%;height:100%}</style><script src="https://cdn.jsdelivr.net/npm/@hyperframes/player@latest/dist/hyperframes-player.global.js"><\/script></head><body><script>fetch("/public/catalog/blocks/code-snippet-apple-terminal-novel.json").then(function(r){return r.json()}).then(function(d){var p=document.createElement("hyperframes-player");p.setAttribute("srcdoc",d.html);p.setAttribute("controls","");p.setAttribute("autoplay","");p.setAttribute("loop","");p.setAttribute("muted","");p.setAttribute("poster","https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/code-snippet-apple-terminal-novel.png");document.body.appendChild(p)});<\/script></body></html>`}
|
||||
/>
|
||||
|
||||
## Source
|
||||
|
||||
<Accordion title="code-snippet-apple-terminal-novel.html">
|
||||
|
||||
```html
|
||||
```html code-snippet-apple-terminal-novel.html
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
@@ -269,13 +269,23 @@ That writes one file: `compositions/code-snippet-apple-terminal-novel.html`.
|
||||
</html>
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
</CatalogSlot>
|
||||
|
||||
## Usage
|
||||
<CatalogSlot slot="install">
|
||||
|
||||
After installing, add the block to your host composition:
|
||||
<InstallCommand command="npx hyperframes add code-snippet-apple-terminal-novel" item="code-snippet-apple-terminal-novel" />
|
||||
|
||||
```html
|
||||
That writes one file: `compositions/code-snippet-apple-terminal-novel.html`.
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
<CatalogSlot slot="docs">
|
||||
|
||||
## Add it to your video
|
||||
|
||||
It runs for 12 seconds at 1920×1080. Paste this into your composition:
|
||||
|
||||
```html index.html
|
||||
<div
|
||||
data-composition-id="code-snippet-apple-terminal-novel"
|
||||
data-composition-src="compositions/code-snippet-apple-terminal-novel.html"
|
||||
@@ -287,14 +297,8 @@ After installing, add the block to your host composition:
|
||||
></div>
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
- Faithful recreation of macOS Terminal.app Novel profile — warm parchment background with #3E2900 dark brown text
|
||||
- macOS window chrome with traffic light buttons and centered title bar
|
||||
- Per-character GSAP typing animation — command types out character by character
|
||||
- Output lines reveal sequentially after command executes
|
||||
- Cursor blinks three times at the end then holds steady
|
||||
- Self-contained HTML — no external assets, only GSAP CDN
|
||||
Move it in time with `data-start`. Put it on a different timeline row with
|
||||
`data-track-index`. See [data attributes](/concepts/data-attributes) for the rest.
|
||||
|
||||
{/* hf:generated-footer */}
|
||||
|
||||
@@ -305,3 +309,7 @@ Tagged `code` `developer` `showcase` `terminal` `apple` `apple-terminal`.
|
||||
- [Browse the complete Catalog](/catalog)
|
||||
- [Add assets and Catalog items in Studio](/studio/assets-and-blocks)
|
||||
- [Build a richer composition](/go-further)
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
</CatalogDetail>
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
---
|
||||
title: "Code Snippet - Apple Terminal Ocean"
|
||||
description: "Apple Terminal Ocean profile: deep blue background with white text, per-character typing animation of a shell session."
|
||||
mode: "frame"
|
||||
---
|
||||
|
||||
import { CatalogDetail, CatalogSlot } from "/snippets/catalog-detail.jsx";
|
||||
import { InstallCommand } from "/snippets/install-command.jsx";
|
||||
|
||||
## Install
|
||||
<CatalogDetail
|
||||
previewSrc="/public/catalog/blocks/code-snippet-apple-terminal-ocean.json"
|
||||
compositionId="code-snippet-apple-terminal-ocean"
|
||||
compositionSrc="compositions/code-snippet-apple-terminal-ocean.html"
|
||||
title="Code Snippet - Apple Terminal Ocean"
|
||||
description="Apple Terminal Ocean profile: deep blue background with white text, per-character typing animation of a shell session."
|
||||
variables={[]}
|
||||
meta={{"duration":12,"width":1920,"height":1080,"category":"Code Snippets","badge":"Stable","codeLines":253}}
|
||||
attribution={{"path":"registry/blocks/code-snippet-apple-terminal-ocean","tags":["code","developer","showcase","terminal","apple","apple-terminal"]}}
|
||||
hasCode
|
||||
rawUrl="https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/code-snippet-apple-terminal-ocean/code-snippet-apple-terminal-ocean.html"
|
||||
>
|
||||
|
||||
<InstallCommand command="npx hyperframes add code-snippet-apple-terminal-ocean" item="code-snippet-apple-terminal-ocean" />
|
||||
<CatalogSlot slot="code">
|
||||
|
||||
That writes one file: `compositions/code-snippet-apple-terminal-ocean.html`.
|
||||
|
||||
<iframe
|
||||
className="w-full aspect-video rounded-xl border-0 bg-zinc-100 dark:bg-zinc-800"
|
||||
title="code-snippet-apple-terminal-ocean preview"
|
||||
loading="lazy"
|
||||
srcDoc={`<!doctype html><html><head><meta charset="utf-8"><style>html,body{margin:0;height:100%;overflow:hidden;background:transparent}hyperframes-player{display:block;width:100%;height:100%}</style><script src="https://cdn.jsdelivr.net/npm/@hyperframes/player@latest/dist/hyperframes-player.global.js"><\/script></head><body><script>fetch("/public/catalog/blocks/code-snippet-apple-terminal-ocean.json").then(function(r){return r.json()}).then(function(d){var p=document.createElement("hyperframes-player");p.setAttribute("srcdoc",d.html);p.setAttribute("controls","");p.setAttribute("autoplay","");p.setAttribute("loop","");p.setAttribute("muted","");p.setAttribute("poster","https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/code-snippet-apple-terminal-ocean.png");document.body.appendChild(p)});<\/script></body></html>`}
|
||||
/>
|
||||
|
||||
## Source
|
||||
|
||||
<Accordion title="code-snippet-apple-terminal-ocean.html">
|
||||
|
||||
```html
|
||||
```html code-snippet-apple-terminal-ocean.html
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
@@ -278,13 +278,23 @@ That writes one file: `compositions/code-snippet-apple-terminal-ocean.html`.
|
||||
</html>
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
</CatalogSlot>
|
||||
|
||||
## Usage
|
||||
<CatalogSlot slot="install">
|
||||
|
||||
After installing, add the block to your host composition:
|
||||
<InstallCommand command="npx hyperframes add code-snippet-apple-terminal-ocean" item="code-snippet-apple-terminal-ocean" />
|
||||
|
||||
```html
|
||||
That writes one file: `compositions/code-snippet-apple-terminal-ocean.html`.
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
<CatalogSlot slot="docs">
|
||||
|
||||
## Add it to your video
|
||||
|
||||
It runs for 12 seconds at 1920×1080. Paste this into your composition:
|
||||
|
||||
```html index.html
|
||||
<div
|
||||
data-composition-id="code-snippet-apple-terminal-ocean"
|
||||
data-composition-src="compositions/code-snippet-apple-terminal-ocean.html"
|
||||
@@ -296,14 +306,8 @@ After installing, add the block to your host composition:
|
||||
></div>
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
- Faithful recreation of macOS Terminal.app Ocean profile — #224FBE blue background with white text
|
||||
- macOS window chrome with traffic light buttons and centered title bar
|
||||
- Per-character GSAP typing animation — command types out character by character
|
||||
- Output lines reveal sequentially after command executes
|
||||
- Cursor blinks three times at the end then holds steady
|
||||
- Self-contained HTML — no external assets, only GSAP CDN
|
||||
Move it in time with `data-start`. Put it on a different timeline row with
|
||||
`data-track-index`. See [data attributes](/concepts/data-attributes) for the rest.
|
||||
|
||||
{/* hf:generated-footer */}
|
||||
|
||||
@@ -314,3 +318,7 @@ Tagged `code` `developer` `showcase` `terminal` `apple` `apple-terminal`.
|
||||
- [Browse the complete Catalog](/catalog)
|
||||
- [Add assets and Catalog items in Studio](/studio/assets-and-blocks)
|
||||
- [Build a richer composition](/go-further)
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
</CatalogDetail>
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
---
|
||||
title: "Code Snippet - Apple Terminal Pro"
|
||||
description: "Apple Terminal Pro profile: black background with grey text and lime green cursor, per-character typing animation of a shell session."
|
||||
mode: "frame"
|
||||
---
|
||||
|
||||
import { CatalogDetail, CatalogSlot } from "/snippets/catalog-detail.jsx";
|
||||
import { InstallCommand } from "/snippets/install-command.jsx";
|
||||
|
||||
## Install
|
||||
<CatalogDetail
|
||||
previewSrc="/public/catalog/blocks/code-snippet-apple-terminal-pro.json"
|
||||
compositionId="code-snippet-apple-terminal-pro"
|
||||
compositionSrc="compositions/code-snippet-apple-terminal-pro.html"
|
||||
title="Code Snippet - Apple Terminal Pro"
|
||||
description="Apple Terminal Pro profile: black background with grey text and lime green cursor, per-character typing animation of a shell session."
|
||||
variables={[]}
|
||||
meta={{"duration":12,"width":1920,"height":1080,"category":"Code Snippets","badge":"Stable","codeLines":247}}
|
||||
attribution={{"path":"registry/blocks/code-snippet-apple-terminal-pro","tags":["code","developer","showcase","terminal","apple","apple-terminal"]}}
|
||||
hasCode
|
||||
rawUrl="https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/code-snippet-apple-terminal-pro/code-snippet-apple-terminal-pro.html"
|
||||
>
|
||||
|
||||
<InstallCommand command="npx hyperframes add code-snippet-apple-terminal-pro" item="code-snippet-apple-terminal-pro" />
|
||||
<CatalogSlot slot="code">
|
||||
|
||||
That writes one file: `compositions/code-snippet-apple-terminal-pro.html`.
|
||||
|
||||
<iframe
|
||||
className="w-full aspect-video rounded-xl border-0 bg-zinc-100 dark:bg-zinc-800"
|
||||
title="code-snippet-apple-terminal-pro preview"
|
||||
loading="lazy"
|
||||
srcDoc={`<!doctype html><html><head><meta charset="utf-8"><style>html,body{margin:0;height:100%;overflow:hidden;background:transparent}hyperframes-player{display:block;width:100%;height:100%}</style><script src="https://cdn.jsdelivr.net/npm/@hyperframes/player@latest/dist/hyperframes-player.global.js"><\/script></head><body><script>fetch("/public/catalog/blocks/code-snippet-apple-terminal-pro.json").then(function(r){return r.json()}).then(function(d){var p=document.createElement("hyperframes-player");p.setAttribute("srcdoc",d.html);p.setAttribute("controls","");p.setAttribute("autoplay","");p.setAttribute("loop","");p.setAttribute("muted","");p.setAttribute("poster","https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/code-snippet-apple-terminal-pro.png");document.body.appendChild(p)});<\/script></body></html>`}
|
||||
/>
|
||||
|
||||
## Source
|
||||
|
||||
<Accordion title="code-snippet-apple-terminal-pro.html">
|
||||
|
||||
```html
|
||||
```html code-snippet-apple-terminal-pro.html
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
@@ -272,13 +272,23 @@ That writes one file: `compositions/code-snippet-apple-terminal-pro.html`.
|
||||
</html>
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
</CatalogSlot>
|
||||
|
||||
## Usage
|
||||
<CatalogSlot slot="install">
|
||||
|
||||
After installing, add the block to your host composition:
|
||||
<InstallCommand command="npx hyperframes add code-snippet-apple-terminal-pro" item="code-snippet-apple-terminal-pro" />
|
||||
|
||||
```html
|
||||
That writes one file: `compositions/code-snippet-apple-terminal-pro.html`.
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
<CatalogSlot slot="docs">
|
||||
|
||||
## Add it to your video
|
||||
|
||||
It runs for 12 seconds at 1920×1080. Paste this into your composition:
|
||||
|
||||
```html index.html
|
||||
<div
|
||||
data-composition-id="code-snippet-apple-terminal-pro"
|
||||
data-composition-src="compositions/code-snippet-apple-terminal-pro.html"
|
||||
@@ -290,14 +300,8 @@ After installing, add the block to your host composition:
|
||||
></div>
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
- Faithful recreation of macOS Terminal.app Pro profile — black background, #BBBBBB grey text, #88FF67 lime cursor
|
||||
- macOS window chrome with traffic light buttons and centered title bar
|
||||
- Per-character GSAP typing animation — command types out character by character
|
||||
- Output lines reveal sequentially after command executes
|
||||
- Cursor blinks three times at the end then holds steady
|
||||
- Self-contained HTML — no external assets, only GSAP CDN
|
||||
Move it in time with `data-start`. Put it on a different timeline row with
|
||||
`data-track-index`. See [data attributes](/concepts/data-attributes) for the rest.
|
||||
|
||||
{/* hf:generated-footer */}
|
||||
|
||||
@@ -308,3 +312,7 @@ Tagged `code` `developer` `showcase` `terminal` `apple` `apple-terminal`.
|
||||
- [Browse the complete Catalog](/catalog)
|
||||
- [Add assets and Catalog items in Studio](/studio/assets-and-blocks)
|
||||
- [Build a richer composition](/go-further)
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
</CatalogDetail>
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
---
|
||||
title: "Code Snippet - Apple Terminal Red Sands"
|
||||
description: "Apple Terminal Red Sands profile: deep red background with sandy text, per-character typing animation of a shell session."
|
||||
mode: "frame"
|
||||
---
|
||||
|
||||
import { CatalogDetail, CatalogSlot } from "/snippets/catalog-detail.jsx";
|
||||
import { InstallCommand } from "/snippets/install-command.jsx";
|
||||
|
||||
## Install
|
||||
<CatalogDetail
|
||||
previewSrc="/public/catalog/blocks/code-snippet-apple-terminal-red-sands.json"
|
||||
compositionId="code-snippet-apple-terminal-red-sands"
|
||||
compositionSrc="compositions/code-snippet-apple-terminal-red-sands.html"
|
||||
title="Code Snippet - Apple Terminal Red Sands"
|
||||
description="Apple Terminal Red Sands profile: deep red background with sandy text, per-character typing animation of a shell session."
|
||||
variables={[]}
|
||||
meta={{"duration":12,"width":1920,"height":1080,"category":"Code Snippets","badge":"Stable","codeLines":244}}
|
||||
attribution={{"path":"registry/blocks/code-snippet-apple-terminal-red-sands","tags":["code","developer","showcase","terminal","apple","apple-terminal"]}}
|
||||
hasCode
|
||||
rawUrl="https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/code-snippet-apple-terminal-red-sands/code-snippet-apple-terminal-red-sands.html"
|
||||
>
|
||||
|
||||
<InstallCommand command="npx hyperframes add code-snippet-apple-terminal-red-sands" item="code-snippet-apple-terminal-red-sands" />
|
||||
<CatalogSlot slot="code">
|
||||
|
||||
That writes one file: `compositions/code-snippet-apple-terminal-red-sands.html`.
|
||||
|
||||
<iframe
|
||||
className="w-full aspect-video rounded-xl border-0 bg-zinc-100 dark:bg-zinc-800"
|
||||
title="code-snippet-apple-terminal-red-sands preview"
|
||||
loading="lazy"
|
||||
srcDoc={`<!doctype html><html><head><meta charset="utf-8"><style>html,body{margin:0;height:100%;overflow:hidden;background:transparent}hyperframes-player{display:block;width:100%;height:100%}</style><script src="https://cdn.jsdelivr.net/npm/@hyperframes/player@latest/dist/hyperframes-player.global.js"><\/script></head><body><script>fetch("/public/catalog/blocks/code-snippet-apple-terminal-red-sands.json").then(function(r){return r.json()}).then(function(d){var p=document.createElement("hyperframes-player");p.setAttribute("srcdoc",d.html);p.setAttribute("controls","");p.setAttribute("autoplay","");p.setAttribute("loop","");p.setAttribute("muted","");p.setAttribute("poster","https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/code-snippet-apple-terminal-red-sands.png");document.body.appendChild(p)});<\/script></body></html>`}
|
||||
/>
|
||||
|
||||
## Source
|
||||
|
||||
<Accordion title="code-snippet-apple-terminal-red-sands.html">
|
||||
|
||||
```html
|
||||
```html code-snippet-apple-terminal-red-sands.html
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
@@ -269,13 +269,23 @@ That writes one file: `compositions/code-snippet-apple-terminal-red-sands.html`.
|
||||
</html>
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
</CatalogSlot>
|
||||
|
||||
## Usage
|
||||
<CatalogSlot slot="install">
|
||||
|
||||
After installing, add the block to your host composition:
|
||||
<InstallCommand command="npx hyperframes add code-snippet-apple-terminal-red-sands" item="code-snippet-apple-terminal-red-sands" />
|
||||
|
||||
```html
|
||||
That writes one file: `compositions/code-snippet-apple-terminal-red-sands.html`.
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
<CatalogSlot slot="docs">
|
||||
|
||||
## Add it to your video
|
||||
|
||||
It runs for 12 seconds at 1920×1080. Paste this into your composition:
|
||||
|
||||
```html index.html
|
||||
<div
|
||||
data-composition-id="code-snippet-apple-terminal-red-sands"
|
||||
data-composition-src="compositions/code-snippet-apple-terminal-red-sands.html"
|
||||
@@ -287,14 +297,8 @@ After installing, add the block to your host composition:
|
||||
></div>
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
- Faithful recreation of macOS Terminal.app Red Sands profile — #7B140E red background with #FFE1B9 sandy text
|
||||
- macOS window chrome with traffic light buttons and centered title bar
|
||||
- Per-character GSAP typing animation — command types out character by character
|
||||
- Output lines reveal sequentially after command executes
|
||||
- Cursor blinks three times at the end then holds steady
|
||||
- Self-contained HTML — no external assets, only GSAP CDN
|
||||
Move it in time with `data-start`. Put it on a different timeline row with
|
||||
`data-track-index`. See [data attributes](/concepts/data-attributes) for the rest.
|
||||
|
||||
{/* hf:generated-footer */}
|
||||
|
||||
@@ -305,3 +309,7 @@ Tagged `code` `developer` `showcase` `terminal` `apple` `apple-terminal`.
|
||||
- [Browse the complete Catalog](/catalog)
|
||||
- [Add assets and Catalog items in Studio](/studio/assets-and-blocks)
|
||||
- [Build a richer composition](/go-further)
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
</CatalogDetail>
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
---
|
||||
title: "Code Snippet - Apple Terminal Silver Aerogel"
|
||||
description: "Apple Terminal Silver Aerogel profile: dark grey background with white text, per-character typing animation of a shell session."
|
||||
mode: "frame"
|
||||
---
|
||||
|
||||
import { CatalogDetail, CatalogSlot } from "/snippets/catalog-detail.jsx";
|
||||
import { InstallCommand } from "/snippets/install-command.jsx";
|
||||
|
||||
## Install
|
||||
<CatalogDetail
|
||||
previewSrc="/public/catalog/blocks/code-snippet-apple-terminal-silver-aerogel.json"
|
||||
compositionId="code-snippet-apple-terminal-silver-aerogel"
|
||||
compositionSrc="compositions/code-snippet-apple-terminal-silver-aerogel.html"
|
||||
title="Code Snippet - Apple Terminal Silver Aerogel"
|
||||
description="Apple Terminal Silver Aerogel profile: dark grey background with white text, per-character typing animation of a shell session."
|
||||
variables={[]}
|
||||
meta={{"duration":12,"width":1920,"height":1080,"category":"Code Snippets","badge":"Stable","codeLines":250}}
|
||||
attribution={{"path":"registry/blocks/code-snippet-apple-terminal-silver-aerogel","tags":["code","developer","showcase","terminal","apple","apple-terminal"]}}
|
||||
hasCode
|
||||
rawUrl="https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/code-snippet-apple-terminal-silver-aerogel/code-snippet-apple-terminal-silver-aerogel.html"
|
||||
>
|
||||
|
||||
<InstallCommand command="npx hyperframes add code-snippet-apple-terminal-silver-aerogel" item="code-snippet-apple-terminal-silver-aerogel" />
|
||||
<CatalogSlot slot="code">
|
||||
|
||||
That writes one file: `compositions/code-snippet-apple-terminal-silver-aerogel.html`.
|
||||
|
||||
<iframe
|
||||
className="w-full aspect-video rounded-xl border-0 bg-zinc-100 dark:bg-zinc-800"
|
||||
title="code-snippet-apple-terminal-silver-aerogel preview"
|
||||
loading="lazy"
|
||||
srcDoc={`<!doctype html><html><head><meta charset="utf-8"><style>html,body{margin:0;height:100%;overflow:hidden;background:transparent}hyperframes-player{display:block;width:100%;height:100%}</style><script src="https://cdn.jsdelivr.net/npm/@hyperframes/player@latest/dist/hyperframes-player.global.js"><\/script></head><body><script>fetch("/public/catalog/blocks/code-snippet-apple-terminal-silver-aerogel.json").then(function(r){return r.json()}).then(function(d){var p=document.createElement("hyperframes-player");p.setAttribute("srcdoc",d.html);p.setAttribute("controls","");p.setAttribute("autoplay","");p.setAttribute("loop","");p.setAttribute("muted","");p.setAttribute("poster","https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/code-snippet-apple-terminal-silver-aerogel.png");document.body.appendChild(p)});<\/script></body></html>`}
|
||||
/>
|
||||
|
||||
## Source
|
||||
|
||||
<Accordion title="code-snippet-apple-terminal-silver-aerogel.html">
|
||||
|
||||
```html
|
||||
```html code-snippet-apple-terminal-silver-aerogel.html
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
@@ -275,13 +275,23 @@ That writes one file: `compositions/code-snippet-apple-terminal-silver-aerogel.h
|
||||
</html>
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
</CatalogSlot>
|
||||
|
||||
## Usage
|
||||
<CatalogSlot slot="install">
|
||||
|
||||
After installing, add the block to your host composition:
|
||||
<InstallCommand command="npx hyperframes add code-snippet-apple-terminal-silver-aerogel" item="code-snippet-apple-terminal-silver-aerogel" />
|
||||
|
||||
```html
|
||||
That writes one file: `compositions/code-snippet-apple-terminal-silver-aerogel.html`.
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
<CatalogSlot slot="docs">
|
||||
|
||||
## Add it to your video
|
||||
|
||||
It runs for 12 seconds at 1920×1080. Paste this into your composition:
|
||||
|
||||
```html index.html
|
||||
<div
|
||||
data-composition-id="code-snippet-apple-terminal-silver-aerogel"
|
||||
data-composition-src="compositions/code-snippet-apple-terminal-silver-aerogel.html"
|
||||
@@ -293,14 +303,8 @@ After installing, add the block to your host composition:
|
||||
></div>
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
- Faithful recreation of macOS Terminal.app Silver Aerogel profile — #3D3D3D dark grey background with white text
|
||||
- macOS window chrome with traffic light buttons and centered title bar
|
||||
- Per-character GSAP typing animation — command types out character by character
|
||||
- Output lines reveal sequentially after command executes
|
||||
- Cursor blinks three times at the end then holds steady
|
||||
- Self-contained HTML — no external assets, only GSAP CDN
|
||||
Move it in time with `data-start`. Put it on a different timeline row with
|
||||
`data-track-index`. See [data attributes](/concepts/data-attributes) for the rest.
|
||||
|
||||
{/* hf:generated-footer */}
|
||||
|
||||
@@ -311,3 +315,7 @@ Tagged `code` `developer` `showcase` `terminal` `apple` `apple-terminal`.
|
||||
- [Browse the complete Catalog](/catalog)
|
||||
- [Add assets and Catalog items in Studio](/studio/assets-and-blocks)
|
||||
- [Build a richer composition](/go-further)
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
</CatalogDetail>
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
---
|
||||
title: "Code Snippet - Apple Terminal Solid Colors"
|
||||
description: "Apple Terminal Solid Colors profile: deep purple background with white text, per-character typing animation of a shell session."
|
||||
mode: "frame"
|
||||
---
|
||||
|
||||
import { CatalogDetail, CatalogSlot } from "/snippets/catalog-detail.jsx";
|
||||
import { InstallCommand } from "/snippets/install-command.jsx";
|
||||
|
||||
## Install
|
||||
<CatalogDetail
|
||||
previewSrc="/public/catalog/blocks/code-snippet-apple-terminal-solid-colors.json"
|
||||
compositionId="code-snippet-apple-terminal-solid-colors"
|
||||
compositionSrc="compositions/code-snippet-apple-terminal-solid-colors.html"
|
||||
title="Code Snippet - Apple Terminal Solid Colors"
|
||||
description="Apple Terminal Solid Colors profile: deep purple background with white text, per-character typing animation of a shell session."
|
||||
variables={[]}
|
||||
meta={{"duration":12,"width":1920,"height":1080,"category":"Code Snippets","badge":"Stable","codeLines":247}}
|
||||
attribution={{"path":"registry/blocks/code-snippet-apple-terminal-solid-colors","tags":["code","developer","showcase","terminal","apple","apple-terminal"]}}
|
||||
hasCode
|
||||
rawUrl="https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/code-snippet-apple-terminal-solid-colors/code-snippet-apple-terminal-solid-colors.html"
|
||||
>
|
||||
|
||||
<InstallCommand command="npx hyperframes add code-snippet-apple-terminal-solid-colors" item="code-snippet-apple-terminal-solid-colors" />
|
||||
<CatalogSlot slot="code">
|
||||
|
||||
That writes one file: `compositions/code-snippet-apple-terminal-solid-colors.html`.
|
||||
|
||||
<iframe
|
||||
className="w-full aspect-video rounded-xl border-0 bg-zinc-100 dark:bg-zinc-800"
|
||||
title="code-snippet-apple-terminal-solid-colors preview"
|
||||
loading="lazy"
|
||||
srcDoc={`<!doctype html><html><head><meta charset="utf-8"><style>html,body{margin:0;height:100%;overflow:hidden;background:transparent}hyperframes-player{display:block;width:100%;height:100%}</style><script src="https://cdn.jsdelivr.net/npm/@hyperframes/player@latest/dist/hyperframes-player.global.js"><\/script></head><body><script>fetch("/public/catalog/blocks/code-snippet-apple-terminal-solid-colors.json").then(function(r){return r.json()}).then(function(d){var p=document.createElement("hyperframes-player");p.setAttribute("srcdoc",d.html);p.setAttribute("controls","");p.setAttribute("autoplay","");p.setAttribute("loop","");p.setAttribute("muted","");p.setAttribute("poster","https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/code-snippet-apple-terminal-solid-colors.png");document.body.appendChild(p)});<\/script></body></html>`}
|
||||
/>
|
||||
|
||||
## Source
|
||||
|
||||
<Accordion title="code-snippet-apple-terminal-solid-colors.html">
|
||||
|
||||
```html
|
||||
```html code-snippet-apple-terminal-solid-colors.html
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
@@ -272,13 +272,23 @@ That writes one file: `compositions/code-snippet-apple-terminal-solid-colors.htm
|
||||
</html>
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
</CatalogSlot>
|
||||
|
||||
## Usage
|
||||
<CatalogSlot slot="install">
|
||||
|
||||
After installing, add the block to your host composition:
|
||||
<InstallCommand command="npx hyperframes add code-snippet-apple-terminal-solid-colors" item="code-snippet-apple-terminal-solid-colors" />
|
||||
|
||||
```html
|
||||
That writes one file: `compositions/code-snippet-apple-terminal-solid-colors.html`.
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
<CatalogSlot slot="docs">
|
||||
|
||||
## Add it to your video
|
||||
|
||||
It runs for 12 seconds at 1920×1080. Paste this into your composition:
|
||||
|
||||
```html index.html
|
||||
<div
|
||||
data-composition-id="code-snippet-apple-terminal-solid-colors"
|
||||
data-composition-src="compositions/code-snippet-apple-terminal-solid-colors.html"
|
||||
@@ -290,14 +300,8 @@ After installing, add the block to your host composition:
|
||||
></div>
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
- Faithful recreation of macOS Terminal.app Solid Colors profile — #4C0099 deep purple background with white text
|
||||
- macOS window chrome with traffic light buttons and centered title bar
|
||||
- Per-character GSAP typing animation — command types out character by character
|
||||
- Output lines reveal sequentially after command executes
|
||||
- Cursor blinks three times at the end then holds steady
|
||||
- Self-contained HTML — no external assets, only GSAP CDN
|
||||
Move it in time with `data-start`. Put it on a different timeline row with
|
||||
`data-track-index`. See [data attributes](/concepts/data-attributes) for the rest.
|
||||
|
||||
{/* hf:generated-footer */}
|
||||
|
||||
@@ -308,3 +312,7 @@ Tagged `code` `developer` `showcase` `terminal` `apple` `apple-terminal`.
|
||||
- [Browse the complete Catalog](/catalog)
|
||||
- [Add assets and Catalog items in Studio](/studio/assets-and-blocks)
|
||||
- [Build a richer composition](/go-further)
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
</CatalogDetail>
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
---
|
||||
title: "Code Snippet - Dark 2026"
|
||||
description: "VS Code workbench with per-character typing animation in the Dark 2026 theme. Full editor chrome with activity bar, sidebar, tabs, terminal, and status bar."
|
||||
mode: "frame"
|
||||
---
|
||||
|
||||
import { CatalogDetail, CatalogSlot } from "/snippets/catalog-detail.jsx";
|
||||
import { InstallCommand } from "/snippets/install-command.jsx";
|
||||
|
||||
## Install
|
||||
<CatalogDetail
|
||||
previewSrc="/public/catalog/blocks/code-snippet-dark-2026.json"
|
||||
compositionId="code-snippet-dark-2026"
|
||||
compositionSrc="compositions/code-snippet-dark-2026.html"
|
||||
title="Code Snippet - Dark 2026"
|
||||
description="VS Code workbench with per-character typing animation in the Dark 2026 theme. Full editor chrome with activity bar, sidebar, tabs, terminal, and status bar."
|
||||
variables={[]}
|
||||
meta={{"duration":11,"width":1920,"height":1080,"category":"Code Snippets","badge":"Stable","codeLines":1091}}
|
||||
attribution={{"path":"registry/blocks/code-snippet-dark-2026","tags":["code","developer","showcase","vscode"]}}
|
||||
hasCode
|
||||
rawUrl="https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/code-snippet-dark-2026/code-snippet-dark-2026.html"
|
||||
>
|
||||
|
||||
<InstallCommand command="npx hyperframes add code-snippet-dark-2026" item="code-snippet-dark-2026" />
|
||||
<CatalogSlot slot="code">
|
||||
|
||||
That writes `compositions/code-snippet-dark-2026.html`, plus 1 supporting file under `assets/`.
|
||||
|
||||
<iframe
|
||||
className="w-full aspect-video rounded-xl border-0 bg-zinc-100 dark:bg-zinc-800"
|
||||
title="code-snippet-dark-2026 preview"
|
||||
loading="lazy"
|
||||
srcDoc={`<!doctype html><html><head><meta charset="utf-8"><style>html,body{margin:0;height:100%;overflow:hidden;background:transparent}hyperframes-player{display:block;width:100%;height:100%}</style><script src="https://cdn.jsdelivr.net/npm/@hyperframes/player@latest/dist/hyperframes-player.global.js"><\/script></head><body><script>fetch("/public/catalog/blocks/code-snippet-dark-2026.json").then(function(r){return r.json()}).then(function(d){var p=document.createElement("hyperframes-player");p.setAttribute("srcdoc",d.html);p.setAttribute("controls","");p.setAttribute("autoplay","");p.setAttribute("loop","");p.setAttribute("muted","");p.setAttribute("poster","https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/code-snippet-dark-2026.png");document.body.appendChild(p)});<\/script></body></html>`}
|
||||
/>
|
||||
|
||||
## Source
|
||||
|
||||
<Accordion title="code-snippet-dark-2026.html">
|
||||
|
||||
```html
|
||||
```html code-snippet-dark-2026.html
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
@@ -33,6 +33,7 @@ That writes `compositions/code-snippet-dark-2026.html`, plus 1 supporting file u
|
||||
<body>
|
||||
<template id="vscode-dark-2026-template">
|
||||
<div
|
||||
id="root"
|
||||
class="vscode-theme-scene"
|
||||
data-composition-id="vscode-dark-2026"
|
||||
data-width="1920"
|
||||
@@ -160,7 +161,7 @@ That writes `compositions/code-snippet-dark-2026.html`, plus 1 supporting file u
|
||||
sans-serif;
|
||||
}
|
||||
|
||||
.vscode-theme-scene {
|
||||
#root {
|
||||
--activity-bg: #181818;
|
||||
--activity-fg: #d7d7d7;
|
||||
--activity-muted: #868686;
|
||||
@@ -201,7 +202,7 @@ That writes `compositions/code-snippet-dark-2026.html`, plus 1 supporting file u
|
||||
overflow: hidden;
|
||||
background:
|
||||
linear-gradient(180deg, rgba(0, 0, 0, 0.16), rgba(0, 0, 0, 0.32)),
|
||||
url("../assets/background.jpeg") center / cover no-repeat;
|
||||
url("assets/background.jpeg") center / cover no-repeat;
|
||||
color: var(--editor-fg);
|
||||
}
|
||||
|
||||
@@ -1115,13 +1116,23 @@ That writes `compositions/code-snippet-dark-2026.html`, plus 1 supporting file u
|
||||
</html>
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
</CatalogSlot>
|
||||
|
||||
## Usage
|
||||
<CatalogSlot slot="install">
|
||||
|
||||
After installing, add the block to your host composition:
|
||||
<InstallCommand command="npx hyperframes add code-snippet-dark-2026" item="code-snippet-dark-2026" />
|
||||
|
||||
```html
|
||||
That writes `compositions/code-snippet-dark-2026.html`, plus 1 supporting file under `assets/`.
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
<CatalogSlot slot="docs">
|
||||
|
||||
## Add it to your video
|
||||
|
||||
It runs for 11 seconds at 1920×1080. Paste this into your composition:
|
||||
|
||||
```html index.html
|
||||
<div
|
||||
data-composition-id="code-snippet-dark-2026"
|
||||
data-composition-src="compositions/code-snippet-dark-2026.html"
|
||||
@@ -1133,13 +1144,8 @@ After installing, add the block to your host composition:
|
||||
></div>
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
- Full VS Code workbench recreation — activity bar, sidebar, explorer tree, editor tabs, breadcrumbs, terminal panel, status bar
|
||||
- Per-character typing animation with cursor tracking and active line highlight
|
||||
- Colors sourced from official `microsoft/vscode` built-in theme JSON
|
||||
- 3D perspective tilt at the end of the typing sequence
|
||||
- Terminal panel with mock test output
|
||||
Move it in time with `data-start`. Put it on a different timeline row with
|
||||
`data-track-index`. See [data attributes](/concepts/data-attributes) for the rest.
|
||||
|
||||
{/* hf:generated-footer */}
|
||||
|
||||
@@ -1150,3 +1156,7 @@ Tagged `code` `developer` `showcase` `vscode`.
|
||||
- [Browse the complete Catalog](/catalog)
|
||||
- [Add assets and Catalog items in Studio](/studio/assets-and-blocks)
|
||||
- [Build a richer composition](/go-further)
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
</CatalogDetail>
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
---
|
||||
title: "Code Snippet - Dark Modern"
|
||||
description: "VS Code workbench with per-character typing animation in the Dark Modern theme. Full editor chrome with activity bar, sidebar, tabs, terminal, and status bar."
|
||||
mode: "frame"
|
||||
---
|
||||
|
||||
import { CatalogDetail, CatalogSlot } from "/snippets/catalog-detail.jsx";
|
||||
import { InstallCommand } from "/snippets/install-command.jsx";
|
||||
|
||||
## Install
|
||||
<CatalogDetail
|
||||
previewSrc="/public/catalog/blocks/code-snippet-dark-modern.json"
|
||||
compositionId="code-snippet-dark-modern"
|
||||
compositionSrc="compositions/code-snippet-dark-modern.html"
|
||||
title="Code Snippet - Dark Modern"
|
||||
description="VS Code workbench with per-character typing animation in the Dark Modern theme. Full editor chrome with activity bar, sidebar, tabs, terminal, and status bar."
|
||||
variables={[]}
|
||||
meta={{"duration":11,"width":1920,"height":1080,"category":"Code Snippets","badge":"Stable","codeLines":1087}}
|
||||
attribution={{"path":"registry/blocks/code-snippet-dark-modern","tags":["code","developer","showcase","vscode"]}}
|
||||
hasCode
|
||||
rawUrl="https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/code-snippet-dark-modern/code-snippet-dark-modern.html"
|
||||
>
|
||||
|
||||
<InstallCommand command="npx hyperframes add code-snippet-dark-modern" item="code-snippet-dark-modern" />
|
||||
<CatalogSlot slot="code">
|
||||
|
||||
That writes `compositions/code-snippet-dark-modern.html`, plus 1 supporting file under `assets/`.
|
||||
|
||||
<iframe
|
||||
className="w-full aspect-video rounded-xl border-0 bg-zinc-100 dark:bg-zinc-800"
|
||||
title="code-snippet-dark-modern preview"
|
||||
loading="lazy"
|
||||
srcDoc={`<!doctype html><html><head><meta charset="utf-8"><style>html,body{margin:0;height:100%;overflow:hidden;background:transparent}hyperframes-player{display:block;width:100%;height:100%}</style><script src="https://cdn.jsdelivr.net/npm/@hyperframes/player@latest/dist/hyperframes-player.global.js"><\/script></head><body><script>fetch("/public/catalog/blocks/code-snippet-dark-modern.json").then(function(r){return r.json()}).then(function(d){var p=document.createElement("hyperframes-player");p.setAttribute("srcdoc",d.html);p.setAttribute("controls","");p.setAttribute("autoplay","");p.setAttribute("loop","");p.setAttribute("muted","");p.setAttribute("poster","https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/code-snippet-dark-modern.png");document.body.appendChild(p)});<\/script></body></html>`}
|
||||
/>
|
||||
|
||||
## Source
|
||||
|
||||
<Accordion title="code-snippet-dark-modern.html">
|
||||
|
||||
```html
|
||||
```html code-snippet-dark-modern.html
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
@@ -33,6 +33,7 @@ That writes `compositions/code-snippet-dark-modern.html`, plus 1 supporting file
|
||||
<body>
|
||||
<template id="vscode-dark-modern-template">
|
||||
<div
|
||||
id="root"
|
||||
class="vscode-theme-scene"
|
||||
data-composition-id="vscode-dark-modern"
|
||||
data-width="1920"
|
||||
@@ -160,7 +161,7 @@ That writes `compositions/code-snippet-dark-modern.html`, plus 1 supporting file
|
||||
sans-serif;
|
||||
}
|
||||
|
||||
.vscode-theme-scene {
|
||||
#root {
|
||||
--activity-bg: #181818;
|
||||
--activity-fg: #d7d7d7;
|
||||
--activity-muted: #868686;
|
||||
@@ -201,7 +202,7 @@ That writes `compositions/code-snippet-dark-modern.html`, plus 1 supporting file
|
||||
overflow: hidden;
|
||||
background:
|
||||
linear-gradient(180deg, rgba(0, 0, 0, 0.16), rgba(0, 0, 0, 0.32)),
|
||||
url("../assets/background.jpeg") center / cover no-repeat;
|
||||
url("assets/background.jpeg") center / cover no-repeat;
|
||||
color: var(--editor-fg);
|
||||
}
|
||||
|
||||
@@ -1111,13 +1112,23 @@ That writes `compositions/code-snippet-dark-modern.html`, plus 1 supporting file
|
||||
</html>
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
</CatalogSlot>
|
||||
|
||||
## Usage
|
||||
<CatalogSlot slot="install">
|
||||
|
||||
After installing, add the block to your host composition:
|
||||
<InstallCommand command="npx hyperframes add code-snippet-dark-modern" item="code-snippet-dark-modern" />
|
||||
|
||||
```html
|
||||
That writes `compositions/code-snippet-dark-modern.html`, plus 1 supporting file under `assets/`.
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
<CatalogSlot slot="docs">
|
||||
|
||||
## Add it to your video
|
||||
|
||||
It runs for 11 seconds at 1920×1080. Paste this into your composition:
|
||||
|
||||
```html index.html
|
||||
<div
|
||||
data-composition-id="code-snippet-dark-modern"
|
||||
data-composition-src="compositions/code-snippet-dark-modern.html"
|
||||
@@ -1129,13 +1140,8 @@ After installing, add the block to your host composition:
|
||||
></div>
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
- Full VS Code workbench recreation — activity bar, sidebar, explorer tree, editor tabs, breadcrumbs, terminal panel, status bar
|
||||
- Per-character typing animation with cursor tracking and active line highlight
|
||||
- Colors sourced from official `microsoft/vscode` built-in theme JSON
|
||||
- 3D perspective tilt at the end of the typing sequence
|
||||
- Terminal panel with mock test output
|
||||
Move it in time with `data-start`. Put it on a different timeline row with
|
||||
`data-track-index`. See [data attributes](/concepts/data-attributes) for the rest.
|
||||
|
||||
{/* hf:generated-footer */}
|
||||
|
||||
@@ -1146,3 +1152,7 @@ Tagged `code` `developer` `showcase` `vscode`.
|
||||
- [Browse the complete Catalog](/catalog)
|
||||
- [Add assets and Catalog items in Studio](/studio/assets-and-blocks)
|
||||
- [Build a richer composition](/go-further)
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
</CatalogDetail>
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
---
|
||||
title: "Code Snippet - Dark+"
|
||||
description: "VS Code workbench with per-character typing animation in the Dark+ theme. Full editor chrome with activity bar, sidebar, tabs, terminal, and status bar."
|
||||
mode: "frame"
|
||||
---
|
||||
|
||||
import { CatalogDetail, CatalogSlot } from "/snippets/catalog-detail.jsx";
|
||||
import { InstallCommand } from "/snippets/install-command.jsx";
|
||||
|
||||
## Install
|
||||
<CatalogDetail
|
||||
previewSrc="/public/catalog/blocks/code-snippet-dark-plus.json"
|
||||
compositionId="code-snippet-dark-plus"
|
||||
compositionSrc="compositions/code-snippet-dark-plus.html"
|
||||
title="Code Snippet - Dark+"
|
||||
description="VS Code workbench with per-character typing animation in the Dark+ theme. Full editor chrome with activity bar, sidebar, tabs, terminal, and status bar."
|
||||
variables={[]}
|
||||
meta={{"duration":11,"width":1920,"height":1080,"category":"Code Snippets","badge":"Stable","codeLines":1056}}
|
||||
attribution={{"path":"registry/blocks/code-snippet-dark-plus","tags":["code","developer","showcase","vscode"]}}
|
||||
hasCode
|
||||
rawUrl="https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/code-snippet-dark-plus/code-snippet-dark-plus.html"
|
||||
>
|
||||
|
||||
<InstallCommand command="npx hyperframes add code-snippet-dark-plus" item="code-snippet-dark-plus" />
|
||||
<CatalogSlot slot="code">
|
||||
|
||||
That writes `compositions/code-snippet-dark-plus.html`, plus 1 supporting file under `assets/`.
|
||||
|
||||
<iframe
|
||||
className="w-full aspect-video rounded-xl border-0 bg-zinc-100 dark:bg-zinc-800"
|
||||
title="code-snippet-dark-plus preview"
|
||||
loading="lazy"
|
||||
srcDoc={`<!doctype html><html><head><meta charset="utf-8"><style>html,body{margin:0;height:100%;overflow:hidden;background:transparent}hyperframes-player{display:block;width:100%;height:100%}</style><script src="https://cdn.jsdelivr.net/npm/@hyperframes/player@latest/dist/hyperframes-player.global.js"><\/script></head><body><script>fetch("/public/catalog/blocks/code-snippet-dark-plus.json").then(function(r){return r.json()}).then(function(d){var p=document.createElement("hyperframes-player");p.setAttribute("srcdoc",d.html);p.setAttribute("controls","");p.setAttribute("autoplay","");p.setAttribute("loop","");p.setAttribute("muted","");p.setAttribute("poster","https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/code-snippet-dark-plus.png");document.body.appendChild(p)});<\/script></body></html>`}
|
||||
/>
|
||||
|
||||
## Source
|
||||
|
||||
<Accordion title="code-snippet-dark-plus.html">
|
||||
|
||||
```html
|
||||
```html code-snippet-dark-plus.html
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
@@ -33,6 +33,7 @@ That writes `compositions/code-snippet-dark-plus.html`, plus 1 supporting file u
|
||||
<body>
|
||||
<template id="vscode-dark-plus-template">
|
||||
<div
|
||||
id="root"
|
||||
class="vscode-theme-scene"
|
||||
data-composition-id="vscode-dark-plus"
|
||||
data-width="1920"
|
||||
@@ -160,7 +161,7 @@ That writes `compositions/code-snippet-dark-plus.html`, plus 1 supporting file u
|
||||
sans-serif;
|
||||
}
|
||||
|
||||
.vscode-theme-scene {
|
||||
#root {
|
||||
--activity-bg: #181818;
|
||||
--activity-fg: #d7d7d7;
|
||||
--activity-muted: #868686;
|
||||
@@ -201,7 +202,7 @@ That writes `compositions/code-snippet-dark-plus.html`, plus 1 supporting file u
|
||||
overflow: hidden;
|
||||
background:
|
||||
linear-gradient(180deg, rgba(0, 0, 0, 0.16), rgba(0, 0, 0, 0.32)),
|
||||
url("../assets/background.jpeg") center / cover no-repeat;
|
||||
url("assets/background.jpeg") center / cover no-repeat;
|
||||
color: var(--editor-fg);
|
||||
}
|
||||
|
||||
@@ -1080,13 +1081,23 @@ That writes `compositions/code-snippet-dark-plus.html`, plus 1 supporting file u
|
||||
</html>
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
</CatalogSlot>
|
||||
|
||||
## Usage
|
||||
<CatalogSlot slot="install">
|
||||
|
||||
After installing, add the block to your host composition:
|
||||
<InstallCommand command="npx hyperframes add code-snippet-dark-plus" item="code-snippet-dark-plus" />
|
||||
|
||||
```html
|
||||
That writes `compositions/code-snippet-dark-plus.html`, plus 1 supporting file under `assets/`.
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
<CatalogSlot slot="docs">
|
||||
|
||||
## Add it to your video
|
||||
|
||||
It runs for 11 seconds at 1920×1080. Paste this into your composition:
|
||||
|
||||
```html index.html
|
||||
<div
|
||||
data-composition-id="code-snippet-dark-plus"
|
||||
data-composition-src="compositions/code-snippet-dark-plus.html"
|
||||
@@ -1098,13 +1109,8 @@ After installing, add the block to your host composition:
|
||||
></div>
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
- Full VS Code workbench recreation — activity bar, sidebar, explorer tree, editor tabs, breadcrumbs, terminal panel, status bar
|
||||
- Per-character typing animation with cursor tracking and active line highlight
|
||||
- Colors sourced from official `microsoft/vscode` built-in theme JSON
|
||||
- 3D perspective tilt at the end of the typing sequence
|
||||
- Terminal panel with mock test output
|
||||
Move it in time with `data-start`. Put it on a different timeline row with
|
||||
`data-track-index`. See [data attributes](/concepts/data-attributes) for the rest.
|
||||
|
||||
{/* hf:generated-footer */}
|
||||
|
||||
@@ -1115,3 +1121,7 @@ Tagged `code` `developer` `showcase` `vscode`.
|
||||
- [Browse the complete Catalog](/catalog)
|
||||
- [Add assets and Catalog items in Studio](/studio/assets-and-blocks)
|
||||
- [Build a richer composition](/go-further)
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
</CatalogDetail>
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
---
|
||||
title: "Code Snippet Flight"
|
||||
description: "Discrete code snippets fly in from the side and assemble into a stacked program, staggered. Block-level FLIP."
|
||||
mode: "frame"
|
||||
---
|
||||
|
||||
import { CatalogDetail, CatalogSlot } from "/snippets/catalog-detail.jsx";
|
||||
import { InstallCommand } from "/snippets/install-command.jsx";
|
||||
|
||||
## Install
|
||||
<CatalogDetail
|
||||
previewSrc="/public/catalog/blocks/code-snippet-flight.json"
|
||||
compositionId="code-snippet-flight"
|
||||
compositionSrc="compositions/code-snippet-flight.html"
|
||||
title="Code Snippet Flight"
|
||||
description="Discrete code snippets fly in from the side and assemble into a stacked program, staggered. Block-level FLIP."
|
||||
variables={[]}
|
||||
meta={{"duration":6,"width":1920,"height":1080,"category":"Code Animations","badge":"Stable","codeLines":668}}
|
||||
attribution={{"path":"registry/blocks/code-snippet-flight","tags":["code","code-animation","flight","developer"]}}
|
||||
hasCode
|
||||
rawUrl="https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/code-snippet-flight/code-snippet-flight.html"
|
||||
>
|
||||
|
||||
<InstallCommand command="npx hyperframes add code-snippet-flight" item="code-snippet-flight" />
|
||||
<CatalogSlot slot="code">
|
||||
|
||||
That writes one file: `compositions/code-snippet-flight.html`.
|
||||
|
||||
<iframe
|
||||
className="w-full aspect-video rounded-xl border-0 bg-zinc-100 dark:bg-zinc-800"
|
||||
title="code-snippet-flight preview"
|
||||
loading="lazy"
|
||||
srcDoc={`<!doctype html><html><head><meta charset="utf-8"><style>html,body{margin:0;height:100%;overflow:hidden;background:transparent}hyperframes-player{display:block;width:100%;height:100%}</style><script src="https://cdn.jsdelivr.net/npm/@hyperframes/player@latest/dist/hyperframes-player.global.js"><\/script></head><body><script>fetch("/public/catalog/blocks/code-snippet-flight.json").then(function(r){return r.json()}).then(function(d){var p=document.createElement("hyperframes-player");p.setAttribute("srcdoc",d.html);p.setAttribute("controls","");p.setAttribute("autoplay","");p.setAttribute("loop","");p.setAttribute("muted","");p.setAttribute("poster","https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/code-snippet-flight.png");document.body.appendChild(p)});<\/script></body></html>`}
|
||||
/>
|
||||
|
||||
## Source
|
||||
|
||||
<Accordion title="code-snippet-flight.html">
|
||||
|
||||
```html
|
||||
```html code-snippet-flight.html
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
@@ -673,12 +673,15 @@ That writes one file: `compositions/code-snippet-flight.html`.
|
||||
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
|
||||
window.__timelines["code-snippet-flight"] = tl; // register AFTER setup completes
|
||||
if (typeof window.__hfForceTimelineRebind === "function") {
|
||||
window.__hfForceTimelineRebind(); // re-nest now that we are populated
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -690,16 +693,37 @@ That writes one file: `compositions/code-snippet-flight.html`.
|
||||
</html>
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
</CatalogSlot>
|
||||
|
||||
## Usage
|
||||
<CatalogSlot slot="install">
|
||||
|
||||
After installing, add the block to your host composition:
|
||||
<InstallCommand command="npx hyperframes add code-snippet-flight" item="code-snippet-flight" />
|
||||
|
||||
```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>
|
||||
That writes one file: `compositions/code-snippet-flight.html`.
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
<CatalogSlot slot="docs">
|
||||
|
||||
## Add it to your video
|
||||
|
||||
It runs for 6 seconds at 1920×1080. Paste this into your composition:
|
||||
|
||||
```html index.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>
|
||||
```
|
||||
|
||||
Move it in time with `data-start`. Put it on a different timeline row with
|
||||
`data-track-index`. See [data attributes](/concepts/data-attributes) for the rest.
|
||||
|
||||
{/* hf:generated-footer */}
|
||||
|
||||
Tagged `code` `code-animation` `flight` `developer`.
|
||||
@@ -709,3 +733,7 @@ Tagged `code` `code-animation` `flight` `developer`.
|
||||
- [Browse the complete Catalog](/catalog)
|
||||
- [Add assets and Catalog items in Studio](/studio/assets-and-blocks)
|
||||
- [Build a richer composition](/go-further)
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
</CatalogDetail>
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
---
|
||||
title: "Code Snippet - High Contrast Light"
|
||||
description: "VS Code workbench with per-character typing animation in the High Contrast Light theme. Full editor chrome with activity bar, sidebar, tabs, terminal, and status bar."
|
||||
mode: "frame"
|
||||
---
|
||||
|
||||
import { CatalogDetail, CatalogSlot } from "/snippets/catalog-detail.jsx";
|
||||
import { InstallCommand } from "/snippets/install-command.jsx";
|
||||
|
||||
## Install
|
||||
<CatalogDetail
|
||||
previewSrc="/public/catalog/blocks/code-snippet-high-contrast-light.json"
|
||||
compositionId="code-snippet-high-contrast-light"
|
||||
compositionSrc="compositions/code-snippet-high-contrast-light.html"
|
||||
title="Code Snippet - High Contrast Light"
|
||||
description="VS Code workbench with per-character typing animation in the High Contrast Light theme. Full editor chrome with activity bar, sidebar, tabs, terminal, and status bar."
|
||||
variables={[]}
|
||||
meta={{"duration":11,"width":1920,"height":1080,"category":"Code Snippets","badge":"Stable","codeLines":1051}}
|
||||
attribution={{"path":"registry/blocks/code-snippet-high-contrast-light","tags":["code","developer","showcase","vscode"]}}
|
||||
hasCode
|
||||
rawUrl="https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/code-snippet-high-contrast-light/code-snippet-high-contrast-light.html"
|
||||
>
|
||||
|
||||
<InstallCommand command="npx hyperframes add code-snippet-high-contrast-light" item="code-snippet-high-contrast-light" />
|
||||
<CatalogSlot slot="code">
|
||||
|
||||
That writes `compositions/code-snippet-high-contrast-light.html`, plus 1 supporting file under `assets/`.
|
||||
|
||||
<iframe
|
||||
className="w-full aspect-video rounded-xl border-0 bg-zinc-100 dark:bg-zinc-800"
|
||||
title="code-snippet-high-contrast-light preview"
|
||||
loading="lazy"
|
||||
srcDoc={`<!doctype html><html><head><meta charset="utf-8"><style>html,body{margin:0;height:100%;overflow:hidden;background:transparent}hyperframes-player{display:block;width:100%;height:100%}</style><script src="https://cdn.jsdelivr.net/npm/@hyperframes/player@latest/dist/hyperframes-player.global.js"><\/script></head><body><script>fetch("/public/catalog/blocks/code-snippet-high-contrast-light.json").then(function(r){return r.json()}).then(function(d){var p=document.createElement("hyperframes-player");p.setAttribute("srcdoc",d.html);p.setAttribute("controls","");p.setAttribute("autoplay","");p.setAttribute("loop","");p.setAttribute("muted","");p.setAttribute("poster","https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/code-snippet-high-contrast-light.png");document.body.appendChild(p)});<\/script></body></html>`}
|
||||
/>
|
||||
|
||||
## Source
|
||||
|
||||
<Accordion title="code-snippet-high-contrast-light.html">
|
||||
|
||||
```html
|
||||
```html code-snippet-high-contrast-light.html
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
@@ -33,6 +33,7 @@ That writes `compositions/code-snippet-high-contrast-light.html`, plus 1 support
|
||||
<body>
|
||||
<template id="vscode-high-contrast-light-template">
|
||||
<div
|
||||
id="root"
|
||||
class="vscode-theme-scene"
|
||||
data-composition-id="vscode-high-contrast-light"
|
||||
data-width="1920"
|
||||
@@ -160,7 +161,7 @@ That writes `compositions/code-snippet-high-contrast-light.html`, plus 1 support
|
||||
sans-serif;
|
||||
}
|
||||
|
||||
.vscode-theme-scene {
|
||||
#root {
|
||||
--activity-bg: #181818;
|
||||
--activity-fg: #d7d7d7;
|
||||
--activity-muted: #868686;
|
||||
@@ -201,7 +202,7 @@ That writes `compositions/code-snippet-high-contrast-light.html`, plus 1 support
|
||||
overflow: hidden;
|
||||
background:
|
||||
linear-gradient(180deg, rgba(0, 0, 0, 0.16), rgba(0, 0, 0, 0.32)),
|
||||
url("../assets/background.jpeg") center / cover no-repeat;
|
||||
url("assets/background.jpeg") center / cover no-repeat;
|
||||
color: var(--editor-fg);
|
||||
}
|
||||
|
||||
@@ -1075,13 +1076,23 @@ That writes `compositions/code-snippet-high-contrast-light.html`, plus 1 support
|
||||
</html>
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
</CatalogSlot>
|
||||
|
||||
## Usage
|
||||
<CatalogSlot slot="install">
|
||||
|
||||
After installing, add the block to your host composition:
|
||||
<InstallCommand command="npx hyperframes add code-snippet-high-contrast-light" item="code-snippet-high-contrast-light" />
|
||||
|
||||
```html
|
||||
That writes `compositions/code-snippet-high-contrast-light.html`, plus 1 supporting file under `assets/`.
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
<CatalogSlot slot="docs">
|
||||
|
||||
## Add it to your video
|
||||
|
||||
It runs for 11 seconds at 1920×1080. Paste this into your composition:
|
||||
|
||||
```html index.html
|
||||
<div
|
||||
data-composition-id="code-snippet-high-contrast-light"
|
||||
data-composition-src="compositions/code-snippet-high-contrast-light.html"
|
||||
@@ -1093,13 +1104,8 @@ After installing, add the block to your host composition:
|
||||
></div>
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
- Full VS Code workbench recreation — activity bar, sidebar, explorer tree, editor tabs, breadcrumbs, terminal panel, status bar
|
||||
- Per-character typing animation with cursor tracking and active line highlight
|
||||
- Colors sourced from official `microsoft/vscode` built-in theme JSON
|
||||
- 3D perspective tilt at the end of the typing sequence
|
||||
- Terminal panel with mock test output
|
||||
Move it in time with `data-start`. Put it on a different timeline row with
|
||||
`data-track-index`. See [data attributes](/concepts/data-attributes) for the rest.
|
||||
|
||||
{/* hf:generated-footer */}
|
||||
|
||||
@@ -1110,3 +1116,7 @@ Tagged `code` `developer` `showcase` `vscode`.
|
||||
- [Browse the complete Catalog](/catalog)
|
||||
- [Add assets and Catalog items in Studio](/studio/assets-and-blocks)
|
||||
- [Build a richer composition](/go-further)
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
</CatalogDetail>
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
---
|
||||
title: "Code Snippet - High Contrast"
|
||||
description: "VS Code workbench with per-character typing animation in the High Contrast theme. Full editor chrome with activity bar, sidebar, tabs, terminal, and status bar."
|
||||
mode: "frame"
|
||||
---
|
||||
|
||||
import { CatalogDetail, CatalogSlot } from "/snippets/catalog-detail.jsx";
|
||||
import { InstallCommand } from "/snippets/install-command.jsx";
|
||||
|
||||
## Install
|
||||
<CatalogDetail
|
||||
previewSrc="/public/catalog/blocks/code-snippet-high-contrast.json"
|
||||
compositionId="code-snippet-high-contrast"
|
||||
compositionSrc="compositions/code-snippet-high-contrast.html"
|
||||
title="Code Snippet - High Contrast"
|
||||
description="VS Code workbench with per-character typing animation in the High Contrast theme. Full editor chrome with activity bar, sidebar, tabs, terminal, and status bar."
|
||||
variables={[]}
|
||||
meta={{"duration":11,"width":1920,"height":1080,"category":"Code Snippets","badge":"Stable","codeLines":1054}}
|
||||
attribution={{"path":"registry/blocks/code-snippet-high-contrast","tags":["code","developer","showcase","vscode"]}}
|
||||
hasCode
|
||||
rawUrl="https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/code-snippet-high-contrast/code-snippet-high-contrast.html"
|
||||
>
|
||||
|
||||
<InstallCommand command="npx hyperframes add code-snippet-high-contrast" item="code-snippet-high-contrast" />
|
||||
<CatalogSlot slot="code">
|
||||
|
||||
That writes `compositions/code-snippet-high-contrast.html`, plus 1 supporting file under `assets/`.
|
||||
|
||||
<iframe
|
||||
className="w-full aspect-video rounded-xl border-0 bg-zinc-100 dark:bg-zinc-800"
|
||||
title="code-snippet-high-contrast preview"
|
||||
loading="lazy"
|
||||
srcDoc={`<!doctype html><html><head><meta charset="utf-8"><style>html,body{margin:0;height:100%;overflow:hidden;background:transparent}hyperframes-player{display:block;width:100%;height:100%}</style><script src="https://cdn.jsdelivr.net/npm/@hyperframes/player@latest/dist/hyperframes-player.global.js"><\/script></head><body><script>fetch("/public/catalog/blocks/code-snippet-high-contrast.json").then(function(r){return r.json()}).then(function(d){var p=document.createElement("hyperframes-player");p.setAttribute("srcdoc",d.html);p.setAttribute("controls","");p.setAttribute("autoplay","");p.setAttribute("loop","");p.setAttribute("muted","");p.setAttribute("poster","https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/code-snippet-high-contrast.png");document.body.appendChild(p)});<\/script></body></html>`}
|
||||
/>
|
||||
|
||||
## Source
|
||||
|
||||
<Accordion title="code-snippet-high-contrast.html">
|
||||
|
||||
```html
|
||||
```html code-snippet-high-contrast.html
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
@@ -33,6 +33,7 @@ That writes `compositions/code-snippet-high-contrast.html`, plus 1 supporting fi
|
||||
<body>
|
||||
<template id="vscode-high-contrast-template">
|
||||
<div
|
||||
id="root"
|
||||
class="vscode-theme-scene"
|
||||
data-composition-id="vscode-high-contrast"
|
||||
data-width="1920"
|
||||
@@ -160,7 +161,7 @@ That writes `compositions/code-snippet-high-contrast.html`, plus 1 supporting fi
|
||||
sans-serif;
|
||||
}
|
||||
|
||||
.vscode-theme-scene {
|
||||
#root {
|
||||
--activity-bg: #181818;
|
||||
--activity-fg: #d7d7d7;
|
||||
--activity-muted: #868686;
|
||||
@@ -201,7 +202,7 @@ That writes `compositions/code-snippet-high-contrast.html`, plus 1 supporting fi
|
||||
overflow: hidden;
|
||||
background:
|
||||
linear-gradient(180deg, rgba(0, 0, 0, 0.16), rgba(0, 0, 0, 0.32)),
|
||||
url("../assets/background.jpeg") center / cover no-repeat;
|
||||
url("assets/background.jpeg") center / cover no-repeat;
|
||||
color: var(--editor-fg);
|
||||
}
|
||||
|
||||
@@ -1078,13 +1079,23 @@ That writes `compositions/code-snippet-high-contrast.html`, plus 1 supporting fi
|
||||
</html>
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
</CatalogSlot>
|
||||
|
||||
## Usage
|
||||
<CatalogSlot slot="install">
|
||||
|
||||
After installing, add the block to your host composition:
|
||||
<InstallCommand command="npx hyperframes add code-snippet-high-contrast" item="code-snippet-high-contrast" />
|
||||
|
||||
```html
|
||||
That writes `compositions/code-snippet-high-contrast.html`, plus 1 supporting file under `assets/`.
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
<CatalogSlot slot="docs">
|
||||
|
||||
## Add it to your video
|
||||
|
||||
It runs for 11 seconds at 1920×1080. Paste this into your composition:
|
||||
|
||||
```html index.html
|
||||
<div
|
||||
data-composition-id="code-snippet-high-contrast"
|
||||
data-composition-src="compositions/code-snippet-high-contrast.html"
|
||||
@@ -1096,13 +1107,8 @@ After installing, add the block to your host composition:
|
||||
></div>
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
- Full VS Code workbench recreation — activity bar, sidebar, explorer tree, editor tabs, breadcrumbs, terminal panel, status bar
|
||||
- Per-character typing animation with cursor tracking and active line highlight
|
||||
- Colors sourced from official `microsoft/vscode` built-in theme JSON
|
||||
- 3D perspective tilt at the end of the typing sequence
|
||||
- Terminal panel with mock test output
|
||||
Move it in time with `data-start`. Put it on a different timeline row with
|
||||
`data-track-index`. See [data attributes](/concepts/data-attributes) for the rest.
|
||||
|
||||
{/* hf:generated-footer */}
|
||||
|
||||
@@ -1113,3 +1119,7 @@ Tagged `code` `developer` `showcase` `vscode`.
|
||||
- [Browse the complete Catalog](/catalog)
|
||||
- [Add assets and Catalog items in Studio](/studio/assets-and-blocks)
|
||||
- [Build a richer composition](/go-further)
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
</CatalogDetail>
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
---
|
||||
title: "Code Snippet - Light 2026"
|
||||
description: "VS Code workbench with per-character typing animation in the Light 2026 theme. Full editor chrome with activity bar, sidebar, tabs, terminal, and status bar."
|
||||
mode: "frame"
|
||||
---
|
||||
|
||||
import { CatalogDetail, CatalogSlot } from "/snippets/catalog-detail.jsx";
|
||||
import { InstallCommand } from "/snippets/install-command.jsx";
|
||||
|
||||
## Install
|
||||
<CatalogDetail
|
||||
previewSrc="/public/catalog/blocks/code-snippet-light-2026.json"
|
||||
compositionId="code-snippet-light-2026"
|
||||
compositionSrc="compositions/code-snippet-light-2026.html"
|
||||
title="Code Snippet - Light 2026"
|
||||
description="VS Code workbench with per-character typing animation in the Light 2026 theme. Full editor chrome with activity bar, sidebar, tabs, terminal, and status bar."
|
||||
variables={[]}
|
||||
meta={{"duration":11,"width":1920,"height":1080,"category":"Code Snippets","badge":"Stable","codeLines":1090}}
|
||||
attribution={{"path":"registry/blocks/code-snippet-light-2026","tags":["code","developer","showcase","vscode"]}}
|
||||
hasCode
|
||||
rawUrl="https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/code-snippet-light-2026/code-snippet-light-2026.html"
|
||||
>
|
||||
|
||||
<InstallCommand command="npx hyperframes add code-snippet-light-2026" item="code-snippet-light-2026" />
|
||||
<CatalogSlot slot="code">
|
||||
|
||||
That writes `compositions/code-snippet-light-2026.html`, plus 1 supporting file under `assets/`.
|
||||
|
||||
<iframe
|
||||
className="w-full aspect-video rounded-xl border-0 bg-zinc-100 dark:bg-zinc-800"
|
||||
title="code-snippet-light-2026 preview"
|
||||
loading="lazy"
|
||||
srcDoc={`<!doctype html><html><head><meta charset="utf-8"><style>html,body{margin:0;height:100%;overflow:hidden;background:transparent}hyperframes-player{display:block;width:100%;height:100%}</style><script src="https://cdn.jsdelivr.net/npm/@hyperframes/player@latest/dist/hyperframes-player.global.js"><\/script></head><body><script>fetch("/public/catalog/blocks/code-snippet-light-2026.json").then(function(r){return r.json()}).then(function(d){var p=document.createElement("hyperframes-player");p.setAttribute("srcdoc",d.html);p.setAttribute("controls","");p.setAttribute("autoplay","");p.setAttribute("loop","");p.setAttribute("muted","");p.setAttribute("poster","https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/code-snippet-light-2026.png");document.body.appendChild(p)});<\/script></body></html>`}
|
||||
/>
|
||||
|
||||
## Source
|
||||
|
||||
<Accordion title="code-snippet-light-2026.html">
|
||||
|
||||
```html
|
||||
```html code-snippet-light-2026.html
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
@@ -33,6 +33,7 @@ That writes `compositions/code-snippet-light-2026.html`, plus 1 supporting file
|
||||
<body>
|
||||
<template id="vscode-light-2026-template">
|
||||
<div
|
||||
id="root"
|
||||
class="vscode-theme-scene"
|
||||
data-composition-id="vscode-light-2026"
|
||||
data-width="1920"
|
||||
@@ -160,7 +161,7 @@ That writes `compositions/code-snippet-light-2026.html`, plus 1 supporting file
|
||||
sans-serif;
|
||||
}
|
||||
|
||||
.vscode-theme-scene {
|
||||
#root {
|
||||
--activity-bg: #181818;
|
||||
--activity-fg: #d7d7d7;
|
||||
--activity-muted: #868686;
|
||||
@@ -201,7 +202,7 @@ That writes `compositions/code-snippet-light-2026.html`, plus 1 supporting file
|
||||
overflow: hidden;
|
||||
background:
|
||||
linear-gradient(180deg, rgba(0, 0, 0, 0.16), rgba(0, 0, 0, 0.32)),
|
||||
url("../assets/background.jpeg") center / cover no-repeat;
|
||||
url("assets/background.jpeg") center / cover no-repeat;
|
||||
color: var(--editor-fg);
|
||||
}
|
||||
|
||||
@@ -1114,13 +1115,23 @@ That writes `compositions/code-snippet-light-2026.html`, plus 1 supporting file
|
||||
</html>
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
</CatalogSlot>
|
||||
|
||||
## Usage
|
||||
<CatalogSlot slot="install">
|
||||
|
||||
After installing, add the block to your host composition:
|
||||
<InstallCommand command="npx hyperframes add code-snippet-light-2026" item="code-snippet-light-2026" />
|
||||
|
||||
```html
|
||||
That writes `compositions/code-snippet-light-2026.html`, plus 1 supporting file under `assets/`.
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
<CatalogSlot slot="docs">
|
||||
|
||||
## Add it to your video
|
||||
|
||||
It runs for 11 seconds at 1920×1080. Paste this into your composition:
|
||||
|
||||
```html index.html
|
||||
<div
|
||||
data-composition-id="code-snippet-light-2026"
|
||||
data-composition-src="compositions/code-snippet-light-2026.html"
|
||||
@@ -1132,13 +1143,8 @@ After installing, add the block to your host composition:
|
||||
></div>
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
- Full VS Code workbench recreation — activity bar, sidebar, explorer tree, editor tabs, breadcrumbs, terminal panel, status bar
|
||||
- Per-character typing animation with cursor tracking and active line highlight
|
||||
- Colors sourced from official `microsoft/vscode` built-in theme JSON
|
||||
- 3D perspective tilt at the end of the typing sequence
|
||||
- Terminal panel with mock test output
|
||||
Move it in time with `data-start`. Put it on a different timeline row with
|
||||
`data-track-index`. See [data attributes](/concepts/data-attributes) for the rest.
|
||||
|
||||
{/* hf:generated-footer */}
|
||||
|
||||
@@ -1149,3 +1155,7 @@ Tagged `code` `developer` `showcase` `vscode`.
|
||||
- [Browse the complete Catalog](/catalog)
|
||||
- [Add assets and Catalog items in Studio](/studio/assets-and-blocks)
|
||||
- [Build a richer composition](/go-further)
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
</CatalogDetail>
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
---
|
||||
title: "Code Snippet - Light Modern"
|
||||
description: "VS Code workbench with per-character typing animation in the Light Modern theme. Full editor chrome with activity bar, sidebar, tabs, terminal, and status bar."
|
||||
mode: "frame"
|
||||
---
|
||||
|
||||
import { CatalogDetail, CatalogSlot } from "/snippets/catalog-detail.jsx";
|
||||
import { InstallCommand } from "/snippets/install-command.jsx";
|
||||
|
||||
## Install
|
||||
<CatalogDetail
|
||||
previewSrc="/public/catalog/blocks/code-snippet-light-modern.json"
|
||||
compositionId="code-snippet-light-modern"
|
||||
compositionSrc="compositions/code-snippet-light-modern.html"
|
||||
title="Code Snippet - Light Modern"
|
||||
description="VS Code workbench with per-character typing animation in the Light Modern theme. Full editor chrome with activity bar, sidebar, tabs, terminal, and status bar."
|
||||
variables={[]}
|
||||
meta={{"duration":11,"width":1920,"height":1080,"category":"Code Snippets","badge":"Stable","codeLines":1088}}
|
||||
attribution={{"path":"registry/blocks/code-snippet-light-modern","tags":["code","developer","showcase","vscode"]}}
|
||||
hasCode
|
||||
rawUrl="https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/code-snippet-light-modern/code-snippet-light-modern.html"
|
||||
>
|
||||
|
||||
<InstallCommand command="npx hyperframes add code-snippet-light-modern" item="code-snippet-light-modern" />
|
||||
<CatalogSlot slot="code">
|
||||
|
||||
That writes `compositions/code-snippet-light-modern.html`, plus 1 supporting file under `assets/`.
|
||||
|
||||
<iframe
|
||||
className="w-full aspect-video rounded-xl border-0 bg-zinc-100 dark:bg-zinc-800"
|
||||
title="code-snippet-light-modern preview"
|
||||
loading="lazy"
|
||||
srcDoc={`<!doctype html><html><head><meta charset="utf-8"><style>html,body{margin:0;height:100%;overflow:hidden;background:transparent}hyperframes-player{display:block;width:100%;height:100%}</style><script src="https://cdn.jsdelivr.net/npm/@hyperframes/player@latest/dist/hyperframes-player.global.js"><\/script></head><body><script>fetch("/public/catalog/blocks/code-snippet-light-modern.json").then(function(r){return r.json()}).then(function(d){var p=document.createElement("hyperframes-player");p.setAttribute("srcdoc",d.html);p.setAttribute("controls","");p.setAttribute("autoplay","");p.setAttribute("loop","");p.setAttribute("muted","");p.setAttribute("poster","https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/code-snippet-light-modern.png");document.body.appendChild(p)});<\/script></body></html>`}
|
||||
/>
|
||||
|
||||
## Source
|
||||
|
||||
<Accordion title="code-snippet-light-modern.html">
|
||||
|
||||
```html
|
||||
```html code-snippet-light-modern.html
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
@@ -33,6 +33,7 @@ That writes `compositions/code-snippet-light-modern.html`, plus 1 supporting fil
|
||||
<body>
|
||||
<template id="vscode-light-modern-template">
|
||||
<div
|
||||
id="root"
|
||||
class="vscode-theme-scene"
|
||||
data-composition-id="vscode-light-modern"
|
||||
data-width="1920"
|
||||
@@ -160,7 +161,7 @@ That writes `compositions/code-snippet-light-modern.html`, plus 1 supporting fil
|
||||
sans-serif;
|
||||
}
|
||||
|
||||
.vscode-theme-scene {
|
||||
#root {
|
||||
--activity-bg: #181818;
|
||||
--activity-fg: #d7d7d7;
|
||||
--activity-muted: #868686;
|
||||
@@ -201,7 +202,7 @@ That writes `compositions/code-snippet-light-modern.html`, plus 1 supporting fil
|
||||
overflow: hidden;
|
||||
background:
|
||||
linear-gradient(180deg, rgba(0, 0, 0, 0.16), rgba(0, 0, 0, 0.32)),
|
||||
url("../assets/background.jpeg") center / cover no-repeat;
|
||||
url("assets/background.jpeg") center / cover no-repeat;
|
||||
color: var(--editor-fg);
|
||||
}
|
||||
|
||||
@@ -1112,13 +1113,23 @@ That writes `compositions/code-snippet-light-modern.html`, plus 1 supporting fil
|
||||
</html>
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
</CatalogSlot>
|
||||
|
||||
## Usage
|
||||
<CatalogSlot slot="install">
|
||||
|
||||
After installing, add the block to your host composition:
|
||||
<InstallCommand command="npx hyperframes add code-snippet-light-modern" item="code-snippet-light-modern" />
|
||||
|
||||
```html
|
||||
That writes `compositions/code-snippet-light-modern.html`, plus 1 supporting file under `assets/`.
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
<CatalogSlot slot="docs">
|
||||
|
||||
## Add it to your video
|
||||
|
||||
It runs for 11 seconds at 1920×1080. Paste this into your composition:
|
||||
|
||||
```html index.html
|
||||
<div
|
||||
data-composition-id="code-snippet-light-modern"
|
||||
data-composition-src="compositions/code-snippet-light-modern.html"
|
||||
@@ -1130,13 +1141,8 @@ After installing, add the block to your host composition:
|
||||
></div>
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
- Full VS Code workbench recreation — activity bar, sidebar, explorer tree, editor tabs, breadcrumbs, terminal panel, status bar
|
||||
- Per-character typing animation with cursor tracking and active line highlight
|
||||
- Colors sourced from official `microsoft/vscode` built-in theme JSON
|
||||
- 3D perspective tilt at the end of the typing sequence
|
||||
- Terminal panel with mock test output
|
||||
Move it in time with `data-start`. Put it on a different timeline row with
|
||||
`data-track-index`. See [data attributes](/concepts/data-attributes) for the rest.
|
||||
|
||||
{/* hf:generated-footer */}
|
||||
|
||||
@@ -1147,3 +1153,7 @@ Tagged `code` `developer` `showcase` `vscode`.
|
||||
- [Browse the complete Catalog](/catalog)
|
||||
- [Add assets and Catalog items in Studio](/studio/assets-and-blocks)
|
||||
- [Build a richer composition](/go-further)
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
</CatalogDetail>
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
---
|
||||
title: "Code Snippet - Light+"
|
||||
description: "VS Code workbench with per-character typing animation in the Light+ theme. Full editor chrome with activity bar, sidebar, tabs, terminal, and status bar."
|
||||
mode: "frame"
|
||||
---
|
||||
|
||||
import { CatalogDetail, CatalogSlot } from "/snippets/catalog-detail.jsx";
|
||||
import { InstallCommand } from "/snippets/install-command.jsx";
|
||||
|
||||
## Install
|
||||
<CatalogDetail
|
||||
previewSrc="/public/catalog/blocks/code-snippet-light-plus.json"
|
||||
compositionId="code-snippet-light-plus"
|
||||
compositionSrc="compositions/code-snippet-light-plus.html"
|
||||
title="Code Snippet - Light+"
|
||||
description="VS Code workbench with per-character typing animation in the Light+ theme. Full editor chrome with activity bar, sidebar, tabs, terminal, and status bar."
|
||||
variables={[]}
|
||||
meta={{"duration":11,"width":1920,"height":1080,"category":"Code Snippets","badge":"Stable","codeLines":1056}}
|
||||
attribution={{"path":"registry/blocks/code-snippet-light-plus","tags":["code","developer","showcase","vscode"]}}
|
||||
hasCode
|
||||
rawUrl="https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/code-snippet-light-plus/code-snippet-light-plus.html"
|
||||
>
|
||||
|
||||
<InstallCommand command="npx hyperframes add code-snippet-light-plus" item="code-snippet-light-plus" />
|
||||
<CatalogSlot slot="code">
|
||||
|
||||
That writes `compositions/code-snippet-light-plus.html`, plus 1 supporting file under `assets/`.
|
||||
|
||||
<iframe
|
||||
className="w-full aspect-video rounded-xl border-0 bg-zinc-100 dark:bg-zinc-800"
|
||||
title="code-snippet-light-plus preview"
|
||||
loading="lazy"
|
||||
srcDoc={`<!doctype html><html><head><meta charset="utf-8"><style>html,body{margin:0;height:100%;overflow:hidden;background:transparent}hyperframes-player{display:block;width:100%;height:100%}</style><script src="https://cdn.jsdelivr.net/npm/@hyperframes/player@latest/dist/hyperframes-player.global.js"><\/script></head><body><script>fetch("/public/catalog/blocks/code-snippet-light-plus.json").then(function(r){return r.json()}).then(function(d){var p=document.createElement("hyperframes-player");p.setAttribute("srcdoc",d.html);p.setAttribute("controls","");p.setAttribute("autoplay","");p.setAttribute("loop","");p.setAttribute("muted","");p.setAttribute("poster","https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/code-snippet-light-plus.png");document.body.appendChild(p)});<\/script></body></html>`}
|
||||
/>
|
||||
|
||||
## Source
|
||||
|
||||
<Accordion title="code-snippet-light-plus.html">
|
||||
|
||||
```html
|
||||
```html code-snippet-light-plus.html
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
@@ -33,6 +33,7 @@ That writes `compositions/code-snippet-light-plus.html`, plus 1 supporting file
|
||||
<body>
|
||||
<template id="vscode-light-plus-template">
|
||||
<div
|
||||
id="root"
|
||||
class="vscode-theme-scene"
|
||||
data-composition-id="vscode-light-plus"
|
||||
data-width="1920"
|
||||
@@ -160,7 +161,7 @@ That writes `compositions/code-snippet-light-plus.html`, plus 1 supporting file
|
||||
sans-serif;
|
||||
}
|
||||
|
||||
.vscode-theme-scene {
|
||||
#root {
|
||||
--activity-bg: #181818;
|
||||
--activity-fg: #d7d7d7;
|
||||
--activity-muted: #868686;
|
||||
@@ -201,7 +202,7 @@ That writes `compositions/code-snippet-light-plus.html`, plus 1 supporting file
|
||||
overflow: hidden;
|
||||
background:
|
||||
linear-gradient(180deg, rgba(0, 0, 0, 0.16), rgba(0, 0, 0, 0.32)),
|
||||
url("../assets/background.jpeg") center / cover no-repeat;
|
||||
url("assets/background.jpeg") center / cover no-repeat;
|
||||
color: var(--editor-fg);
|
||||
}
|
||||
|
||||
@@ -1080,13 +1081,23 @@ That writes `compositions/code-snippet-light-plus.html`, plus 1 supporting file
|
||||
</html>
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
</CatalogSlot>
|
||||
|
||||
## Usage
|
||||
<CatalogSlot slot="install">
|
||||
|
||||
After installing, add the block to your host composition:
|
||||
<InstallCommand command="npx hyperframes add code-snippet-light-plus" item="code-snippet-light-plus" />
|
||||
|
||||
```html
|
||||
That writes `compositions/code-snippet-light-plus.html`, plus 1 supporting file under `assets/`.
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
<CatalogSlot slot="docs">
|
||||
|
||||
## Add it to your video
|
||||
|
||||
It runs for 11 seconds at 1920×1080. Paste this into your composition:
|
||||
|
||||
```html index.html
|
||||
<div
|
||||
data-composition-id="code-snippet-light-plus"
|
||||
data-composition-src="compositions/code-snippet-light-plus.html"
|
||||
@@ -1098,13 +1109,8 @@ After installing, add the block to your host composition:
|
||||
></div>
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
- Full VS Code workbench recreation — activity bar, sidebar, explorer tree, editor tabs, breadcrumbs, terminal panel, status bar
|
||||
- Per-character typing animation with cursor tracking and active line highlight
|
||||
- Colors sourced from official `microsoft/vscode` built-in theme JSON
|
||||
- 3D perspective tilt at the end of the typing sequence
|
||||
- Terminal panel with mock test output
|
||||
Move it in time with `data-start`. Put it on a different timeline row with
|
||||
`data-track-index`. See [data attributes](/concepts/data-attributes) for the rest.
|
||||
|
||||
{/* hf:generated-footer */}
|
||||
|
||||
@@ -1115,3 +1121,7 @@ Tagged `code` `developer` `showcase` `vscode`.
|
||||
- [Browse the complete Catalog](/catalog)
|
||||
- [Add assets and Catalog items in Studio](/studio/assets-and-blocks)
|
||||
- [Build a richer composition](/go-further)
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
</CatalogDetail>
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
---
|
||||
title: "Code Snippet - Monokai"
|
||||
description: "VS Code workbench with per-character typing animation in the Monokai theme. Full editor chrome with activity bar, sidebar, tabs, terminal, and status bar."
|
||||
mode: "frame"
|
||||
---
|
||||
|
||||
import { CatalogDetail, CatalogSlot } from "/snippets/catalog-detail.jsx";
|
||||
import { InstallCommand } from "/snippets/install-command.jsx";
|
||||
|
||||
## Install
|
||||
<CatalogDetail
|
||||
previewSrc="/public/catalog/blocks/code-snippet-monokai.json"
|
||||
compositionId="code-snippet-monokai"
|
||||
compositionSrc="compositions/code-snippet-monokai.html"
|
||||
title="Code Snippet - Monokai"
|
||||
description="VS Code workbench with per-character typing animation in the Monokai theme. Full editor chrome with activity bar, sidebar, tabs, terminal, and status bar."
|
||||
variables={[]}
|
||||
meta={{"duration":11,"width":1920,"height":1080,"category":"Code Snippets","badge":"Stable","codeLines":1068}}
|
||||
attribution={{"path":"registry/blocks/code-snippet-monokai","tags":["code","developer","showcase","vscode"]}}
|
||||
hasCode
|
||||
rawUrl="https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/code-snippet-monokai/code-snippet-monokai.html"
|
||||
>
|
||||
|
||||
<InstallCommand command="npx hyperframes add code-snippet-monokai" item="code-snippet-monokai" />
|
||||
<CatalogSlot slot="code">
|
||||
|
||||
That writes `compositions/code-snippet-monokai.html`, plus 1 supporting file under `assets/`.
|
||||
|
||||
<iframe
|
||||
className="w-full aspect-video rounded-xl border-0 bg-zinc-100 dark:bg-zinc-800"
|
||||
title="code-snippet-monokai preview"
|
||||
loading="lazy"
|
||||
srcDoc={`<!doctype html><html><head><meta charset="utf-8"><style>html,body{margin:0;height:100%;overflow:hidden;background:transparent}hyperframes-player{display:block;width:100%;height:100%}</style><script src="https://cdn.jsdelivr.net/npm/@hyperframes/player@latest/dist/hyperframes-player.global.js"><\/script></head><body><script>fetch("/public/catalog/blocks/code-snippet-monokai.json").then(function(r){return r.json()}).then(function(d){var p=document.createElement("hyperframes-player");p.setAttribute("srcdoc",d.html);p.setAttribute("controls","");p.setAttribute("autoplay","");p.setAttribute("loop","");p.setAttribute("muted","");p.setAttribute("poster","https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/code-snippet-monokai.png");document.body.appendChild(p)});<\/script></body></html>`}
|
||||
/>
|
||||
|
||||
## Source
|
||||
|
||||
<Accordion title="code-snippet-monokai.html">
|
||||
|
||||
```html
|
||||
```html code-snippet-monokai.html
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
@@ -33,6 +33,7 @@ That writes `compositions/code-snippet-monokai.html`, plus 1 supporting file und
|
||||
<body>
|
||||
<template id="vscode-monokai-template">
|
||||
<div
|
||||
id="root"
|
||||
class="vscode-theme-scene"
|
||||
data-composition-id="vscode-monokai"
|
||||
data-width="1920"
|
||||
@@ -160,7 +161,7 @@ That writes `compositions/code-snippet-monokai.html`, plus 1 supporting file und
|
||||
sans-serif;
|
||||
}
|
||||
|
||||
.vscode-theme-scene {
|
||||
#root {
|
||||
--activity-bg: #181818;
|
||||
--activity-fg: #d7d7d7;
|
||||
--activity-muted: #868686;
|
||||
@@ -201,7 +202,7 @@ That writes `compositions/code-snippet-monokai.html`, plus 1 supporting file und
|
||||
overflow: hidden;
|
||||
background:
|
||||
linear-gradient(180deg, rgba(0, 0, 0, 0.16), rgba(0, 0, 0, 0.32)),
|
||||
url("../assets/background.jpeg") center / cover no-repeat;
|
||||
url("assets/background.jpeg") center / cover no-repeat;
|
||||
color: var(--editor-fg);
|
||||
}
|
||||
|
||||
@@ -1092,13 +1093,23 @@ That writes `compositions/code-snippet-monokai.html`, plus 1 supporting file und
|
||||
</html>
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
</CatalogSlot>
|
||||
|
||||
## Usage
|
||||
<CatalogSlot slot="install">
|
||||
|
||||
After installing, add the block to your host composition:
|
||||
<InstallCommand command="npx hyperframes add code-snippet-monokai" item="code-snippet-monokai" />
|
||||
|
||||
```html
|
||||
That writes `compositions/code-snippet-monokai.html`, plus 1 supporting file under `assets/`.
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
<CatalogSlot slot="docs">
|
||||
|
||||
## Add it to your video
|
||||
|
||||
It runs for 11 seconds at 1920×1080. Paste this into your composition:
|
||||
|
||||
```html index.html
|
||||
<div
|
||||
data-composition-id="code-snippet-monokai"
|
||||
data-composition-src="compositions/code-snippet-monokai.html"
|
||||
@@ -1110,13 +1121,8 @@ After installing, add the block to your host composition:
|
||||
></div>
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
- Full VS Code workbench recreation — activity bar, sidebar, explorer tree, editor tabs, breadcrumbs, terminal panel, status bar
|
||||
- Per-character typing animation with cursor tracking and active line highlight
|
||||
- Colors sourced from official `microsoft/vscode` built-in theme JSON
|
||||
- 3D perspective tilt at the end of the typing sequence
|
||||
- Terminal panel with mock test output
|
||||
Move it in time with `data-start`. Put it on a different timeline row with
|
||||
`data-track-index`. See [data attributes](/concepts/data-attributes) for the rest.
|
||||
|
||||
{/* hf:generated-footer */}
|
||||
|
||||
@@ -1127,3 +1133,7 @@ Tagged `code` `developer` `showcase` `vscode`.
|
||||
- [Browse the complete Catalog](/catalog)
|
||||
- [Add assets and Catalog items in Studio](/studio/assets-and-blocks)
|
||||
- [Build a richer composition](/go-further)
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
</CatalogDetail>
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
---
|
||||
title: "Code Snippet - Solarized Light"
|
||||
description: "VS Code workbench with per-character typing animation in the Solarized Light theme. Full editor chrome with activity bar, sidebar, tabs, terminal, and status bar."
|
||||
mode: "frame"
|
||||
---
|
||||
|
||||
import { CatalogDetail, CatalogSlot } from "/snippets/catalog-detail.jsx";
|
||||
import { InstallCommand } from "/snippets/install-command.jsx";
|
||||
|
||||
## Install
|
||||
<CatalogDetail
|
||||
previewSrc="/public/catalog/blocks/code-snippet-solarized-light.json"
|
||||
compositionId="code-snippet-solarized-light"
|
||||
compositionSrc="compositions/code-snippet-solarized-light.html"
|
||||
title="Code Snippet - Solarized Light"
|
||||
description="VS Code workbench with per-character typing animation in the Solarized Light theme. Full editor chrome with activity bar, sidebar, tabs, terminal, and status bar."
|
||||
variables={[]}
|
||||
meta={{"duration":11,"width":1920,"height":1080,"category":"Code Snippets","badge":"Stable","codeLines":1070}}
|
||||
attribution={{"path":"registry/blocks/code-snippet-solarized-light","tags":["code","developer","showcase","vscode"]}}
|
||||
hasCode
|
||||
rawUrl="https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/code-snippet-solarized-light/code-snippet-solarized-light.html"
|
||||
>
|
||||
|
||||
<InstallCommand command="npx hyperframes add code-snippet-solarized-light" item="code-snippet-solarized-light" />
|
||||
<CatalogSlot slot="code">
|
||||
|
||||
That writes `compositions/code-snippet-solarized-light.html`, plus 1 supporting file under `assets/`.
|
||||
|
||||
<iframe
|
||||
className="w-full aspect-video rounded-xl border-0 bg-zinc-100 dark:bg-zinc-800"
|
||||
title="code-snippet-solarized-light preview"
|
||||
loading="lazy"
|
||||
srcDoc={`<!doctype html><html><head><meta charset="utf-8"><style>html,body{margin:0;height:100%;overflow:hidden;background:transparent}hyperframes-player{display:block;width:100%;height:100%}</style><script src="https://cdn.jsdelivr.net/npm/@hyperframes/player@latest/dist/hyperframes-player.global.js"><\/script></head><body><script>fetch("/public/catalog/blocks/code-snippet-solarized-light.json").then(function(r){return r.json()}).then(function(d){var p=document.createElement("hyperframes-player");p.setAttribute("srcdoc",d.html);p.setAttribute("controls","");p.setAttribute("autoplay","");p.setAttribute("loop","");p.setAttribute("muted","");p.setAttribute("poster","https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/code-snippet-solarized-light.png");document.body.appendChild(p)});<\/script></body></html>`}
|
||||
/>
|
||||
|
||||
## Source
|
||||
|
||||
<Accordion title="code-snippet-solarized-light.html">
|
||||
|
||||
```html
|
||||
```html code-snippet-solarized-light.html
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
@@ -33,6 +33,7 @@ That writes `compositions/code-snippet-solarized-light.html`, plus 1 supporting
|
||||
<body>
|
||||
<template id="vscode-solarized-light-template">
|
||||
<div
|
||||
id="root"
|
||||
class="vscode-theme-scene"
|
||||
data-composition-id="vscode-solarized-light"
|
||||
data-width="1920"
|
||||
@@ -160,7 +161,7 @@ That writes `compositions/code-snippet-solarized-light.html`, plus 1 supporting
|
||||
sans-serif;
|
||||
}
|
||||
|
||||
.vscode-theme-scene {
|
||||
#root {
|
||||
--activity-bg: #181818;
|
||||
--activity-fg: #d7d7d7;
|
||||
--activity-muted: #868686;
|
||||
@@ -201,7 +202,7 @@ That writes `compositions/code-snippet-solarized-light.html`, plus 1 supporting
|
||||
overflow: hidden;
|
||||
background:
|
||||
linear-gradient(180deg, rgba(0, 0, 0, 0.16), rgba(0, 0, 0, 0.32)),
|
||||
url("../assets/background.jpeg") center / cover no-repeat;
|
||||
url("assets/background.jpeg") center / cover no-repeat;
|
||||
color: var(--editor-fg);
|
||||
}
|
||||
|
||||
@@ -1094,13 +1095,23 @@ That writes `compositions/code-snippet-solarized-light.html`, plus 1 supporting
|
||||
</html>
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
</CatalogSlot>
|
||||
|
||||
## Usage
|
||||
<CatalogSlot slot="install">
|
||||
|
||||
After installing, add the block to your host composition:
|
||||
<InstallCommand command="npx hyperframes add code-snippet-solarized-light" item="code-snippet-solarized-light" />
|
||||
|
||||
```html
|
||||
That writes `compositions/code-snippet-solarized-light.html`, plus 1 supporting file under `assets/`.
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
<CatalogSlot slot="docs">
|
||||
|
||||
## Add it to your video
|
||||
|
||||
It runs for 11 seconds at 1920×1080. Paste this into your composition:
|
||||
|
||||
```html index.html
|
||||
<div
|
||||
data-composition-id="code-snippet-solarized-light"
|
||||
data-composition-src="compositions/code-snippet-solarized-light.html"
|
||||
@@ -1112,13 +1123,8 @@ After installing, add the block to your host composition:
|
||||
></div>
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
- Full VS Code workbench recreation — activity bar, sidebar, explorer tree, editor tabs, breadcrumbs, terminal panel, status bar
|
||||
- Per-character typing animation with cursor tracking and active line highlight
|
||||
- Colors sourced from official `microsoft/vscode` built-in theme JSON
|
||||
- 3D perspective tilt at the end of the typing sequence
|
||||
- Terminal panel with mock test output
|
||||
Move it in time with `data-start`. Put it on a different timeline row with
|
||||
`data-track-index`. See [data attributes](/concepts/data-attributes) for the rest.
|
||||
|
||||
{/* hf:generated-footer */}
|
||||
|
||||
@@ -1129,3 +1135,7 @@ Tagged `code` `developer` `showcase` `vscode`.
|
||||
- [Browse the complete Catalog](/catalog)
|
||||
- [Add assets and Catalog items in Studio](/studio/assets-and-blocks)
|
||||
- [Build a richer composition](/go-further)
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
</CatalogDetail>
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
---
|
||||
title: "Code Snippet - Visual Studio Dark"
|
||||
description: "VS Code workbench with per-character typing animation in the Visual Studio Dark theme. Full editor chrome with activity bar, sidebar, tabs, terminal, and status bar."
|
||||
mode: "frame"
|
||||
---
|
||||
|
||||
import { CatalogDetail, CatalogSlot } from "/snippets/catalog-detail.jsx";
|
||||
import { InstallCommand } from "/snippets/install-command.jsx";
|
||||
|
||||
## Install
|
||||
<CatalogDetail
|
||||
previewSrc="/public/catalog/blocks/code-snippet-visual-studio-dark.json"
|
||||
compositionId="code-snippet-visual-studio-dark"
|
||||
compositionSrc="compositions/code-snippet-visual-studio-dark.html"
|
||||
title="Code Snippet - Visual Studio Dark"
|
||||
description="VS Code workbench with per-character typing animation in the Visual Studio Dark theme. Full editor chrome with activity bar, sidebar, tabs, terminal, and status bar."
|
||||
variables={[]}
|
||||
meta={{"duration":11,"width":1920,"height":1080,"category":"Code Snippets","badge":"Stable","codeLines":1056}}
|
||||
attribution={{"path":"registry/blocks/code-snippet-visual-studio-dark","tags":["code","developer","showcase","vscode"]}}
|
||||
hasCode
|
||||
rawUrl="https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/code-snippet-visual-studio-dark/code-snippet-visual-studio-dark.html"
|
||||
>
|
||||
|
||||
<InstallCommand command="npx hyperframes add code-snippet-visual-studio-dark" item="code-snippet-visual-studio-dark" />
|
||||
<CatalogSlot slot="code">
|
||||
|
||||
That writes `compositions/code-snippet-visual-studio-dark.html`, plus 1 supporting file under `assets/`.
|
||||
|
||||
<iframe
|
||||
className="w-full aspect-video rounded-xl border-0 bg-zinc-100 dark:bg-zinc-800"
|
||||
title="code-snippet-visual-studio-dark preview"
|
||||
loading="lazy"
|
||||
srcDoc={`<!doctype html><html><head><meta charset="utf-8"><style>html,body{margin:0;height:100%;overflow:hidden;background:transparent}hyperframes-player{display:block;width:100%;height:100%}</style><script src="https://cdn.jsdelivr.net/npm/@hyperframes/player@latest/dist/hyperframes-player.global.js"><\/script></head><body><script>fetch("/public/catalog/blocks/code-snippet-visual-studio-dark.json").then(function(r){return r.json()}).then(function(d){var p=document.createElement("hyperframes-player");p.setAttribute("srcdoc",d.html);p.setAttribute("controls","");p.setAttribute("autoplay","");p.setAttribute("loop","");p.setAttribute("muted","");p.setAttribute("poster","https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/code-snippet-visual-studio-dark.png");document.body.appendChild(p)});<\/script></body></html>`}
|
||||
/>
|
||||
|
||||
## Source
|
||||
|
||||
<Accordion title="code-snippet-visual-studio-dark.html">
|
||||
|
||||
```html
|
||||
```html code-snippet-visual-studio-dark.html
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
@@ -33,6 +33,7 @@ That writes `compositions/code-snippet-visual-studio-dark.html`, plus 1 supporti
|
||||
<body>
|
||||
<template id="vscode-visual-studio-dark-template">
|
||||
<div
|
||||
id="root"
|
||||
class="vscode-theme-scene"
|
||||
data-composition-id="vscode-visual-studio-dark"
|
||||
data-width="1920"
|
||||
@@ -160,7 +161,7 @@ That writes `compositions/code-snippet-visual-studio-dark.html`, plus 1 supporti
|
||||
sans-serif;
|
||||
}
|
||||
|
||||
.vscode-theme-scene {
|
||||
#root {
|
||||
--activity-bg: #181818;
|
||||
--activity-fg: #d7d7d7;
|
||||
--activity-muted: #868686;
|
||||
@@ -201,7 +202,7 @@ That writes `compositions/code-snippet-visual-studio-dark.html`, plus 1 supporti
|
||||
overflow: hidden;
|
||||
background:
|
||||
linear-gradient(180deg, rgba(0, 0, 0, 0.16), rgba(0, 0, 0, 0.32)),
|
||||
url("../assets/background.jpeg") center / cover no-repeat;
|
||||
url("assets/background.jpeg") center / cover no-repeat;
|
||||
color: var(--editor-fg);
|
||||
}
|
||||
|
||||
@@ -1080,13 +1081,23 @@ That writes `compositions/code-snippet-visual-studio-dark.html`, plus 1 supporti
|
||||
</html>
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
</CatalogSlot>
|
||||
|
||||
## Usage
|
||||
<CatalogSlot slot="install">
|
||||
|
||||
After installing, add the block to your host composition:
|
||||
<InstallCommand command="npx hyperframes add code-snippet-visual-studio-dark" item="code-snippet-visual-studio-dark" />
|
||||
|
||||
```html
|
||||
That writes `compositions/code-snippet-visual-studio-dark.html`, plus 1 supporting file under `assets/`.
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
<CatalogSlot slot="docs">
|
||||
|
||||
## Add it to your video
|
||||
|
||||
It runs for 11 seconds at 1920×1080. Paste this into your composition:
|
||||
|
||||
```html index.html
|
||||
<div
|
||||
data-composition-id="code-snippet-visual-studio-dark"
|
||||
data-composition-src="compositions/code-snippet-visual-studio-dark.html"
|
||||
@@ -1098,13 +1109,8 @@ After installing, add the block to your host composition:
|
||||
></div>
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
- Full VS Code workbench recreation — activity bar, sidebar, explorer tree, editor tabs, breadcrumbs, terminal panel, status bar
|
||||
- Per-character typing animation with cursor tracking and active line highlight
|
||||
- Colors sourced from official `microsoft/vscode` built-in theme JSON
|
||||
- 3D perspective tilt at the end of the typing sequence
|
||||
- Terminal panel with mock test output
|
||||
Move it in time with `data-start`. Put it on a different timeline row with
|
||||
`data-track-index`. See [data attributes](/concepts/data-attributes) for the rest.
|
||||
|
||||
{/* hf:generated-footer */}
|
||||
|
||||
@@ -1115,3 +1121,7 @@ Tagged `code` `developer` `showcase` `vscode`.
|
||||
- [Browse the complete Catalog](/catalog)
|
||||
- [Add assets and Catalog items in Studio](/studio/assets-and-blocks)
|
||||
- [Build a richer composition](/go-further)
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
</CatalogDetail>
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
---
|
||||
title: "Code Snippet - Visual Studio Light"
|
||||
description: "VS Code workbench with per-character typing animation in the Visual Studio Light theme. Full editor chrome with activity bar, sidebar, tabs, terminal, and status bar."
|
||||
mode: "frame"
|
||||
---
|
||||
|
||||
import { CatalogDetail, CatalogSlot } from "/snippets/catalog-detail.jsx";
|
||||
import { InstallCommand } from "/snippets/install-command.jsx";
|
||||
|
||||
## Install
|
||||
<CatalogDetail
|
||||
previewSrc="/public/catalog/blocks/code-snippet-visual-studio-light.json"
|
||||
compositionId="code-snippet-visual-studio-light"
|
||||
compositionSrc="compositions/code-snippet-visual-studio-light.html"
|
||||
title="Code Snippet - Visual Studio Light"
|
||||
description="VS Code workbench with per-character typing animation in the Visual Studio Light theme. Full editor chrome with activity bar, sidebar, tabs, terminal, and status bar."
|
||||
variables={[]}
|
||||
meta={{"duration":11,"width":1920,"height":1080,"category":"Code Snippets","badge":"Stable","codeLines":1056}}
|
||||
attribution={{"path":"registry/blocks/code-snippet-visual-studio-light","tags":["code","developer","showcase","vscode"]}}
|
||||
hasCode
|
||||
rawUrl="https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/code-snippet-visual-studio-light/code-snippet-visual-studio-light.html"
|
||||
>
|
||||
|
||||
<InstallCommand command="npx hyperframes add code-snippet-visual-studio-light" item="code-snippet-visual-studio-light" />
|
||||
<CatalogSlot slot="code">
|
||||
|
||||
That writes `compositions/code-snippet-visual-studio-light.html`, plus 1 supporting file under `assets/`.
|
||||
|
||||
<iframe
|
||||
className="w-full aspect-video rounded-xl border-0 bg-zinc-100 dark:bg-zinc-800"
|
||||
title="code-snippet-visual-studio-light preview"
|
||||
loading="lazy"
|
||||
srcDoc={`<!doctype html><html><head><meta charset="utf-8"><style>html,body{margin:0;height:100%;overflow:hidden;background:transparent}hyperframes-player{display:block;width:100%;height:100%}</style><script src="https://cdn.jsdelivr.net/npm/@hyperframes/player@latest/dist/hyperframes-player.global.js"><\/script></head><body><script>fetch("/public/catalog/blocks/code-snippet-visual-studio-light.json").then(function(r){return r.json()}).then(function(d){var p=document.createElement("hyperframes-player");p.setAttribute("srcdoc",d.html);p.setAttribute("controls","");p.setAttribute("autoplay","");p.setAttribute("loop","");p.setAttribute("muted","");p.setAttribute("poster","https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/code-snippet-visual-studio-light.png");document.body.appendChild(p)});<\/script></body></html>`}
|
||||
/>
|
||||
|
||||
## Source
|
||||
|
||||
<Accordion title="code-snippet-visual-studio-light.html">
|
||||
|
||||
```html
|
||||
```html code-snippet-visual-studio-light.html
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
@@ -33,6 +33,7 @@ That writes `compositions/code-snippet-visual-studio-light.html`, plus 1 support
|
||||
<body>
|
||||
<template id="vscode-visual-studio-light-template">
|
||||
<div
|
||||
id="root"
|
||||
class="vscode-theme-scene"
|
||||
data-composition-id="vscode-visual-studio-light"
|
||||
data-width="1920"
|
||||
@@ -160,7 +161,7 @@ That writes `compositions/code-snippet-visual-studio-light.html`, plus 1 support
|
||||
sans-serif;
|
||||
}
|
||||
|
||||
.vscode-theme-scene {
|
||||
#root {
|
||||
--activity-bg: #181818;
|
||||
--activity-fg: #d7d7d7;
|
||||
--activity-muted: #868686;
|
||||
@@ -201,7 +202,7 @@ That writes `compositions/code-snippet-visual-studio-light.html`, plus 1 support
|
||||
overflow: hidden;
|
||||
background:
|
||||
linear-gradient(180deg, rgba(0, 0, 0, 0.16), rgba(0, 0, 0, 0.32)),
|
||||
url("../assets/background.jpeg") center / cover no-repeat;
|
||||
url("assets/background.jpeg") center / cover no-repeat;
|
||||
color: var(--editor-fg);
|
||||
}
|
||||
|
||||
@@ -1080,13 +1081,23 @@ That writes `compositions/code-snippet-visual-studio-light.html`, plus 1 support
|
||||
</html>
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
</CatalogSlot>
|
||||
|
||||
## Usage
|
||||
<CatalogSlot slot="install">
|
||||
|
||||
After installing, add the block to your host composition:
|
||||
<InstallCommand command="npx hyperframes add code-snippet-visual-studio-light" item="code-snippet-visual-studio-light" />
|
||||
|
||||
```html
|
||||
That writes `compositions/code-snippet-visual-studio-light.html`, plus 1 supporting file under `assets/`.
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
<CatalogSlot slot="docs">
|
||||
|
||||
## Add it to your video
|
||||
|
||||
It runs for 11 seconds at 1920×1080. Paste this into your composition:
|
||||
|
||||
```html index.html
|
||||
<div
|
||||
data-composition-id="code-snippet-visual-studio-light"
|
||||
data-composition-src="compositions/code-snippet-visual-studio-light.html"
|
||||
@@ -1098,13 +1109,8 @@ After installing, add the block to your host composition:
|
||||
></div>
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
- Full VS Code workbench recreation — activity bar, sidebar, explorer tree, editor tabs, breadcrumbs, terminal panel, status bar
|
||||
- Per-character typing animation with cursor tracking and active line highlight
|
||||
- Colors sourced from official `microsoft/vscode` built-in theme JSON
|
||||
- 3D perspective tilt at the end of the typing sequence
|
||||
- Terminal panel with mock test output
|
||||
Move it in time with `data-start`. Put it on a different timeline row with
|
||||
`data-track-index`. See [data attributes](/concepts/data-attributes) for the rest.
|
||||
|
||||
{/* hf:generated-footer */}
|
||||
|
||||
@@ -1115,3 +1121,7 @@ Tagged `code` `developer` `showcase` `vscode`.
|
||||
- [Browse the complete Catalog](/catalog)
|
||||
- [Add assets and Catalog items in Studio](/studio/assets-and-blocks)
|
||||
- [Build a richer composition](/go-further)
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
</CatalogDetail>
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
---
|
||||
title: "Code Typing"
|
||||
description: "Token-streamed typing reveal with a caret that tracks the frontier — deterministic, no CSS animation."
|
||||
mode: "frame"
|
||||
---
|
||||
|
||||
import { CatalogDetail, CatalogSlot } from "/snippets/catalog-detail.jsx";
|
||||
import { InstallCommand } from "/snippets/install-command.jsx";
|
||||
|
||||
## Install
|
||||
<CatalogDetail
|
||||
previewSrc="/public/catalog/blocks/code-typing.json"
|
||||
compositionId="code-typing"
|
||||
compositionSrc="compositions/code-typing.html"
|
||||
title="Code Typing"
|
||||
description="Token-streamed typing reveal with a caret that tracks the frontier — deterministic, no CSS animation."
|
||||
variables={[]}
|
||||
meta={{"duration":5,"width":1920,"height":1080,"category":"Code Animations","badge":"Stable","codeLines":763}}
|
||||
attribution={{"path":"registry/blocks/code-typing","tags":["code","code-animation","typing","developer"]}}
|
||||
hasCode
|
||||
rawUrl="https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/code-typing/code-typing.html"
|
||||
>
|
||||
|
||||
<InstallCommand command="npx hyperframes add code-typing" item="code-typing" />
|
||||
<CatalogSlot slot="code">
|
||||
|
||||
That writes one file: `compositions/code-typing.html`.
|
||||
|
||||
<iframe
|
||||
className="w-full aspect-video rounded-xl border-0 bg-zinc-100 dark:bg-zinc-800"
|
||||
title="code-typing preview"
|
||||
loading="lazy"
|
||||
srcDoc={`<!doctype html><html><head><meta charset="utf-8"><style>html,body{margin:0;height:100%;overflow:hidden;background:transparent}hyperframes-player{display:block;width:100%;height:100%}</style><script src="https://cdn.jsdelivr.net/npm/@hyperframes/player@latest/dist/hyperframes-player.global.js"><\/script></head><body><script>fetch("/public/catalog/blocks/code-typing.json").then(function(r){return r.json()}).then(function(d){var p=document.createElement("hyperframes-player");p.setAttribute("srcdoc",d.html);p.setAttribute("controls","");p.setAttribute("autoplay","");p.setAttribute("loop","");p.setAttribute("muted","");p.setAttribute("poster","https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/code-typing.png");document.body.appendChild(p)});<\/script></body></html>`}
|
||||
/>
|
||||
|
||||
## Source
|
||||
|
||||
<Accordion title="code-typing.html">
|
||||
|
||||
```html
|
||||
```html code-typing.html
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
@@ -788,16 +788,37 @@ That writes one file: `compositions/code-typing.html`.
|
||||
</html>
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
</CatalogSlot>
|
||||
|
||||
## Usage
|
||||
<CatalogSlot slot="install">
|
||||
|
||||
After installing, add the block to your host composition:
|
||||
<InstallCommand command="npx hyperframes add code-typing" item="code-typing" />
|
||||
|
||||
```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>
|
||||
That writes one file: `compositions/code-typing.html`.
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
<CatalogSlot slot="docs">
|
||||
|
||||
## Add it to your video
|
||||
|
||||
It runs for 5 seconds at 1920×1080. Paste this into your composition:
|
||||
|
||||
```html index.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>
|
||||
```
|
||||
|
||||
Move it in time with `data-start`. Put it on a different timeline row with
|
||||
`data-track-index`. See [data attributes](/concepts/data-attributes) for the rest.
|
||||
|
||||
{/* hf:generated-footer */}
|
||||
|
||||
Tagged `code` `code-animation` `typing` `developer`.
|
||||
@@ -807,3 +828,7 @@ Tagged `code` `code-animation` `typing` `developer`.
|
||||
- [Browse the complete Catalog](/catalog)
|
||||
- [Add assets and Catalog items in Studio](/studio/assets-and-blocks)
|
||||
- [Build a richer composition](/go-further)
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
</CatalogDetail>
|
||||
|
||||
@@ -1,24 +1,27 @@
|
||||
---
|
||||
title: "Cosmic Orb"
|
||||
description: "An iridescent soap-bubble planet: a slowly spinning sphere of nebula bands, dust lanes and twinkling stars wrapped in a refracting glass limb with chromatic aberration at the edge."
|
||||
mode: "frame"
|
||||
---
|
||||
|
||||
import { CatalogDetail, CatalogSlot } from "/snippets/catalog-detail.jsx";
|
||||
import { InstallCommand } from "/snippets/install-command.jsx";
|
||||
import { VariablesExplorer } from "/snippets/variables-explorer.jsx";
|
||||
|
||||
## Install
|
||||
|
||||
<InstallCommand command="npx hyperframes add cosmic-orb" item="cosmic-orb" />
|
||||
|
||||
That writes one file: `compositions/cosmic-orb.html`.
|
||||
|
||||
<VariablesExplorer
|
||||
<CatalogDetail
|
||||
previewSrc="/public/catalog/blocks/cosmic-orb.json"
|
||||
compositionId="cosmic-orb"
|
||||
compositionSrc="compositions/cosmic-orb.html"
|
||||
title="Cosmic Orb"
|
||||
description="An iridescent soap-bubble planet: a slowly spinning sphere of nebula bands, dust lanes and twinkling stars wrapped in a refracting glass limb with chromatic aberration at the edge."
|
||||
variables={[{"id":"hue","type":"number","label":"Nebula hue","default":268,"min":0,"max":360,"step":1,"unit":"deg"},{"id":"accent","type":"number","label":"Accent hue","default":196,"min":0,"max":360,"step":1,"unit":"deg"},{"id":"spin","type":"number","label":"Spin rate","default":0.035,"min":0,"max":0.5,"step":0.005,"unit":"turns/s"},{"id":"stars","type":"number","label":"Star density","default":1,"min":0,"max":2,"step":0.05},{"id":"glow","type":"number","label":"Glow intensity","default":1,"min":0,"max":2,"step":0.05},{"id":"size","type":"number","label":"Orb size","default":0.86,"min":0.2,"max":1.2,"step":0.01},{"id":"pulse","type":"number","label":"Beat pulse","default":0,"min":0,"max":1,"step":0.01},{"id":"pulseEnvelope","type":"string","label":"Beat pulse envelope (comma-separated 0-1 samples)","default":"","placeholder":"0,0.2,1,0.4,0"},{"id":"backdrop","type":"color","label":"Backdrop","default":"#04040a"}]}
|
||||
meta={{"duration":10,"width":1920,"height":1080,"category":"Showcases","badge":"Experimental","codeLines":431}}
|
||||
attribution={{"path":"registry/blocks/cosmic-orb","tags":["webgl","shader","background","showcase","3d"]}}
|
||||
hasCode
|
||||
rawUrl="https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/cosmic-orb/cosmic-orb.html"
|
||||
>
|
||||
|
||||
<CatalogSlot slot="code">
|
||||
|
||||
```html cosmic-orb.html
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
@@ -453,7 +456,17 @@ That writes one file: `compositions/cosmic-orb.html`.
|
||||
</html>
|
||||
```
|
||||
|
||||
</VariablesExplorer>
|
||||
</CatalogSlot>
|
||||
|
||||
<CatalogSlot slot="install">
|
||||
|
||||
<InstallCommand command="npx hyperframes add cosmic-orb" item="cosmic-orb" />
|
||||
|
||||
That writes one file: `compositions/cosmic-orb.html`.
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
<CatalogSlot slot="docs">
|
||||
|
||||
## Add it to your video
|
||||
|
||||
@@ -516,446 +529,6 @@ defaults, so this behaves exactly like the preview above until you change one:
|
||||
></div>
|
||||
```
|
||||
|
||||
## Source
|
||||
|
||||
<Accordion title="cosmic-orb.html">
|
||||
|
||||
```html
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=1920, height=1080" />
|
||||
<title>Cosmic Orb</title>
|
||||
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
|
||||
<style>
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
body {
|
||||
background: #000;
|
||||
overflow: hidden;
|
||||
}
|
||||
#co-root {
|
||||
position: relative;
|
||||
width: 1920px;
|
||||
height: 1080px;
|
||||
overflow: hidden;
|
||||
}
|
||||
#co-backdrop {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: #04040a;
|
||||
}
|
||||
#co-canvas {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 1920px;
|
||||
height: 1080px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div
|
||||
id="co-root"
|
||||
data-composition-id="cosmic-orb"
|
||||
data-root="true"
|
||||
data-width="1920"
|
||||
data-height="1080"
|
||||
data-start="0"
|
||||
data-duration="10"
|
||||
data-composition-variables='[
|
||||
{"id":"hue","type":"number","label":"Nebula hue","default":268,"min":0,"max":360,"step":1,"unit":"deg"},
|
||||
{"id":"accent","type":"number","label":"Accent hue","default":196,"min":0,"max":360,"step":1,"unit":"deg"},
|
||||
{"id":"spin","type":"number","label":"Spin rate","default":0.035,"min":0,"max":0.5,"step":0.005,"unit":"turns/s"},
|
||||
{"id":"stars","type":"number","label":"Star density","default":1,"min":0,"max":2,"step":0.05},
|
||||
{"id":"glow","type":"number","label":"Glow intensity","default":1,"min":0,"max":2,"step":0.05},
|
||||
{"id":"size","type":"number","label":"Orb size","default":0.86,"min":0.2,"max":1.2,"step":0.01},
|
||||
{"id":"pulse","type":"number","label":"Beat pulse","default":0,"min":0,"max":1,"step":0.01},
|
||||
{"id":"pulseEnvelope","type":"string","label":"Beat pulse envelope (comma-separated 0-1 samples)","default":"","placeholder":"0,0.2,1,0.4,0"},
|
||||
{"id":"backdrop","type":"color","label":"Backdrop","default":"#04040a"}
|
||||
]'
|
||||
>
|
||||
<div id="co-backdrop"></div>
|
||||
<canvas id="co-canvas" width="1920" height="1080"></canvas>
|
||||
|
||||
<!-- Driver clip: gives HyperFrames a timed element to own on track 0. -->
|
||||
<div
|
||||
id="co-drv"
|
||||
class="clip"
|
||||
data-start="0"
|
||||
data-duration="10"
|
||||
data-track-index="0"
|
||||
style="position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none"
|
||||
></div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function () {
|
||||
var DUR = 10;
|
||||
var W = 1920;
|
||||
var H = 1080;
|
||||
|
||||
var ROOT = document.getElementById("co-root");
|
||||
var CS = getComputedStyle(ROOT);
|
||||
|
||||
// `data-composition-variables` is the single owner of every declared
|
||||
// default — parse it rather than repeating each default a second time
|
||||
// in JS. `window.__hyperframes` is NOT guaranteed to exist yet when this
|
||||
// inline script runs, so it can only ever be an override, never the
|
||||
// source. Precedence: host CSS custom property, runtime variable bag,
|
||||
// declared default.
|
||||
var DECL = {};
|
||||
JSON.parse(ROOT.getAttribute("data-composition-variables") || "[]").forEach(function (v) {
|
||||
DECL[v.id] = v.default;
|
||||
});
|
||||
var HF = window.__hyperframes;
|
||||
var V = (HF && HF.getVariables && HF.getVariables()) || {};
|
||||
|
||||
function raw(id) {
|
||||
var css = CS.getPropertyValue("--" + id.toLowerCase()).trim();
|
||||
if (css !== "") return css;
|
||||
if (V[id] !== undefined && V[id] !== "") return V[id];
|
||||
return DECL[id];
|
||||
}
|
||||
function num(id) {
|
||||
var n = parseFloat(raw(id));
|
||||
return isFinite(n) ? n : 0;
|
||||
}
|
||||
function str(id) {
|
||||
var s = raw(id);
|
||||
return typeof s === "string" ? s.trim() : "";
|
||||
}
|
||||
|
||||
var HUE = num("hue");
|
||||
var ACCENT = num("accent");
|
||||
var SPIN = num("spin");
|
||||
var STARS = num("stars");
|
||||
var GLOW = num("glow");
|
||||
var SIZE = num("size");
|
||||
var PULSE = num("pulse");
|
||||
var BACKDROP = str("backdrop");
|
||||
|
||||
document.getElementById("co-backdrop").style.background = BACKDROP;
|
||||
|
||||
// Beat hook. A precomputed envelope (comma-separated 0-1 samples spread
|
||||
// evenly across the clip) is sampled closed-form at time t — no audio
|
||||
// analysis, no accumulator. Empty envelope => the flat `pulse` value.
|
||||
var ENV = str("pulseEnvelope")
|
||||
.split(",")
|
||||
.map(function (s) {
|
||||
return parseFloat(s);
|
||||
})
|
||||
.filter(function (n) {
|
||||
return isFinite(n);
|
||||
});
|
||||
|
||||
function pulseAt(t) {
|
||||
if (ENV.length === 0) return PULSE;
|
||||
if (ENV.length === 1) return ENV[0];
|
||||
var x = Math.min(1, Math.max(0, t / DUR)) * (ENV.length - 1);
|
||||
var i = Math.floor(x);
|
||||
var j = Math.min(i + 1, ENV.length - 1);
|
||||
return ENV[i] + (ENV[j] - ENV[i]) * (x - i);
|
||||
}
|
||||
|
||||
var canvas = document.getElementById("co-canvas");
|
||||
var gl =
|
||||
canvas.getContext("webgl", {
|
||||
alpha: true,
|
||||
antialias: false,
|
||||
depth: false,
|
||||
stencil: false,
|
||||
preserveDrawingBuffer: true,
|
||||
powerPreference: "high-performance",
|
||||
}) ||
|
||||
canvas.getContext("experimental-webgl", {
|
||||
alpha: true,
|
||||
preserveDrawingBuffer: true,
|
||||
});
|
||||
|
||||
var VERT = [
|
||||
"attribute vec2 aPos;",
|
||||
"void main() { gl_Position = vec4(aPos, 0.0, 1.0); }",
|
||||
].join("\n");
|
||||
|
||||
var FRAG = [
|
||||
"precision highp float;",
|
||||
"uniform vec2 uRes;",
|
||||
"uniform float uTime;",
|
||||
"uniform float uRadius;",
|
||||
"uniform float uSpin;",
|
||||
"uniform float uHue;",
|
||||
"uniform float uAccent;",
|
||||
"uniform float uStars;",
|
||||
"uniform float uGlow;",
|
||||
"uniform float uPulse;",
|
||||
"const float TAU = 6.28318531;",
|
||||
"",
|
||||
"// One cheap hash for every random-looking thing in here.",
|
||||
"float h1(float x) { return fract(sin(x * 127.1) * 43758.5453); }",
|
||||
"float h2(vec2 p) { return h1(dot(p, vec2(1.0, 157.31))); }",
|
||||
"",
|
||||
"vec3 hue2rgb(float h) {",
|
||||
" return clamp(abs(mod(h * 6.0 + vec3(0.0, 4.0, 2.0), 6.0) - 3.0) - 1.0, 0.0, 1.0);",
|
||||
"}",
|
||||
"",
|
||||
"vec3 rotX(vec3 p, float a) { float c = cos(a), s = sin(a); return vec3(p.x, c * p.y - s * p.z, s * p.y + c * p.z); }",
|
||||
"vec3 rotY(vec3 p, float a) { float c = cos(a), s = sin(a); return vec3(c * p.x + s * p.z, p.y, -s * p.x + c * p.z); }",
|
||||
"vec3 rotZ(vec3 p, float a) { float c = cos(a), s = sin(a); return vec3(c * p.x - s * p.y, s * p.x + c * p.y, p.z); }",
|
||||
"",
|
||||
"vec3 safeDir(vec3 v) {",
|
||||
" float l = dot(v, v);",
|
||||
" if (l < 1e-6) return vec3(0.0, 0.0, -1.0);",
|
||||
" return v * inversesqrt(l);",
|
||||
"}",
|
||||
"",
|
||||
"// Fixed axial tilt, then spin about the tilted polar axis.",
|
||||
"vec3 toBody(vec3 n, float spin) { return rotY(rotZ(rotX(n, -0.34), 0.42), -spin); }",
|
||||
"vec2 lonlat(vec3 d) { return vec2(atan(d.x, d.z), asin(clamp(d.y, -1.0, 1.0))); }",
|
||||
"",
|
||||
"float dens(float base) { return clamp(1.0 - (1.0 - base) * uStars, 0.0, 1.0); }",
|
||||
"",
|
||||
"// One star size-class: cell grid, seeded jitter, soft round core.",
|
||||
"float starField(vec2 q, float freq, float base, float rad) {",
|
||||
" vec2 g = q * freq;",
|
||||
" vec2 id = floor(g);",
|
||||
" float h = h2(id);",
|
||||
" if (h < dens(base)) return 0.0;",
|
||||
" vec2 c = vec2(h2(id + vec2(3.7, 1.3)), h2(id + vec2(9.1, 5.5)));",
|
||||
" float d = length(fract(g) - c);",
|
||||
" float core = smoothstep(rad, 0.0, d);",
|
||||
" // Twinkle: a per-star phase driven straight off uTime, no accumulator.",
|
||||
" float tw = 0.62 + 0.38 * sin(uTime * 2.4 + h * 53.7);",
|
||||
" return core * core * (0.45 + 0.55 * h1(h * 91.7)) * tw;",
|
||||
"}",
|
||||
"",
|
||||
"// 4-point diffraction glare, only worth paying for on the brightest class.",
|
||||
"float starGlare(vec2 q, float freq, float base) {",
|
||||
" vec2 g = q * freq;",
|
||||
" vec2 id = floor(g);",
|
||||
" float h = h2(id);",
|
||||
" if (h < dens(base)) return 0.0;",
|
||||
" vec2 c = vec2(h2(id + vec2(3.7, 1.3)), h2(id + vec2(9.1, 5.5)));",
|
||||
" vec2 dv = abs(fract(g) - c);",
|
||||
" float hx = exp(-dv.x * 70.0) * exp(-dv.y * 9.0);",
|
||||
" float hy = exp(-dv.y * 70.0) * exp(-dv.x * 9.0);",
|
||||
" return max(hx, hy) * 0.45;",
|
||||
"}",
|
||||
"",
|
||||
"// Three size-classes at ~6 / 11 / 19 cell frequency.",
|
||||
"float starsAll(vec2 q, float gain) {",
|
||||
" float s = starField(q, 6.0, 0.958, 0.20);",
|
||||
" s += starGlare(q, 6.0, 0.958);",
|
||||
" s += starField(q, 11.0, 0.920, 0.18) * 0.95;",
|
||||
" s += starField(q, 19.0, 0.865, 0.17) * 0.75;",
|
||||
" return s * gain;",
|
||||
"}",
|
||||
"",
|
||||
"vec3 nebula(vec2 q) {",
|
||||
" // Two sin-field turbulence layers.",
|
||||
" float t1 = sin(q.x * 3.1 + sin(q.y * 2.7) * 1.9) * sin(q.y * 4.3 + sin(q.x * 1.7) * 2.3);",
|
||||
" float t2 = sin(q.x * 7.9 + sin(q.y * 5.1) * 1.3) * sin(q.y * 9.7 + sin(q.x * 4.4) * 1.1);",
|
||||
" float turb = t1 * 0.66 + t2 * 0.34;",
|
||||
"",
|
||||
" // Galaxy bands, warped by the turbulence.",
|
||||
" float band = sin(q.y * 3.2 + turb * 1.7 + q.x * 0.55);",
|
||||
" float bands = pow(max(0.0, band * 0.5 + 0.5), 3.2);",
|
||||
"",
|
||||
" // Dust lanes: broad darkened swathes, not a thin curve.",
|
||||
" float lane = sin(q.y * 2.9 + q.x * 1.3 + t1 * 1.6);",
|
||||
" float dust = 0.22 + 0.78 * smoothstep(-0.45, 0.75, lane);",
|
||||
"",
|
||||
" // Core bulge.",
|
||||
" float dc = length(vec2((q.x - 0.55) * 0.5, (q.y + 0.12) * 1.1));",
|
||||
" float core = exp(-dc * dc * 2.6);",
|
||||
"",
|
||||
" // Two pocket glows.",
|
||||
" float dA = length(vec2((q.x + 1.95) * 0.62, (q.y - 0.52) * 1.45));",
|
||||
" float dB = length(vec2((q.x - 2.35) * 0.55, (q.y + 0.78) * 1.30));",
|
||||
" float pA = exp(-dA * dA * 4.0);",
|
||||
" float pB = exp(-dB * dB * 5.0);",
|
||||
"",
|
||||
" vec3 cNeb = hue2rgb(uHue / 360.0);",
|
||||
" vec3 cAcc = hue2rgb(uAccent / 360.0);",
|
||||
" vec3 cWarm = hue2rgb(uHue / 360.0 + 0.07);",
|
||||
"",
|
||||
" vec3 col = mix(cNeb, cWarm, 0.5 + 0.5 * turb) * bands * dust * 0.17;",
|
||||
" col += mix(cNeb, vec3(1.0), 0.50) * core * dust * 0.13;",
|
||||
" col += mix(cAcc, vec3(1.0), 0.60) * pA * 0.30 * uGlow;",
|
||||
" col += mix(cNeb, vec3(1.0), 0.50) * pB * 0.24 * uGlow;",
|
||||
" return col;",
|
||||
"}",
|
||||
"",
|
||||
"void main() {",
|
||||
" vec2 p = (gl_FragCoord.xy - uRes * 0.5) / uRadius;",
|
||||
" float r = length(p);",
|
||||
" float aa = 1.4 / uRadius;",
|
||||
" float spin = TAU * uSpin * uTime;",
|
||||
"",
|
||||
" vec3 cNeb = hue2rgb(uHue / 360.0);",
|
||||
" vec3 cAcc = hue2rgb(uAccent / 360.0);",
|
||||
"",
|
||||
" float mask = smoothstep(1.0 + aa, 1.0 - aa, r);",
|
||||
" float halo = exp(-max(0.0, r - 1.0) * 13.0) * (1.0 - mask);",
|
||||
"",
|
||||
" vec3 body = vec3(0.0);",
|
||||
" if (mask > 0.001) {",
|
||||
" float rc = min(r, 1.0);",
|
||||
" // Analytic sphere: the normal falls straight out of z = sqrt(1 - r^2).",
|
||||
" float z = sqrt(max(0.0, 1.0 - rc * rc));",
|
||||
" vec3 N = vec3(p.x, p.y, z);",
|
||||
" vec2 q = lonlat(toBody(safeDir(N), spin));",
|
||||
"",
|
||||
" float gain = 0.9 + 2.6 * uPulse;",
|
||||
" body = nebula(q);",
|
||||
" body += vec3(1.0) * starsAll(q, gain) * (0.95 + 0.15 * uGlow);",
|
||||
"",
|
||||
" // Limb darkening.",
|
||||
" body *= mix(0.32, 1.0, pow(z, 0.5));",
|
||||
"",
|
||||
" // Refract-sampled back layer, split per channel: the chromatic",
|
||||
" // aberration that reads as glass at the limb.",
|
||||
" vec3 I = vec3(0.0, 0.0, -1.0);",
|
||||
" vec3 dR = safeDir(refract(I, N, 1.0 / 1.09));",
|
||||
" vec3 dG = safeDir(refract(I, N, 1.0 / 1.13));",
|
||||
" vec3 dB = safeDir(refract(I, N, 1.0 / 1.17));",
|
||||
" vec2 qR = lonlat(toBody(dR, spin));",
|
||||
" vec2 qG = lonlat(toBody(dG, spin));",
|
||||
" vec2 qB = lonlat(toBody(dB, spin));",
|
||||
" vec3 back = vec3(starsAll(qR, gain), starsAll(qG, gain), starsAll(qB, gain));",
|
||||
" float fres = pow(1.0 - z, 1.8);",
|
||||
" body += back * fres * 2.2;",
|
||||
" body += nebula(qG) * fres * 0.8;",
|
||||
"",
|
||||
" // Aurora sheen riding the glass shell.",
|
||||
" float ang = atan(p.y, p.x);",
|
||||
" float sheen = smoothstep(0.72, 0.99, r) * smoothstep(1.0, 0.90, r);",
|
||||
" float w = 0.5 + 0.5 * sin(ang * 2.0 + spin * 1.6);",
|
||||
" body += (mix(cNeb, cAcc, w) * 0.55 + vec3(0.16)) * sheen * 0.30 * uGlow;",
|
||||
"",
|
||||
" // Three speculars, fixed in screen space: the key light does not spin.",
|
||||
" vec2 a1 = p - vec2(-0.34, 0.62);",
|
||||
" vec2 a2 = p - vec2(0.58, 0.60);",
|
||||
" vec2 a3 = p - vec2(0.24, -0.80);",
|
||||
" float sp = exp(-dot(a1, a1) * 30.0) * 0.30;",
|
||||
" sp += exp(-dot(a2, a2) * 150.0) * 0.80;",
|
||||
" sp += exp(-dot(a3, a3) * 70.0) * 0.30;",
|
||||
" body += vec3(1.0) * sp * (0.30 + 0.70 * pow(1.0 - z, 1.5)) * uGlow;",
|
||||
"",
|
||||
" // Fresnel void glow hugging the inside of the rim.",
|
||||
" body += mix(cAcc, vec3(1.0), 0.4) * pow(1.0 - z, 6.0) * 0.30 * uGlow;",
|
||||
" }",
|
||||
"",
|
||||
" vec3 haloCol = mix(cNeb, cAcc, 0.35);",
|
||||
" vec3 outCol = body * mask + haloCol * halo * 0.22 * uGlow;",
|
||||
" float outA = clamp(mask + halo * 0.16 * uGlow, 0.0, 1.0);",
|
||||
" gl_FragColor = vec4(clamp(outCol, 0.0, 4.0), outA);",
|
||||
"}",
|
||||
].join("\n");
|
||||
|
||||
var uni = {};
|
||||
var ready = false;
|
||||
|
||||
function compile(type, src) {
|
||||
var sh = gl.createShader(type);
|
||||
gl.shaderSource(sh, src);
|
||||
gl.compileShader(sh);
|
||||
if (!gl.getShaderParameter(sh, gl.COMPILE_STATUS)) {
|
||||
throw new Error("cosmic-orb shader: " + gl.getShaderInfoLog(sh));
|
||||
}
|
||||
return sh;
|
||||
}
|
||||
|
||||
if (gl) {
|
||||
var prog = gl.createProgram();
|
||||
gl.attachShader(prog, compile(gl.VERTEX_SHADER, VERT));
|
||||
gl.attachShader(prog, compile(gl.FRAGMENT_SHADER, FRAG));
|
||||
gl.linkProgram(prog);
|
||||
if (!gl.getProgramParameter(prog, gl.LINK_STATUS)) {
|
||||
throw new Error("cosmic-orb link: " + gl.getProgramInfoLog(prog));
|
||||
}
|
||||
gl.useProgram(prog);
|
||||
|
||||
var buf = gl.createBuffer();
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, buf);
|
||||
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1, -1, 3, -1, -1, 3]), gl.STATIC_DRAW);
|
||||
var loc = gl.getAttribLocation(prog, "aPos");
|
||||
gl.enableVertexAttribArray(loc);
|
||||
gl.vertexAttribPointer(loc, 2, gl.FLOAT, false, 0, 0);
|
||||
|
||||
[
|
||||
"uRes",
|
||||
"uTime",
|
||||
"uRadius",
|
||||
"uSpin",
|
||||
"uHue",
|
||||
"uAccent",
|
||||
"uStars",
|
||||
"uGlow",
|
||||
"uPulse",
|
||||
].forEach(function (n) {
|
||||
uni[n] = gl.getUniformLocation(prog, n);
|
||||
});
|
||||
|
||||
gl.viewport(0, 0, W, H);
|
||||
gl.uniform2f(uni.uRes, W, H);
|
||||
gl.uniform1f(uni.uRadius, (SIZE * H) / 2);
|
||||
gl.uniform1f(uni.uSpin, SPIN);
|
||||
gl.uniform1f(uni.uHue, HUE);
|
||||
gl.uniform1f(uni.uAccent, ACCENT);
|
||||
gl.uniform1f(uni.uStars, STARS);
|
||||
gl.uniform1f(uni.uGlow, GLOW);
|
||||
ready = true;
|
||||
}
|
||||
|
||||
// Every frame is computed from t alone: spin is spin_rate * t, the
|
||||
// pulse is sampled from the envelope at t. No accumulators, no clocks.
|
||||
function draw(t) {
|
||||
if (!ready) return;
|
||||
gl.uniform1f(uni.uTime, t);
|
||||
gl.uniform1f(uni.uPulse, pulseAt(t));
|
||||
gl.clearColor(0, 0, 0, 0);
|
||||
gl.clear(gl.COLOR_BUFFER_BIT);
|
||||
gl.drawArrays(gl.TRIANGLES, 0, 3);
|
||||
gl.flush();
|
||||
}
|
||||
|
||||
window.__timelines = window.__timelines || {};
|
||||
var tl = gsap.timeline({ paused: true });
|
||||
|
||||
// The canvas is repainted from a property SETTER, not from onUpdate:
|
||||
// gsap's seek(t) suppresses events by default, so an onUpdate callback
|
||||
// silently never fires on a scrub and the canvas freezes on frame 0.
|
||||
// Tweened values are always written during render, suppressed or not,
|
||||
// so this fires on every seek — and hands us the frame time directly.
|
||||
var driver = { _t: 0 };
|
||||
Object.defineProperty(driver, "t", {
|
||||
get: function () {
|
||||
return this._t;
|
||||
},
|
||||
set: function (v) {
|
||||
this._t = v;
|
||||
draw(v);
|
||||
},
|
||||
});
|
||||
tl.to(driver, { t: DUR, duration: DUR, ease: "none" }, 0);
|
||||
window.__timelines["cosmic-orb"] = tl;
|
||||
|
||||
draw(0);
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
|
||||
{/* hf:generated-footer */}
|
||||
|
||||
Tagged `webgl` `shader` `background` `showcase` `3d`.
|
||||
@@ -965,3 +538,7 @@ Tagged `webgl` `shader` `background` `showcase` `3d`.
|
||||
- [Browse the complete Catalog](/catalog)
|
||||
- [Add assets and Catalog items in Studio](/studio/assets-and-blocks)
|
||||
- [Build a richer composition](/go-further)
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
</CatalogDetail>
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
---
|
||||
title: "Cross Warp Morph"
|
||||
description: "Shader transition with cross-warped morphing"
|
||||
mode: "frame"
|
||||
---
|
||||
|
||||
import { CatalogDetail, CatalogSlot } from "/snippets/catalog-detail.jsx";
|
||||
import { InstallCommand } from "/snippets/install-command.jsx";
|
||||
|
||||
## Install
|
||||
<CatalogDetail
|
||||
previewSrc="/public/catalog/blocks/cross-warp-morph.json"
|
||||
compositionId="cross-warp-morph"
|
||||
compositionSrc="compositions/cross-warp-morph.html"
|
||||
title="Cross Warp Morph"
|
||||
description="Shader transition with cross-warped morphing"
|
||||
variables={[]}
|
||||
meta={{"duration":4,"width":1920,"height":1080,"category":"Shader Transitions","badge":"Stable","codeLines":377}}
|
||||
attribution={{"path":"registry/blocks/cross-warp-morph","tags":["transition","shader"]}}
|
||||
hasCode
|
||||
rawUrl="https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/cross-warp-morph/cross-warp-morph.html"
|
||||
>
|
||||
|
||||
<InstallCommand command="npx hyperframes add cross-warp-morph" item="cross-warp-morph" />
|
||||
<CatalogSlot slot="code">
|
||||
|
||||
That writes one file: `compositions/cross-warp-morph.html`.
|
||||
|
||||
<iframe
|
||||
className="w-full aspect-video rounded-xl border-0 bg-zinc-100 dark:bg-zinc-800"
|
||||
title="cross-warp-morph preview"
|
||||
loading="lazy"
|
||||
srcDoc={`<!doctype html><html><head><meta charset="utf-8"><style>html,body{margin:0;height:100%;overflow:hidden;background:transparent}hyperframes-player{display:block;width:100%;height:100%}</style><script src="https://cdn.jsdelivr.net/npm/@hyperframes/player@latest/dist/hyperframes-player.global.js"><\/script></head><body><script>fetch("/public/catalog/blocks/cross-warp-morph.json").then(function(r){return r.json()}).then(function(d){var p=document.createElement("hyperframes-player");p.setAttribute("srcdoc",d.html);p.setAttribute("controls","");p.setAttribute("autoplay","");p.setAttribute("loop","");p.setAttribute("muted","");p.setAttribute("poster","https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/cross-warp-morph.png");document.body.appendChild(p)});<\/script></body></html>`}
|
||||
/>
|
||||
|
||||
## Source
|
||||
|
||||
<Accordion title="cross-warp-morph.html">
|
||||
|
||||
```html
|
||||
```html cross-warp-morph.html
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
@@ -402,16 +402,37 @@ That writes one file: `compositions/cross-warp-morph.html`.
|
||||
</html>
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
</CatalogSlot>
|
||||
|
||||
## Usage
|
||||
<CatalogSlot slot="install">
|
||||
|
||||
After installing, add the block to your host composition:
|
||||
<InstallCommand command="npx hyperframes add cross-warp-morph" item="cross-warp-morph" />
|
||||
|
||||
```html
|
||||
<div data-composition-id="cross-warp-morph" data-composition-src="compositions/cross-warp-morph.html" data-start="0" data-duration="4" data-track-index="1" data-width="1920" data-height="1080"></div>
|
||||
That writes one file: `compositions/cross-warp-morph.html`.
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
<CatalogSlot slot="docs">
|
||||
|
||||
## Add it to your video
|
||||
|
||||
It runs for 4 seconds at 1920×1080. Paste this into your composition:
|
||||
|
||||
```html index.html
|
||||
<div
|
||||
data-composition-id="cross-warp-morph"
|
||||
data-composition-src="compositions/cross-warp-morph.html"
|
||||
data-start="0"
|
||||
data-duration="4"
|
||||
data-track-index="1"
|
||||
data-width="1920"
|
||||
data-height="1080"
|
||||
></div>
|
||||
```
|
||||
|
||||
Move it in time with `data-start`. Put it on a different timeline row with
|
||||
`data-track-index`. See [data attributes](/concepts/data-attributes) for the rest.
|
||||
|
||||
{/* hf:generated-footer */}
|
||||
|
||||
Tagged `transition` `shader`.
|
||||
@@ -421,3 +442,7 @@ Tagged `transition` `shader`.
|
||||
- [Browse the complete Catalog](/catalog)
|
||||
- [Add assets and Catalog items in Studio](/studio/assets-and-blocks)
|
||||
- [Build a richer composition](/go-further)
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
</CatalogDetail>
|
||||
|
||||
@@ -0,0 +1,944 @@
|
||||
---
|
||||
title: "Cuboid Carousel"
|
||||
description: "A chain of bevelled cuboids rides a travelling wave as a content carousel."
|
||||
mode: "frame"
|
||||
---
|
||||
|
||||
import { CatalogDetail, CatalogSlot } from "/snippets/catalog-detail.jsx";
|
||||
import { InstallCommand } from "/snippets/install-command.jsx";
|
||||
|
||||
<CatalogDetail
|
||||
previewSrc="/public/catalog/blocks/cuboid-carousel.json"
|
||||
compositionId="cuboid-carousel"
|
||||
compositionSrc="compositions/cuboid-carousel/cuboid-carousel.html"
|
||||
title="Cuboid Carousel"
|
||||
description="A chain of bevelled cuboids rides a travelling wave as a content carousel."
|
||||
variables={[{"id":"cardsJson","type":"string","label":"Content - Cards JSON (optional, see CARDS in source)","default":""},{"id":"heroCard","type":"number","label":"Content - Hero card (1-based)","default":4,"min":1,"max":12,"step":1},{"id":"count","type":"number","label":"Content - Repeating card count","default":8,"min":1,"max":12,"step":1},{"id":"gap","type":"number","label":"Layout - Extra gap between cuboids","default":0.01,"min":0,"max":1.5,"step":0.005},{"id":"cuboidWidth","type":"number","label":"Cuboid - Width","default":1.7,"min":0.3,"max":5,"step":0.01},{"id":"cuboidHeight","type":"number","label":"Cuboid - Height","default":2.4,"min":0.3,"max":6,"step":0.01},{"id":"cuboidDepth","type":"number","label":"Cuboid - Thickness (depth)","default":0.14,"min":0.02,"max":2,"step":0.01},{"id":"cornerRadius","type":"number","label":"Cuboid - Corner radius","default":0.105,"min":0,"max":0.6,"step":0.005},{"id":"bevelSize","type":"number","label":"Cuboid - Bevel size (edge chamfer)","default":0.03,"min":0,"max":0.3,"step":0.001},{"id":"bevelThickness","type":"number","label":"Cuboid - Bevel thickness","default":0.04,"min":0,"max":0.3,"step":0.001},{"id":"smoothness","type":"number","label":"Cuboid - Smoothness (segments)","default":32,"min":1,"max":32,"step":1},{"id":"bodyColor","type":"color","label":"Cuboid - Body colour","default":"#242424"},{"id":"roughness","type":"number","label":"Cuboid - Roughness","default":0.6,"min":0,"max":1,"step":0.01},{"id":"metalness","type":"number","label":"Cuboid - Metalness","default":0.4,"min":0,"max":1,"step":0.01},{"id":"heroScale","type":"number","label":"Hero - Isolated scale","default":1,"min":1,"max":1.6,"step":0.01},{"id":"heroFillIntensity","type":"number","label":"Hero - Face light intensity","default":0.55,"min":0,"max":2,"step":0.05,"description":"A soft fill on the detached face keeps its text readable without changing the original chain lighting."},{"id":"heroExitX","type":"number","label":"Hero - Exit X rotation degrees","default":288,"min":-720,"max":720,"step":1},{"id":"heroExitY","type":"number","label":"Hero - Exit Y rotation degrees","default":-234,"min":-720,"max":720,"step":1},{"id":"heroExitZ","type":"number","label":"Hero - Exit Z rotation degrees","default":198,"min":-720,"max":720,"step":1},{"id":"ambientIntensity","type":"number","label":"Lights - Ambient intensity","default":0.35,"min":0,"max":4,"step":0.01},{"id":"ambientColor","type":"color","label":"Lights - Ambient colour","default":"#ffffff"},{"id":"topIntensity","type":"number","label":"Lights - Top rig intensity","default":3.1,"min":0,"max":12,"step":0.05},{"id":"topColor","type":"color","label":"Lights - Top rig colour","default":"#ffffff"},{"id":"bottomIntensity","type":"number","label":"Lights - Bottom rig intensity","default":2.15,"min":0,"max":12,"step":0.05},{"id":"bottomColor","type":"color","label":"Lights - Bottom rig colour","default":"#ffcaad"},{"id":"keyIntensity","type":"number","label":"Lights - Key rig intensity","default":5,"min":0,"max":12,"step":0.05},{"id":"keyColor","type":"color","label":"Lights - Key rig colour","default":"#ffe9d6"},{"id":"shadowRadius","type":"number","label":"Shadows - Softness at map size 2048","default":44.5,"min":0,"max":50,"step":0.5},{"id":"shadowMapSize","type":"number","label":"Shadows - Map size","default":2048,"min":512,"max":4096,"step":256},{"id":"backdrop","type":"color","label":"Background - Colour","default":"#0b0d13"},{"id":"cameraFov","type":"number","label":"Camera - Initial Field of view","default":11,"min":10,"max":90,"step":0.5},{"id":"cameraX","type":"number","label":"Camera - Initial X","default":-1.65,"min":-20,"max":20,"step":0.05},{"id":"cameraY","type":"number","label":"Camera - Initial Y","default":8.5,"min":-20,"max":20,"step":0.05},{"id":"cameraZ","type":"number","label":"Camera - Initial Z (distance)","default":-8.508708,"min":-20,"max":50,"step":0.1},{"id":"cameraSettleFrame","type":"number","label":"Camera - Settle frame (30 fps)","default":70,"min":20,"max":90,"step":1,"description":"Fast-start ease-out along the saved route. Approved value: 70."},{"id":"handheldStrength","type":"number","label":"Camera - Handheld hold strength","default":1,"min":0,"max":1,"step":0.05,"description":"Camera-only motion during frames 90–150; 0 disables it, 1 matches Blender."},{"id":"cameraTargetFov","type":"number","label":"Camera target - Field of view","default":34,"min":10,"max":90,"step":0.5},{"id":"cameraTargetX","type":"number","label":"Camera target - X","default":-7.25,"min":-20,"max":20,"step":0.05},{"id":"cameraTargetY","type":"number","label":"Camera target - Y","default":1.15,"min":-20,"max":20,"step":0.05},{"id":"cameraTargetZ","type":"number","label":"Camera target - Z","default":6.2,"min":2,"max":50,"step":0.1}]}
|
||||
meta={{"duration":6.67,"width":1920,"height":1080,"category":"3D motion","badge":"Stable","codeLines":873}}
|
||||
attribution={{"path":"registry/blocks/cuboid-carousel","tags":["3d-motion","carousel","cards","travelling-wave","pbr","shadows","room-environment"]}}
|
||||
hasCode
|
||||
rawUrl="https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/cuboid-carousel/cuboid-carousel.html"
|
||||
>
|
||||
|
||||
<CatalogSlot slot="code">
|
||||
|
||||
```html cuboid-carousel.html
|
||||
<!doctype html>
|
||||
<html
|
||||
lang="en"
|
||||
data-composition-variables='[{"id":"cardsJson","type":"string","label":"Content - Cards JSON (optional, see CARDS in source)","default":""},{"id":"heroCard","type":"number","label":"Content - Hero card (1-based)","default":4,"min":1,"max":12,"step":1},{"id":"count","type":"number","label":"Content - Repeating card count","default":8,"min":1,"max":12,"step":1},{"id":"gap","type":"number","label":"Layout - Extra gap between cuboids","default":0.01,"min":0,"max":1.5,"step":0.005},{"id":"cuboidWidth","type":"number","label":"Cuboid - Width","default":1.7,"min":0.3,"max":5,"step":0.01},{"id":"cuboidHeight","type":"number","label":"Cuboid - Height","default":2.4,"min":0.3,"max":6,"step":0.01},{"id":"cuboidDepth","type":"number","label":"Cuboid - Thickness (depth)","default":0.14,"min":0.02,"max":2,"step":0.01},{"id":"cornerRadius","type":"number","label":"Cuboid - Corner radius","default":0.105,"min":0,"max":0.6,"step":0.005},{"id":"bevelSize","type":"number","label":"Cuboid - Bevel size (edge chamfer)","default":0.03,"min":0,"max":0.3,"step":0.001},{"id":"bevelThickness","type":"number","label":"Cuboid - Bevel thickness","default":0.04,"min":0,"max":0.3,"step":0.001},{"id":"smoothness","type":"number","label":"Cuboid - Smoothness (segments)","default":32,"min":1,"max":32,"step":1},{"id":"bodyColor","type":"color","label":"Cuboid - Body colour","default":"#242424"},{"id":"roughness","type":"number","label":"Cuboid - Roughness","default":0.6,"min":0,"max":1,"step":0.01},{"id":"metalness","type":"number","label":"Cuboid - Metalness","default":0.4,"min":0,"max":1,"step":0.01},{"id":"heroScale","type":"number","label":"Hero - Isolated scale","default":1,"min":1,"max":1.6,"step":0.01},{"id":"heroFillIntensity","type":"number","label":"Hero - Face light intensity","default":0.55,"min":0,"max":2,"step":0.05,"description":"A soft fill on the detached face keeps its text readable without changing the original chain lighting."},{"id":"heroExitX","type":"number","label":"Hero - Exit X rotation degrees","default":288,"min":-720,"max":720,"step":1},{"id":"heroExitY","type":"number","label":"Hero - Exit Y rotation degrees","default":-234,"min":-720,"max":720,"step":1},{"id":"heroExitZ","type":"number","label":"Hero - Exit Z rotation degrees","default":198,"min":-720,"max":720,"step":1},{"id":"ambientIntensity","type":"number","label":"Lights - Ambient intensity","default":0.35,"min":0,"max":4,"step":0.01},{"id":"ambientColor","type":"color","label":"Lights - Ambient colour","default":"#ffffff"},{"id":"topIntensity","type":"number","label":"Lights - Top rig intensity","default":3.1,"min":0,"max":12,"step":0.05},{"id":"topColor","type":"color","label":"Lights - Top rig colour","default":"#ffffff"},{"id":"bottomIntensity","type":"number","label":"Lights - Bottom rig intensity","default":2.15,"min":0,"max":12,"step":0.05},{"id":"bottomColor","type":"color","label":"Lights - Bottom rig colour","default":"#ffcaad"},{"id":"keyIntensity","type":"number","label":"Lights - Key rig intensity","default":5,"min":0,"max":12,"step":0.05},{"id":"keyColor","type":"color","label":"Lights - Key rig colour","default":"#ffe9d6"},{"id":"shadowRadius","type":"number","label":"Shadows - Softness at map size 2048","default":44.5,"min":0,"max":50,"step":0.5},{"id":"shadowMapSize","type":"number","label":"Shadows - Map size","default":2048,"min":512,"max":4096,"step":256},{"id":"backdrop","type":"color","label":"Background - Colour","default":"#0b0d13"},{"id":"cameraFov","type":"number","label":"Camera - Initial Field of view","default":11,"min":10,"max":90,"step":0.5},{"id":"cameraX","type":"number","label":"Camera - Initial X","default":-1.65,"min":-20,"max":20,"step":0.05},{"id":"cameraY","type":"number","label":"Camera - Initial Y","default":8.5,"min":-20,"max":20,"step":0.05},{"id":"cameraZ","type":"number","label":"Camera - Initial Z (distance)","default":-8.508708,"min":-20,"max":50,"step":0.1},{"id":"cameraSettleFrame","type":"number","label":"Camera - Settle frame (30 fps)","default":70,"min":20,"max":90,"step":1,"description":"Fast-start ease-out along the saved route. Approved value: 70."},{"id":"handheldStrength","type":"number","label":"Camera - Handheld hold strength","default":1,"min":0,"max":1,"step":0.05,"description":"Camera-only motion during frames 90–150; 0 disables it, 1 matches Blender."},{"id":"cameraTargetFov","type":"number","label":"Camera target - Field of view","default":34,"min":10,"max":90,"step":0.5},{"id":"cameraTargetX","type":"number","label":"Camera target - X","default":-7.25,"min":-20,"max":20,"step":0.05},{"id":"cameraTargetY","type":"number","label":"Camera target - Y","default":1.15,"min":-20,"max":20,"step":0.05},{"id":"cameraTargetZ","type":"number","label":"Camera target - Z","default":6.2,"min":2,"max":50,"step":0.1}]'
|
||||
>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=1920, height=1080" />
|
||||
<title>cuboid-carousel</title>
|
||||
<script src="assets/gsap-3.14.2.min.js"></script>
|
||||
<script type="importmap">
|
||||
{
|
||||
"imports": {
|
||||
"three": "./assets/three.module.min.js",
|
||||
"three/addons/": "./assets/addons/"
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="" />
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
#cc-root {
|
||||
position: relative;
|
||||
width: 1920px;
|
||||
height: 1080px;
|
||||
background: #0b0d13;
|
||||
overflow: hidden;
|
||||
}
|
||||
#cc-bg {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
z-index: 0;
|
||||
background: #0b0d13;
|
||||
}
|
||||
#cc-stage {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
#cc-canvas {
|
||||
display: block;
|
||||
width: 1920px;
|
||||
height: 1080px;
|
||||
}
|
||||
#cc-fontload {
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
font-family: "Inter", sans-serif;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div
|
||||
data-hf-id="hf-im3h"
|
||||
id="cc-root"
|
||||
data-composition-id="cuboid-carousel"
|
||||
data-start="0"
|
||||
data-duration="6.666666666666667"
|
||||
data-width="1920"
|
||||
data-height="1080"
|
||||
>
|
||||
<div data-hf-id="hf-y4po" id="cc-bg" aria-hidden="true"></div>
|
||||
<section
|
||||
data-hf-id="hf-xpi6"
|
||||
id="cc-stage"
|
||||
class="clip"
|
||||
data-start="0"
|
||||
data-duration="6.666666666666667"
|
||||
data-track-index="1"
|
||||
></section>
|
||||
<div data-hf-id="hf-0e46" id="cc-fontload" aria-hidden="true">
|
||||
<span data-hf-id="hf-kqtg" style="font-weight: 400">Aa</span
|
||||
><span data-hf-id="hf-grhl" style="font-weight: 500">Aa</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
/* Studio's variables parser fails on any composition with a <canvas> in
|
||||
its MARKUP - created at runtime instead (identical for render). */
|
||||
(function () {
|
||||
var c = document.createElement("canvas");
|
||||
c.id = "cc-canvas";
|
||||
c.width = 1920;
|
||||
c.height = 1080;
|
||||
document.getElementById("cc-stage").appendChild(c);
|
||||
})();
|
||||
</script>
|
||||
<script type="module">
|
||||
/* Approved Blender choreography, 200 frames at 30 fps. Card geometry,
|
||||
content textures and lighting remain the existing live block. Motion
|
||||
uses saved native card curves and evaluated camera/handheld samples.
|
||||
Frame 31: breakaway; 90–150: locked hero; 150–190: camera fly-through. */
|
||||
import * as THREE from "three";
|
||||
import { RoomEnvironment } from "three/addons/environments/RoomEnvironment.js";
|
||||
import { mergeVertices } from "three/addons/utils/BufferGeometryUtils.js";
|
||||
|
||||
import { MOTION } from "./assets/cuboid-motion.js";
|
||||
|
||||
const DUR = MOTION.endFrame / MOTION.fps;
|
||||
const V =
|
||||
window.__hyperframes && window.__hyperframes.getVariables
|
||||
? window.__hyperframes.getVariables()
|
||||
: {};
|
||||
|
||||
// ---- variable readers (every one clamped to the DialKit range) ----
|
||||
const num = (v, d, lo, hi) => {
|
||||
const n = Number(v);
|
||||
return isFinite(n) ? Math.min(hi, Math.max(lo, n)) : d;
|
||||
};
|
||||
const col = (v, d) =>
|
||||
typeof v === "string" && /^#[0-9a-fA-F]{3,8}$/.test(v.trim()) ? v.trim() : d;
|
||||
const bool = (v, d) =>
|
||||
typeof v === "boolean" ? v : v === "true" ? true : v === "false" ? false : d;
|
||||
const pick = (v, opts, d) => (opts.indexOf(String(v)) >= 0 ? String(v) : d);
|
||||
|
||||
// cuboid
|
||||
const WIDTH = num(V.cuboidWidth, 1.7, 0.3, 5);
|
||||
const HEIGHT = num(V.cuboidHeight, 2.4, 0.3, 6);
|
||||
const DEPTH = num(V.cuboidDepth, 0.16, 0.02, 2);
|
||||
const CORNER_R = num(V.cornerRadius, 0.105, 0, 0.6);
|
||||
const BEVEL_SIZE = num(V.bevelSize, 0.03, 0, 0.3);
|
||||
const BEVEL_THICK = num(V.bevelThickness, 0.04, 0, 0.3);
|
||||
const SMOOTH = Math.round(num(V.smoothness, 32, 1, 32));
|
||||
const ROUGH = num(V.roughness, 0.6, 0, 1);
|
||||
const METAL = num(V.metalness, 0.4, 0, 1);
|
||||
const BODY = col(V.bodyColor, "#242424");
|
||||
// layout
|
||||
const COUNT = Math.round(num(V.count, 8, 1, 12));
|
||||
const GAP = num(V.gap, 0.01, 0, 1.5);
|
||||
// rotation
|
||||
const AXIS = "x";
|
||||
// hero
|
||||
const HERO_SCALE = num(V.heroScale, 1, 1, 1.6);
|
||||
// lights
|
||||
const AMB_I = num(V.ambientIntensity, 0.35, 0, 4);
|
||||
const AMB_C = col(V.ambientColor, "#ffffff");
|
||||
const TOP_I = num(V.topIntensity, 3.1, 0, 12);
|
||||
const TOP_C = col(V.topColor, "#ffffff");
|
||||
const BOT_I = num(V.bottomIntensity, 2.15, 0, 12);
|
||||
const BOT_C = col(V.bottomColor, "#ffcaad");
|
||||
const KEY_I = num(V.keyIntensity, 5, 0, 12);
|
||||
const KEY_C = col(V.keyColor, "#ffe9d6");
|
||||
// shadows
|
||||
const SHADOW_RADIUS = num(V.shadowRadius, 44.5, 0, 50);
|
||||
const MAP_SIZE = Math.max(
|
||||
512,
|
||||
Math.min(4096, Math.round(num(V.shadowMapSize, 2048, 512, 4096) / 256) * 256),
|
||||
);
|
||||
// flat page background
|
||||
const BACKDROP = col(V.backdrop, "#0b0d13");
|
||||
// camera
|
||||
const FOV = num(V.cameraFov, 65, 10, 90);
|
||||
const CAM_X = num(V.cameraX, -0.010523080825805664, -20, 20);
|
||||
const CAM_Y = num(V.cameraY, 0.5195326805114746, -20, 20);
|
||||
const CAM_Z = num(V.cameraZ, -8.508708000183105, -20, 50);
|
||||
|
||||
const TARGET_FOV = num(V.cameraTargetFov, 34, 10, 90);
|
||||
const TARGET_X = num(V.cameraTargetX, -7.25, -20, 20);
|
||||
const TARGET_Y = num(V.cameraTargetY, 1.15, -20, 20);
|
||||
const TARGET_Z = num(V.cameraTargetZ, 6.2, 2, 50);
|
||||
|
||||
/* ---- derived spacing: the no-overlap floor (see header) ---- */
|
||||
const sweepHalf =
|
||||
AXIS === "x"
|
||||
? WIDTH / 2
|
||||
: AXIS === "y"
|
||||
? Math.hypot(WIDTH, DEPTH) / 2
|
||||
: Math.hypot(WIDTH, HEIGHT) / 2;
|
||||
const zSweepHalf =
|
||||
AXIS === "x"
|
||||
? Math.hypot(HEIGHT, DEPTH) / 2
|
||||
: AXIS === "y"
|
||||
? Math.hypot(WIDTH, DEPTH) / 2
|
||||
: DEPTH / 2;
|
||||
const SPACING = Math.max(WIDTH, 2 * sweepHalf + 0.02) + GAP;
|
||||
const HALF_EXTENT = sweepHalf * HERO_SCALE;
|
||||
|
||||
/* ---- viewport maths (ported from viewportHalfWidth) ---- */
|
||||
const ASPECT = 1920 / 1080;
|
||||
const vFov = (FOV * Math.PI) / 180;
|
||||
const hFov = 2 * Math.atan(Math.tan(vFov / 2) * ASPECT);
|
||||
const VIEW_HALF = CAM_Z * Math.tan(hFov / 2);
|
||||
/* Off-screen test. A cuboid reaches back to z = -zSweepHalf*heroScale,
|
||||
where the frustum is WIDER than at z = 0, so culling on VIEW_HALF
|
||||
alone would clip a corner that is still in shot. Measure at the
|
||||
deepest point the geometry can reach. */
|
||||
const EDGE =
|
||||
(CAM_Z + zSweepHalf * HERO_SCALE) * Math.tan(hFov / 2) + Math.abs(CAM_X) + HALF_EXTENT; // |x| > EDGE = off-screen
|
||||
|
||||
/* ================= CONTENT - edit here =================
|
||||
Each: { color, category, title, pills: [1-2 strings] }. The same array
|
||||
can be supplied at render/preview time as JSON via the `cardsJson`
|
||||
variable; anything invalid falls back to this list. */
|
||||
const CARDS = [
|
||||
{
|
||||
color: "#fe552e",
|
||||
category: "Creative requests",
|
||||
title: "Identify key resources",
|
||||
pills: ["In progress", "Jul 17, 2026"],
|
||||
},
|
||||
{
|
||||
color: "#d98a1f",
|
||||
category: "Creative requests",
|
||||
title: "Develop concept art",
|
||||
pills: ["In review", "Jul 20, 2026"],
|
||||
},
|
||||
{
|
||||
color: "#4f6ef0",
|
||||
category: "Product Development",
|
||||
title: "Create Design Prototypes",
|
||||
pills: ["In progress", "Aug 02, 2026"],
|
||||
},
|
||||
{
|
||||
color: "#1f9d57",
|
||||
category: "Project Management",
|
||||
title: "Draft Project Timeline",
|
||||
pills: ["Planned", "Aug 09, 2026"],
|
||||
},
|
||||
{
|
||||
color: "#c026d3",
|
||||
category: "Research",
|
||||
title: "Run user interviews",
|
||||
pills: ["Planned", "Aug 15, 2026"],
|
||||
},
|
||||
{
|
||||
color: "#0891b2",
|
||||
category: "Product Development",
|
||||
title: "Ship the beta",
|
||||
pills: ["Blocked", "Aug 22, 2026"],
|
||||
},
|
||||
{
|
||||
color: "#e11d48",
|
||||
category: "Creative requests",
|
||||
title: "Cut the launch film",
|
||||
pills: ["Planned", "Sep 01, 2026"],
|
||||
},
|
||||
{
|
||||
color: "#7c3aed",
|
||||
category: "Research",
|
||||
title: "Synthesise findings",
|
||||
pills: ["Planned", "Sep 08, 2026"],
|
||||
},
|
||||
];
|
||||
const FALLBACK_COLORS = [
|
||||
"#fe552e",
|
||||
"#d98a1f",
|
||||
"#4f6ef0",
|
||||
"#1f9d57",
|
||||
"#c026d3",
|
||||
"#0891b2",
|
||||
"#e11d48",
|
||||
"#7c3aed",
|
||||
];
|
||||
|
||||
/* cardsJson override: strict parse, silent fallback on any problem. */
|
||||
let ITEMS = CARDS;
|
||||
try {
|
||||
const raw = String(V.cardsJson || "").trim();
|
||||
if (raw) {
|
||||
const parsed = JSON.parse(raw);
|
||||
if (Array.isArray(parsed) && parsed.length >= 1 && parsed.length <= 24) {
|
||||
const clean = parsed.map((e, i) => ({
|
||||
color: typeof e.color === "string" ? e.color : FALLBACK_COLORS[i % 8],
|
||||
category: typeof e.category === "string" ? e.category : "",
|
||||
title: typeof e.title === "string" ? e.title : "",
|
||||
pills: Array.isArray(e.pills) ? e.pills.slice(0, 2).map((x) => String(x)) : [],
|
||||
}));
|
||||
if (clean.every((e) => e.title)) ITEMS = clean;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
/* keep CARDS */
|
||||
}
|
||||
|
||||
const HERO = Math.max(0, Math.min(COUNT - 1, Math.round(num(V.heroCard, 4, 1, 12)) - 1));
|
||||
|
||||
const clamp01 = (x) => Math.max(0, Math.min(1, x));
|
||||
const smooth01 = (x) => x * x * (3 - 2 * x);
|
||||
const N = MOTION.tiles.length;
|
||||
const CAMERA_SETTLE = num(V.cameraSettleFrame, 70, 20, 90);
|
||||
const T_IN = CAMERA_SETTLE / 30,
|
||||
T_BREAK = 31 / 30,
|
||||
T_CENTER = 90 / 30;
|
||||
const T_CLEAR = MOTION.tiles.find((tile) => !tile.hero).hideFrame / 30;
|
||||
const T_OUT = 150 / 30,
|
||||
T_EMPTY = 190 / 30;
|
||||
|
||||
const canvas = document.getElementById("cc-canvas");
|
||||
const renderer = new THREE.WebGLRenderer({ canvas, antialias: true });
|
||||
renderer.setPixelRatio(1);
|
||||
renderer.setSize(1920, 1080, false);
|
||||
renderer.shadowMap.enabled = true;
|
||||
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
|
||||
renderer.outputColorSpace = THREE.SRGBColorSpace;
|
||||
|
||||
const scene = new THREE.Scene();
|
||||
scene.background = new THREE.Color(BACKDROP);
|
||||
document.getElementById("cc-bg").style.background = BACKDROP;
|
||||
|
||||
const camera = new THREE.PerspectiveCamera(FOV, ASPECT, 0.1, 200);
|
||||
camera.position.set(CAM_X, CAM_Y, CAM_Z);
|
||||
camera.lookAt(0, 0, 0);
|
||||
|
||||
// environment (stand-in for the `city` preset)
|
||||
const pmrem = new THREE.PMREMGenerator(renderer);
|
||||
const envRT = pmrem.fromScene(new RoomEnvironment(), 0.04);
|
||||
const envMap = envRT.texture;
|
||||
|
||||
// ---- lights, exactly the experiment's rig ----
|
||||
scene.add(new THREE.AmbientLight(new THREE.Color(AMB_C), AMB_I));
|
||||
|
||||
const top = new THREE.DirectionalLight(new THREE.Color(TOP_C), TOP_I);
|
||||
top.position.set(20, 9, 3);
|
||||
top.castShadow = true;
|
||||
top.shadow.mapSize.set(MAP_SIZE, MAP_SIZE);
|
||||
// "softness at the reference map size (2048)" - stable across mapSize
|
||||
top.shadow.radius = SHADOW_RADIUS * (MAP_SIZE / 2048);
|
||||
top.shadow.bias = -0.0003;
|
||||
top.shadow.normalBias = 0.02;
|
||||
{
|
||||
const c = top.shadow.camera;
|
||||
const halfW = Math.max(14, VIEW_HALF + SPACING * 3);
|
||||
const halfH = Math.max(10, HEIGHT * 0.7 + 2);
|
||||
c.left = -halfW;
|
||||
c.right = halfW;
|
||||
c.top = halfH;
|
||||
c.bottom = -halfH;
|
||||
c.near = 0.5;
|
||||
c.far = 60;
|
||||
c.updateProjectionMatrix();
|
||||
}
|
||||
scene.add(top);
|
||||
|
||||
const bottom = new THREE.DirectionalLight(new THREE.Color(BOT_C), BOT_I);
|
||||
bottom.position.set(0, -8, 3);
|
||||
scene.add(bottom);
|
||||
|
||||
const keyLight = new THREE.DirectionalLight(new THREE.Color(KEY_C), KEY_I);
|
||||
keyLight.position.set(12, 9.2, 7);
|
||||
scene.add(keyLight);
|
||||
|
||||
/* ---- rounded-rect shape + bevelled extrude, ported from
|
||||
roundedRectShape() / buildCardGeometry() ---- */
|
||||
function roundedRectShape(w, h, r) {
|
||||
const s = new THREE.Shape();
|
||||
const x = -w / 2,
|
||||
y = -h / 2;
|
||||
s.moveTo(x + r, y);
|
||||
s.lineTo(x + w - r, y);
|
||||
s.quadraticCurveTo(x + w, y, x + w, y + r);
|
||||
s.lineTo(x + w, y + h - r);
|
||||
s.quadraticCurveTo(x + w, y + h, x + w - r, y + h);
|
||||
s.lineTo(x + r, y + h);
|
||||
s.quadraticCurveTo(x, y + h, x, y + h - r);
|
||||
s.lineTo(x, y + r);
|
||||
s.quadraticCurveTo(x, y, x + r, y);
|
||||
s.closePath();
|
||||
return s;
|
||||
}
|
||||
|
||||
function buildCardGeometry() {
|
||||
const outerR = Math.min(CORNER_R, Math.min(WIDTH, HEIGHT) / 2 - 0.001);
|
||||
const r = Math.max(outerR, 0.002);
|
||||
const bSize = Math.max(
|
||||
0,
|
||||
Math.min(BEVEL_SIZE, r * 0.98, WIDTH / 2 - 0.005, HEIGHT / 2 - 0.005),
|
||||
);
|
||||
const maxThick = Math.max(DEPTH * 0.45, 0.0005);
|
||||
const bThick = Math.max(0, Math.min(BEVEL_THICK, maxThick));
|
||||
const sizeOn = bSize > 0.0005,
|
||||
thickOn = bThick > 0.0005;
|
||||
const bevelOn = sizeOn || thickOn;
|
||||
const innerW = sizeOn ? Math.max(WIDTH - 2 * bSize, 0.01) : WIDTH;
|
||||
const innerH = sizeOn ? Math.max(HEIGHT - 2 * bSize, 0.01) : HEIGHT;
|
||||
const innerR = sizeOn ? Math.max(r - bSize, 0.002) : r;
|
||||
const extrudeDepth = thickOn ? Math.max(DEPTH - 2 * bThick, DEPTH * 0.15, 0.002) : DEPTH;
|
||||
|
||||
const shape = roundedRectShape(innerW, innerH, innerR);
|
||||
|
||||
// Cap UVs span the full face so the text texture lands square on it;
|
||||
// side walls collapse to a single point, as in the source.
|
||||
const uvGenerator = {
|
||||
generateTopUV(_g, vertices, a, b, c) {
|
||||
const p = (i) =>
|
||||
new THREE.Vector2(
|
||||
(vertices[i * 3] + WIDTH / 2) / WIDTH,
|
||||
(vertices[i * 3 + 1] + HEIGHT / 2) / HEIGHT,
|
||||
);
|
||||
return [p(a), p(b), p(c)];
|
||||
},
|
||||
generateSideWallUV() {
|
||||
return [
|
||||
new THREE.Vector2(0, 0),
|
||||
new THREE.Vector2(0, 0),
|
||||
new THREE.Vector2(0, 0),
|
||||
new THREE.Vector2(0, 0),
|
||||
];
|
||||
},
|
||||
};
|
||||
|
||||
const geo = new THREE.ExtrudeGeometry(shape, {
|
||||
depth: extrudeDepth,
|
||||
bevelEnabled: bevelOn,
|
||||
bevelThickness: bThick,
|
||||
bevelSize: bSize,
|
||||
bevelSegments: Math.max(16, Math.round(SMOOTH * 2)),
|
||||
curveSegments: Math.max(16, Math.round(SMOOTH)),
|
||||
steps: 1,
|
||||
UVGenerator: uvGenerator,
|
||||
});
|
||||
geo.center();
|
||||
if (bevelOn) {
|
||||
const merged = mergeVertices(geo, 1e-4);
|
||||
geo.dispose();
|
||||
merged.computeVertexNormals();
|
||||
merged.computeBoundingSphere();
|
||||
return merged;
|
||||
}
|
||||
geo.computeVertexNormals();
|
||||
geo.computeBoundingSphere();
|
||||
return geo;
|
||||
}
|
||||
|
||||
/* ---- face texture, ported from face-renderer.ts (540x756).
|
||||
The source clears to transparent and lets the body colour show
|
||||
through a masking shader; painting the body colour in first gives
|
||||
the same result without the extra pass. ---- */
|
||||
const FACE_W = 540,
|
||||
FACE_H = 756;
|
||||
const FONT_STACK = '"Inter", -apple-system, system-ui, sans-serif';
|
||||
|
||||
function lightTint(hex, amount = 0.55) {
|
||||
return new THREE.Color(hex).lerp(new THREE.Color("#ffffff"), amount).getStyle();
|
||||
}
|
||||
function roundedRectPath(ctx, x, y, w, h, r) {
|
||||
const radius = Math.min(r, w / 2, h / 2);
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x + radius, y);
|
||||
ctx.arcTo(x + w, y, x + w, y + h, radius);
|
||||
ctx.arcTo(x + w, y + h, x, y + h, radius);
|
||||
ctx.arcTo(x, y + h, x, y, radius);
|
||||
ctx.arcTo(x, y, x + w, y, radius);
|
||||
ctx.closePath();
|
||||
}
|
||||
function wrapLines(ctx, text, maxWidth) {
|
||||
const words = String(text).split(" ");
|
||||
const lines = [];
|
||||
let current = "";
|
||||
for (const word of words) {
|
||||
const candidate = current ? current + " " + word : word;
|
||||
if (ctx.measureText(candidate).width > maxWidth && current) {
|
||||
lines.push(current);
|
||||
current = word;
|
||||
} else current = candidate;
|
||||
}
|
||||
if (current) lines.push(current);
|
||||
return lines;
|
||||
}
|
||||
|
||||
function faceTexture(item) {
|
||||
const c = document.createElement("canvas");
|
||||
c.width = FACE_W;
|
||||
c.height = FACE_H;
|
||||
const ctx = c.getContext("2d");
|
||||
ctx.fillStyle = BODY;
|
||||
ctx.fillRect(0, 0, FACE_W, FACE_H);
|
||||
|
||||
const tint = lightTint(item.color);
|
||||
const pad = 45;
|
||||
const contentW = FACE_W - pad * 2;
|
||||
let y = pad + 8;
|
||||
|
||||
ctx.textBaseline = "alphabetic";
|
||||
ctx.textAlign = "left";
|
||||
|
||||
ctx.fillStyle = tint;
|
||||
ctx.font = "400 25px " + FONT_STACK;
|
||||
ctx.fillText(item.category, pad, y + 22);
|
||||
y += 22 + 34;
|
||||
|
||||
ctx.fillStyle = "#ffffff";
|
||||
ctx.font = "500 38px " + FONT_STACK;
|
||||
for (const line of wrapLines(ctx, item.title, contentW)) {
|
||||
ctx.fillText(line, pad, y + 34);
|
||||
y += 47;
|
||||
}
|
||||
|
||||
ctx.font = "400 19px " + FONT_STACK;
|
||||
const pillH = 50;
|
||||
const pillY = FACE_H - pad - pillH;
|
||||
let pillX = pad;
|
||||
for (const label of item.pills) {
|
||||
const pillW = ctx.measureText(label).width + 44;
|
||||
roundedRectPath(ctx, pillX, pillY, pillW, pillH, pillH / 2);
|
||||
ctx.strokeStyle = tint;
|
||||
ctx.lineWidth = 1.4;
|
||||
ctx.stroke();
|
||||
ctx.fillStyle = tint;
|
||||
ctx.textBaseline = "middle";
|
||||
ctx.fillText(label, pillX + 22, pillY + pillH / 2 + 1);
|
||||
ctx.textBaseline = "alphabetic";
|
||||
pillX += pillW + 15;
|
||||
}
|
||||
|
||||
const tex = new THREE.CanvasTexture(c);
|
||||
tex.flipY = false;
|
||||
tex.colorSpace = THREE.SRGBColorSpace;
|
||||
tex.anisotropy = 8;
|
||||
tex.minFilter = THREE.LinearMipmapLinearFilter;
|
||||
tex.magFilter = THREE.LinearFilter;
|
||||
return tex;
|
||||
}
|
||||
|
||||
// ---- build the chain ----
|
||||
const geometry = buildCardGeometry();
|
||||
const sideMat = new THREE.MeshStandardMaterial({
|
||||
color: new THREE.Color(BODY),
|
||||
roughness: ROUGH,
|
||||
metalness: METAL,
|
||||
envMap,
|
||||
envMapIntensity: 0.3,
|
||||
});
|
||||
const EULER_ORDER = AXIS === "x" ? "XYZ" : AXIS === "y" ? "YXZ" : "ZXY";
|
||||
|
||||
let capMats = [];
|
||||
let heroMat = null;
|
||||
let cuboids = [];
|
||||
function buildChain() {
|
||||
if (heroMat) heroMat.dispose();
|
||||
capMats.forEach((m) => {
|
||||
if (m.map) m.map.dispose();
|
||||
m.dispose();
|
||||
});
|
||||
capMats = ITEMS.map((item) => {
|
||||
const capMat = new THREE.MeshStandardMaterial({
|
||||
map: faceTexture(item),
|
||||
roughness: ROUGH,
|
||||
metalness: METAL,
|
||||
envMap,
|
||||
envMapIntensity: 0,
|
||||
});
|
||||
/* The source masks content to the front cap with a smoothstep on
|
||||
position.z (uHalfDepth / uCapBand). Same idea: the back cap drops
|
||||
the map and shows plain body colour, so text never reads mirrored. */
|
||||
capMat.onBeforeCompile = (shader) => {
|
||||
shader.uniforms.uBody = { value: new THREE.Color(BODY) };
|
||||
shader.vertexShader = shader.vertexShader
|
||||
.replace("#include <common>", "#include <common>\nvarying float vLocalZ;")
|
||||
.replace("#include <begin_vertex>", "#include <begin_vertex>\nvLocalZ = position.z;");
|
||||
shader.fragmentShader = shader.fragmentShader
|
||||
.replace(
|
||||
"#include <common>",
|
||||
"#include <common>\nvarying float vLocalZ;\nuniform vec3 uBody;",
|
||||
)
|
||||
.replace(
|
||||
"#include <map_fragment>",
|
||||
"#include <map_fragment>\nif (vLocalZ > 0.0) diffuseColor.rgb = uBody;",
|
||||
)
|
||||
.replace(
|
||||
"#include <emissivemap_fragment>",
|
||||
"#include <emissivemap_fragment>\nif (vLocalZ > 0.0) totalEmissiveRadiance = vec3(0.0);",
|
||||
);
|
||||
};
|
||||
return capMat;
|
||||
});
|
||||
// A separate material avoids brightening repeated cards with the same
|
||||
// content. Emissive fill is masked off the unprinted back cap as well.
|
||||
const heroSource = capMats[HERO % ITEMS.length];
|
||||
heroMat = heroSource.clone();
|
||||
heroMat.onBeforeCompile = heroSource.onBeforeCompile;
|
||||
heroMat.emissive.set(0xffffff);
|
||||
heroMat.emissiveMap = heroSource.map;
|
||||
heroMat.emissiveIntensity = 0;
|
||||
if (cuboids.length === 0) {
|
||||
for (let j = 0; j < N; j++) {
|
||||
// ExtrudeGeometry group 0 = caps, group 1 = side walls
|
||||
const mesh = new THREE.Mesh(geometry, [capMats[0], sideMat]);
|
||||
mesh.rotation.order = EULER_ORDER;
|
||||
mesh.castShadow = true;
|
||||
mesh.receiveShadow = true;
|
||||
scene.add(mesh);
|
||||
cuboids.push(mesh);
|
||||
}
|
||||
}
|
||||
}
|
||||
buildChain();
|
||||
|
||||
// Blender frame 0 maps to web time 0. Every pose is reconstructed on seek.
|
||||
const basis = new THREE.Quaternion().setFromAxisAngle(
|
||||
new THREE.Vector3(1, 0, 0),
|
||||
-Math.PI / 2,
|
||||
);
|
||||
const inverseBasis = basis.clone().invert();
|
||||
const sourceRoot = new THREE.Matrix4().set(...MOTION.rootMatrix);
|
||||
const convertedRoot = new THREE.Matrix4()
|
||||
.makeRotationFromQuaternion(basis)
|
||||
.multiply(sourceRoot)
|
||||
.multiply(new THREE.Matrix4().makeRotationFromQuaternion(inverseBasis));
|
||||
const motionRoot = new THREE.Group();
|
||||
motionRoot.name = "Blender approved Cuboid chain";
|
||||
motionRoot.matrixAutoUpdate = false;
|
||||
motionRoot.matrix.copy(convertedRoot);
|
||||
scene.add(motionRoot);
|
||||
for (const mesh of cuboids) motionRoot.add(mesh);
|
||||
const heroSlot = MOTION.tiles.findIndex((tile) => tile.hero);
|
||||
const sourceStart = new THREE.Vector3(...MOTION.cameraStart).applyQuaternion(basis);
|
||||
const sourceEnd = new THREE.Vector3(...MOTION.cameraEnd).applyQuaternion(basis);
|
||||
const startOffset = new THREE.Vector3(CAM_X, CAM_Y, CAM_Z).sub(sourceStart);
|
||||
const endOffset = new THREE.Vector3(TARGET_X, TARGET_Y, TARGET_Z).sub(sourceEnd);
|
||||
const hasCameraOffset = startOffset.lengthSq() + endOffset.lengthSq() > 1e-14;
|
||||
const targetCamera = new THREE.Vector3(TARGET_X, TARGET_Y, TARGET_Z);
|
||||
const samplePosition = new THREE.Vector3(),
|
||||
basePosition = new THREE.Vector3();
|
||||
const sampleQuaternion = new THREE.Quaternion(),
|
||||
baseQuaternion = new THREE.Quaternion();
|
||||
const qA = new THREE.Quaternion(),
|
||||
qB = new THREE.Quaternion(),
|
||||
qDelta = new THREE.Quaternion();
|
||||
const poseEuler = new THREE.Euler(0, 0, 0, "ZYX");
|
||||
const lookCamera = new THREE.PerspectiveCamera();
|
||||
const zero = new THREE.Vector3();
|
||||
const S = { t: 0 };
|
||||
const HERO_FILL = num(V.heroFillIntensity, 0.55, 0, 2);
|
||||
const HANDHELD = num(V.handheldStrength, 1, 0, 1);
|
||||
const EXIT_ANGLES = [
|
||||
num(V.heroExitX, 288, -720, 720),
|
||||
num(V.heroExitY, -234, -720, 720),
|
||||
num(V.heroExitZ, 198, -720, 720),
|
||||
];
|
||||
const sourceExitAngles = [288, -234, 198];
|
||||
|
||||
function channelAt(keys, frame) {
|
||||
if (!keys || !keys.length) return 0;
|
||||
if (frame <= keys[0][0]) return keys[0][1];
|
||||
let lo = 0,
|
||||
hi = keys.length - 1;
|
||||
if (frame >= keys[hi][0]) return keys[hi][1];
|
||||
while (hi - lo > 1) {
|
||||
const mid = (lo + hi) >> 1;
|
||||
if (keys[mid][0] <= frame) lo = mid;
|
||||
else hi = mid;
|
||||
}
|
||||
const a = keys[lo],
|
||||
b = keys[hi],
|
||||
u = (frame - a[0]) / (b[0] - a[0]);
|
||||
return a[1] + (b[1] - a[1]) * u;
|
||||
}
|
||||
function readCamera(frame, position, quaternion) {
|
||||
const f = Math.max(0, Math.min(MOTION.endFrame, frame));
|
||||
const i = Math.floor(f),
|
||||
j = Math.min(MOTION.endFrame, i + 1),
|
||||
u = f - i;
|
||||
const a = MOTION.camera[i],
|
||||
b = MOTION.camera[j];
|
||||
position
|
||||
.set(a[0] + (b[0] - a[0]) * u, a[1] + (b[1] - a[1]) * u, a[2] + (b[2] - a[2]) * u)
|
||||
.applyQuaternion(basis);
|
||||
qA.set(a[3], a[4], a[5], a[6]);
|
||||
qB.set(b[3], b[4], b[5], b[6]);
|
||||
// Camera optical axes are already -Z forward / +Y up: convert world basis only.
|
||||
quaternion.slerpQuaternions(qA, qB, u).premultiply(basis).normalize();
|
||||
return a[7] + (b[7] - a[7]) * u;
|
||||
}
|
||||
function cameraAt(frame) {
|
||||
const cameraFrame =
|
||||
frame < CAMERA_SETTLE ? (frame * 70) / CAMERA_SETTLE : Math.max(70, frame);
|
||||
const sourceFov = readCamera(cameraFrame, samplePosition, sampleQuaternion);
|
||||
const inHandheld = frame > 90 && frame < 150;
|
||||
if (inHandheld) {
|
||||
readCamera(90, basePosition, baseQuaternion);
|
||||
if (HANDHELD !== 1) {
|
||||
samplePosition.lerpVectors(basePosition, samplePosition, HANDHELD);
|
||||
sampleQuaternion.slerpQuaternions(baseQuaternion, sampleQuaternion, HANDHELD);
|
||||
}
|
||||
} else {
|
||||
basePosition.copy(samplePosition);
|
||||
baseQuaternion.copy(sampleQuaternion);
|
||||
}
|
||||
const u = clamp01(
|
||||
(MOTION.cameraControls["Start FOV degrees"] - sourceFov) /
|
||||
(MOTION.cameraControls["Start FOV degrees"] - MOTION.cameraControls["End FOV degrees"]),
|
||||
);
|
||||
// A changed start must not recompute the approved settled/handheld pose.
|
||||
if (hasCameraOffset && (u < 1 || endOffset.lengthSq() > 1e-14)) {
|
||||
qDelta.copy(baseQuaternion).invert().multiply(sampleQuaternion);
|
||||
const offset = startOffset.clone().lerp(endOffset, u);
|
||||
lookCamera.position.copy(basePosition).add(offset);
|
||||
lookCamera.lookAt(zero);
|
||||
sampleQuaternion.copy(lookCamera.quaternion).multiply(qDelta);
|
||||
samplePosition.add(offset);
|
||||
}
|
||||
camera.position.copy(samplePosition);
|
||||
camera.quaternion.copy(sampleQuaternion);
|
||||
camera.fov = FOV === 65 && TARGET_FOV === 34 ? sourceFov : FOV + (TARGET_FOV - FOV) * u;
|
||||
camera.updateProjectionMatrix();
|
||||
camera.updateMatrixWorld();
|
||||
}
|
||||
const heroInitialX = MOTION.tiles[heroSlot].channels.location[0][0][1];
|
||||
const slotOffsets = MOTION.tiles.map(
|
||||
(tile) => (tile.channels.location[0][0][1] - heroInitialX) / 1.73,
|
||||
);
|
||||
// One mesh owns the hero slot before and after separation. Its arrival
|
||||
// must use the same transport as the constant-rate chain, not the old
|
||||
// slower Blender arrival curve. Keep that transport while it first lifts
|
||||
// clear, then rejoin the approved absolute path without a position jump.
|
||||
const heroXKeys = MOTION.tiles[heroSlot].channels.location[0];
|
||||
const chainXKeys = MOTION.tiles.find((tile) => !tile.hero).channels.location[0];
|
||||
const HERO_SLOT_RELEASE = 36,
|
||||
HERO_PATH_REJOIN = 70;
|
||||
const heroSlotXAt = (frame) => heroInitialX + channelAt(chainXKeys, frame) - chainXKeys[0][1];
|
||||
const heroSlotDelta =
|
||||
heroSlotXAt(HERO_SLOT_RELEASE) - channelAt(heroXKeys, HERO_SLOT_RELEASE);
|
||||
const heroSlotVelocityDelta =
|
||||
(heroSlotXAt(HERO_SLOT_RELEASE) -
|
||||
heroSlotXAt(HERO_SLOT_RELEASE - 0.25) -
|
||||
channelAt(heroXKeys, HERO_SLOT_RELEASE) +
|
||||
channelAt(heroXKeys, HERO_SLOT_RELEASE - 0.25)) /
|
||||
0.25;
|
||||
function heroSlotCorrection(frame) {
|
||||
if (frame <= HERO_SLOT_RELEASE) return heroSlotXAt(frame) - channelAt(heroXKeys, frame);
|
||||
if (frame >= HERO_PATH_REJOIN) return 0;
|
||||
const duration = HERO_PATH_REJOIN - HERO_SLOT_RELEASE;
|
||||
const u = (frame - HERO_SLOT_RELEASE) / duration,
|
||||
u2 = u * u,
|
||||
u3 = u2 * u;
|
||||
return (
|
||||
(2 * u3 - 3 * u2 + 1) * heroSlotDelta +
|
||||
(u3 - 2 * u2 + u) * duration * heroSlotVelocityDelta
|
||||
);
|
||||
}
|
||||
const heroExitBase = MOTION.tiles[heroSlot].channels.rotation_euler.map((keys) =>
|
||||
channelAt(keys, 150),
|
||||
);
|
||||
const actualPose = { frame: 0, camera: [], hero: [], heroRotation: [], visibleTiles: 0 };
|
||||
function renderAt(time) {
|
||||
S.t = Math.max(0, Math.min(DUR, time));
|
||||
const frame = S.t * MOTION.fps;
|
||||
cameraAt(frame);
|
||||
const heroProgress = smooth01(clamp01((frame - 31) / 59));
|
||||
heroMat.emissiveIntensity = HERO_FILL * heroProgress;
|
||||
let visibleTiles = 0;
|
||||
for (let j = 0; j < cuboids.length; j++) {
|
||||
const mesh = cuboids[j],
|
||||
tile = MOTION.tiles[j],
|
||||
c = tile.channels;
|
||||
const p = c.location.map((keys) => channelAt(keys, frame));
|
||||
if (tile.hero) p[0] += heroSlotCorrection(frame);
|
||||
const r = c.rotation_euler.map((keys) => channelAt(keys, frame));
|
||||
if (tile.hero && frame > 150) {
|
||||
for (let axis = 0; axis < 3; axis++)
|
||||
r[axis] =
|
||||
heroExitBase[axis] +
|
||||
((r[axis] - heroExitBase[axis]) * EXIT_ANGLES[axis]) / sourceExitAngles[axis];
|
||||
}
|
||||
mesh.position.set(p[0], p[2], -p[1]);
|
||||
if (!tile.hero) mesh.position.x += slotOffsets[j] * (SPACING - 1.73);
|
||||
// Blender XYZ is extrinsic X→Y→Z; Three's matching Euler order is ZYX.
|
||||
poseEuler.set(r[0], r[1], r[2], "ZYX");
|
||||
mesh.quaternion.setFromEuler(poseEuler).premultiply(basis).multiply(inverseBasis);
|
||||
const scale = c.scale.map((keys) => channelAt(keys, frame));
|
||||
mesh.scale.set(scale[0], scale[2], scale[1]);
|
||||
if (tile.hero) mesh.scale.multiplyScalar(1 + (HERO_SCALE - 1) * heroProgress);
|
||||
const item = ((((tile.itemIndex + HERO - 3) % COUNT) + COUNT) % COUNT) % ITEMS.length;
|
||||
mesh.material[0] = tile.hero && frame >= 31 ? heroMat : capMats[item];
|
||||
mesh.visible = frame < tile.hideFrame;
|
||||
if (mesh.visible) visibleTiles++;
|
||||
if (tile.hero) {
|
||||
actualPose.hero = mesh.position.toArray();
|
||||
actualPose.heroRotation = r;
|
||||
}
|
||||
}
|
||||
actualPose.frame = frame;
|
||||
actualPose.camera = camera.position.toArray();
|
||||
actualPose.visibleTiles = visibleTiles;
|
||||
renderer.toneMappingExposure = 1;
|
||||
renderer.render(scene, camera);
|
||||
}
|
||||
window.__cuboidMotion = {
|
||||
source: MOTION.sourceFile,
|
||||
sha256: MOTION.sourceSha256,
|
||||
fps: MOTION.fps,
|
||||
endFrame: MOTION.endFrame,
|
||||
phases: {
|
||||
breakaway: 31,
|
||||
directApproach: 40,
|
||||
centered: 90,
|
||||
exit: 150,
|
||||
clear: 190,
|
||||
end: 200,
|
||||
},
|
||||
get pose() {
|
||||
return { ...actualPose };
|
||||
},
|
||||
};
|
||||
|
||||
/* ---- timeline: paused, seek-safe, no rAF ---- */
|
||||
window.__timelines = window.__timelines || {};
|
||||
const tl = gsap.timeline({ paused: true });
|
||||
tl.addLabel("arrival", 0);
|
||||
tl.addLabel("alternate-camera", T_IN);
|
||||
tl.addLabel("hero-breakaway", T_BREAK);
|
||||
tl.addLabel("hero-centered", T_CENTER);
|
||||
tl.addLabel("solo-hold", T_CLEAR);
|
||||
tl.addLabel("hero-exit", T_OUT);
|
||||
tl.addLabel("empty", T_EMPTY);
|
||||
tl.to(S, { t: DUR, duration: DUR, ease: "none" }, 0);
|
||||
tl.eventCallback("onUpdate", () => renderAt(S.t));
|
||||
|
||||
/* Studio's player seeks with GSAP events suppressed and dispatches an
|
||||
hf-seek CustomEvent instead - re-sync and repaint so the stage follows. */
|
||||
window.addEventListener("hf-seek", function (event) {
|
||||
const d = event && event.detail;
|
||||
if (d && typeof d.time === "number")
|
||||
tl.totalTime(Math.min(d.time, tl.totalDuration()), true);
|
||||
const fn = tl.eventCallback("onUpdate");
|
||||
if (fn) fn();
|
||||
});
|
||||
window.__timelines["cuboid-carousel"] = tl;
|
||||
|
||||
renderAt(0);
|
||||
/* Draw across the timeline and flush the GPU before ready, so shader links
|
||||
and texture uploads land in the readiness gate, not the first visible frames. */
|
||||
async function warmGpu() {
|
||||
for (const f of [0.25, 0.5, 0.75]) {
|
||||
await new Promise((resolve) => setTimeout(resolve, 0));
|
||||
renderAt(DUR * f);
|
||||
}
|
||||
renderAt(S.t);
|
||||
renderer.getContext().finish();
|
||||
}
|
||||
window.__hf = window.__hf || {};
|
||||
window.__hf.buildReady = window.__hf.buildReady || {};
|
||||
window.__hf.buildReady["cuboid-carousel"] = (
|
||||
document.fonts && document.fonts.ready ? document.fonts.ready : Promise.resolve()
|
||||
)
|
||||
.then(() => {
|
||||
buildChain();
|
||||
renderAt(S.t);
|
||||
return warmGpu();
|
||||
})
|
||||
.catch(() => {});
|
||||
</script>
|
||||
<script>
|
||||
/* Standalone playback: open this file with ?play (add &loop to repeat).
|
||||
Inert without the param — Studio and the renderer never add it, so
|
||||
editing and deterministic renders are unaffected. */
|
||||
(function () {
|
||||
var q = new URLSearchParams(location.search);
|
||||
if (!q.has("play")) return;
|
||||
var tick = setInterval(function () {
|
||||
var map = window.__timelines || {};
|
||||
var ids = Object.keys(map);
|
||||
if (!ids.length) return;
|
||||
clearInterval(tick);
|
||||
for (var i = 0; i < ids.length; i++) {
|
||||
var tl = map[ids[i]];
|
||||
if (q.has("loop")) tl.repeat(-1);
|
||||
tl.play(0);
|
||||
}
|
||||
}, 100);
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
<CatalogSlot slot="install">
|
||||
|
||||
<InstallCommand command="npx hyperframes add cuboid-carousel" item="cuboid-carousel" />
|
||||
|
||||
That writes `compositions/cuboid-carousel/cuboid-carousel.html`, plus 9 supporting files under `compositions/cuboid-carousel/assets/`, `compositions/cuboid-carousel/assets/addons/environments/`, `compositions/cuboid-carousel/assets/addons/utils/`, and `compositions/cuboid-carousel/`.
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
<CatalogSlot slot="docs">
|
||||
|
||||
## Add it to your video
|
||||
|
||||
It runs for 6.67 seconds at 1920×1080. Paste this into your composition:
|
||||
|
||||
```html index.html
|
||||
<div
|
||||
data-composition-id="cuboid-carousel"
|
||||
data-composition-src="compositions/cuboid-carousel/cuboid-carousel.html"
|
||||
data-start="0"
|
||||
data-duration="6.67"
|
||||
data-track-index="1"
|
||||
data-width="1920"
|
||||
data-height="1080"
|
||||
></div>
|
||||
```
|
||||
|
||||
Move it in time with `data-start`. Put it on a different timeline row with
|
||||
`data-track-index`. See [data attributes](/concepts/data-attributes) for the rest.
|
||||
|
||||
{/* hf:generated-footer */}
|
||||
|
||||
Tagged `3d-motion` `carousel` `cards` `travelling-wave` `pbr` `shadows` `room-environment`.
|
||||
|
||||
## Related topics
|
||||
|
||||
- [Browse the complete Catalog](/catalog)
|
||||
- [Add assets and Catalog items in Studio](/studio/assets-and-blocks)
|
||||
- [Build a richer composition](/go-further)
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
</CatalogDetail>
|
||||
@@ -1,35 +1,28 @@
|
||||
---
|
||||
title: "Data Chart"
|
||||
description: "Animated bar + line chart with staggered reveal, NYT-style typography, and value labels"
|
||||
mode: "frame"
|
||||
---
|
||||
|
||||
import { CatalogDetail, CatalogSlot } from "/snippets/catalog-detail.jsx";
|
||||
import { InstallCommand } from "/snippets/install-command.jsx";
|
||||
|
||||
## Install
|
||||
<CatalogDetail
|
||||
previewSrc="/public/catalog/blocks/data-chart.json"
|
||||
compositionId="data-chart"
|
||||
compositionSrc="compositions/data-chart.html"
|
||||
title="Data Chart"
|
||||
description="Animated bar + line chart with staggered reveal, NYT-style typography, and value labels"
|
||||
variables={[]}
|
||||
meta={{"duration":15,"width":1920,"height":1080,"category":"Data","badge":"Stable","codeLines":457}}
|
||||
attribution={{"path":"registry/blocks/data-chart","tags":["data","chart","statistics"]}}
|
||||
hasCode
|
||||
rawUrl="https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/data-chart/data-chart.html"
|
||||
>
|
||||
|
||||
<InstallCommand command="npx hyperframes add data-chart" item="data-chart" />
|
||||
<CatalogSlot slot="code">
|
||||
|
||||
That writes one file: `compositions/data-chart.html`.
|
||||
|
||||
<iframe
|
||||
className="w-full aspect-video rounded-xl border-0 bg-zinc-100 dark:bg-zinc-800"
|
||||
title="data-chart preview"
|
||||
loading="lazy"
|
||||
srcDoc={`<!doctype html><html><head><meta charset="utf-8"><style>html,body{margin:0;height:100%;overflow:hidden;background:transparent}hyperframes-player{display:block;width:100%;height:100%}</style><script src="https://cdn.jsdelivr.net/npm/@hyperframes/player@latest/dist/hyperframes-player.global.js"><\/script></head><body><script>fetch("/public/catalog/blocks/data-chart.json").then(function(r){return r.json()}).then(function(d){var p=document.createElement("hyperframes-player");p.setAttribute("srcdoc",d.html);p.setAttribute("controls","");p.setAttribute("autoplay","");p.setAttribute("loop","");p.setAttribute("muted","");p.setAttribute("poster","https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/data-chart.png");document.body.appendChild(p)});<\/script></body></html>`}
|
||||
/>
|
||||
|
||||
## Change the colors
|
||||
|
||||
Set these CSS variables on the block:
|
||||
|
||||
- `--bg-color` — Background. Defaults to `#faf9f6`.
|
||||
- `--text-color` — Text color. Defaults to `#333333`.
|
||||
|
||||
## Source
|
||||
|
||||
<Accordion title="data-chart.html">
|
||||
|
||||
```html
|
||||
```html data-chart.html
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
@@ -489,16 +482,44 @@ Set these CSS variables on the block:
|
||||
</html>
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
</CatalogSlot>
|
||||
|
||||
## Usage
|
||||
<CatalogSlot slot="install">
|
||||
|
||||
After installing, add the block to your host composition:
|
||||
<InstallCommand command="npx hyperframes add data-chart" item="data-chart" />
|
||||
|
||||
```html
|
||||
<div data-composition-id="data-chart" data-composition-src="compositions/data-chart.html" data-start="0" data-duration="15" data-track-index="1" data-width="1920" data-height="1080"></div>
|
||||
That writes one file: `compositions/data-chart.html`.
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
<CatalogSlot slot="docs">
|
||||
|
||||
## Add it to your video
|
||||
|
||||
It runs for 15 seconds at 1920×1080. Paste this into your composition:
|
||||
|
||||
```html index.html
|
||||
<div
|
||||
data-composition-id="data-chart"
|
||||
data-composition-src="compositions/data-chart.html"
|
||||
data-start="0"
|
||||
data-duration="15"
|
||||
data-track-index="1"
|
||||
data-width="1920"
|
||||
data-height="1080"
|
||||
></div>
|
||||
```
|
||||
|
||||
Move it in time with `data-start`. Put it on a different timeline row with
|
||||
`data-track-index`. See [data attributes](/concepts/data-attributes) for the rest.
|
||||
|
||||
## Change the colors
|
||||
|
||||
Set these CSS variables on the block:
|
||||
|
||||
- `--bg-color` — Background. Defaults to `#faf9f6`.
|
||||
- `--text-color` — Text color. Defaults to `#333333`.
|
||||
|
||||
{/* hf:generated-footer */}
|
||||
|
||||
Tagged `data` `chart` `statistics`.
|
||||
@@ -508,3 +529,7 @@ Tagged `data` `chart` `statistics`.
|
||||
- [Browse the complete Catalog](/catalog)
|
||||
- [Add assets and Catalog items in Studio](/studio/assets-and-blocks)
|
||||
- [Build a richer composition](/go-further)
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
</CatalogDetail>
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
---
|
||||
title: "Domain Warp Dissolve"
|
||||
description: "Shader transition with fractal noise domain warping"
|
||||
mode: "frame"
|
||||
---
|
||||
|
||||
import { CatalogDetail, CatalogSlot } from "/snippets/catalog-detail.jsx";
|
||||
import { InstallCommand } from "/snippets/install-command.jsx";
|
||||
|
||||
## Install
|
||||
<CatalogDetail
|
||||
previewSrc="/public/catalog/blocks/domain-warp-dissolve.json"
|
||||
compositionId="domain-warp-dissolve"
|
||||
compositionSrc="compositions/domain-warp-dissolve.html"
|
||||
title="Domain Warp Dissolve"
|
||||
description="Shader transition with fractal noise domain warping"
|
||||
variables={[]}
|
||||
meta={{"duration":4,"width":1920,"height":1080,"category":"Shader Transitions","badge":"Stable","codeLines":380}}
|
||||
attribution={{"path":"registry/blocks/domain-warp-dissolve","tags":["transition","shader"]}}
|
||||
hasCode
|
||||
rawUrl="https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/domain-warp-dissolve/domain-warp-dissolve.html"
|
||||
>
|
||||
|
||||
<InstallCommand command="npx hyperframes add domain-warp-dissolve" item="domain-warp-dissolve" />
|
||||
<CatalogSlot slot="code">
|
||||
|
||||
That writes one file: `compositions/domain-warp-dissolve.html`.
|
||||
|
||||
<iframe
|
||||
className="w-full aspect-video rounded-xl border-0 bg-zinc-100 dark:bg-zinc-800"
|
||||
title="domain-warp-dissolve preview"
|
||||
loading="lazy"
|
||||
srcDoc={`<!doctype html><html><head><meta charset="utf-8"><style>html,body{margin:0;height:100%;overflow:hidden;background:transparent}hyperframes-player{display:block;width:100%;height:100%}</style><script src="https://cdn.jsdelivr.net/npm/@hyperframes/player@latest/dist/hyperframes-player.global.js"><\/script></head><body><script>fetch("/public/catalog/blocks/domain-warp-dissolve.json").then(function(r){return r.json()}).then(function(d){var p=document.createElement("hyperframes-player");p.setAttribute("srcdoc",d.html);p.setAttribute("controls","");p.setAttribute("autoplay","");p.setAttribute("loop","");p.setAttribute("muted","");p.setAttribute("poster","https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/domain-warp-dissolve.png");document.body.appendChild(p)});<\/script></body></html>`}
|
||||
/>
|
||||
|
||||
## Source
|
||||
|
||||
<Accordion title="domain-warp-dissolve.html">
|
||||
|
||||
```html
|
||||
```html domain-warp-dissolve.html
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
@@ -405,16 +405,37 @@ That writes one file: `compositions/domain-warp-dissolve.html`.
|
||||
</html>
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
</CatalogSlot>
|
||||
|
||||
## Usage
|
||||
<CatalogSlot slot="install">
|
||||
|
||||
After installing, add the block to your host composition:
|
||||
<InstallCommand command="npx hyperframes add domain-warp-dissolve" item="domain-warp-dissolve" />
|
||||
|
||||
```html
|
||||
<div data-composition-id="domain-warp-dissolve" data-composition-src="compositions/domain-warp-dissolve.html" data-start="0" data-duration="4" data-track-index="1" data-width="1920" data-height="1080"></div>
|
||||
That writes one file: `compositions/domain-warp-dissolve.html`.
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
<CatalogSlot slot="docs">
|
||||
|
||||
## Add it to your video
|
||||
|
||||
It runs for 4 seconds at 1920×1080. Paste this into your composition:
|
||||
|
||||
```html index.html
|
||||
<div
|
||||
data-composition-id="domain-warp-dissolve"
|
||||
data-composition-src="compositions/domain-warp-dissolve.html"
|
||||
data-start="0"
|
||||
data-duration="4"
|
||||
data-track-index="1"
|
||||
data-width="1920"
|
||||
data-height="1080"
|
||||
></div>
|
||||
```
|
||||
|
||||
Move it in time with `data-start`. Put it on a different timeline row with
|
||||
`data-track-index`. See [data attributes](/concepts/data-attributes) for the rest.
|
||||
|
||||
{/* hf:generated-footer */}
|
||||
|
||||
Tagged `transition` `shader`.
|
||||
@@ -424,3 +445,7 @@ Tagged `transition` `shader`.
|
||||
- [Browse the complete Catalog](/catalog)
|
||||
- [Add assets and Catalog items in Studio](/studio/assets-and-blocks)
|
||||
- [Build a richer composition](/go-further)
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
</CatalogDetail>
|
||||
|
||||
@@ -1,47 +1,28 @@
|
||||
---
|
||||
title: "Editorial Flash Overlay"
|
||||
description: "Finite neutral-warm light layers for a seek-safe camera-flash cut or social reveal"
|
||||
mode: "frame"
|
||||
---
|
||||
|
||||
import { CatalogDetail, CatalogSlot } from "/snippets/catalog-detail.jsx";
|
||||
import { InstallCommand } from "/snippets/install-command.jsx";
|
||||
|
||||
## Install
|
||||
<CatalogDetail
|
||||
previewSrc="/public/catalog/blocks/editorial-flash-overlay.json"
|
||||
compositionId="editorial-flash-overlay"
|
||||
compositionSrc="compositions/editorial-flash-overlay.html"
|
||||
title="Editorial Flash Overlay"
|
||||
description="Finite neutral-warm light layers for a seek-safe camera-flash cut or social reveal"
|
||||
variables={[]}
|
||||
meta={{"duration":4,"width":1920,"height":1080,"category":"Social Overlays","badge":"Stable","codeLines":129}}
|
||||
attribution={{"path":"registry/blocks/editorial-flash-overlay","tags":["flash","editorial","social","light","transition","overlay","media-treatment-overlay"]}}
|
||||
hasCode
|
||||
rawUrl="https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/editorial-flash-overlay/editorial-flash-overlay.html"
|
||||
>
|
||||
|
||||
<InstallCommand command="npx hyperframes add editorial-flash-overlay" item="editorial-flash-overlay" />
|
||||
<CatalogSlot slot="code">
|
||||
|
||||
That writes one file: `compositions/editorial-flash-overlay.html`.
|
||||
|
||||
<iframe
|
||||
className="w-full aspect-video rounded-xl border-0 bg-zinc-100 dark:bg-zinc-800"
|
||||
title="editorial-flash-overlay preview"
|
||||
loading="lazy"
|
||||
srcDoc={`<!doctype html><html><head><meta charset="utf-8"><style>html,body{margin:0;height:100%;overflow:hidden;background:transparent}hyperframes-player{display:block;width:100%;height:100%}</style><script src="https://cdn.jsdelivr.net/npm/@hyperframes/player@latest/dist/hyperframes-player.global.js"><\/script></head><body><script>fetch("/public/catalog/blocks/editorial-flash-overlay.json").then(function(r){return r.json()}).then(function(d){var p=document.createElement("hyperframes-player");p.setAttribute("srcdoc",d.html);p.setAttribute("controls","");p.setAttribute("autoplay","");p.setAttribute("loop","");p.setAttribute("muted","");p.setAttribute("poster","https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/editorial-flash-overlay.png");document.body.appendChild(p)});<\/script></body></html>`}
|
||||
/>
|
||||
|
||||
## Add it to your video
|
||||
|
||||
It runs for 4 seconds at 1920×1080. Paste this into your composition:
|
||||
|
||||
```html index.html
|
||||
<div
|
||||
data-composition-id="editorial-flash-overlay"
|
||||
data-composition-src="compositions/editorial-flash-overlay.html"
|
||||
data-start="0"
|
||||
data-duration="4"
|
||||
data-track-index="1"
|
||||
data-width="1920"
|
||||
data-height="1080"
|
||||
></div>
|
||||
```
|
||||
|
||||
Move it in time with `data-start`. Put it on a different timeline row with
|
||||
`data-track-index`. See [data attributes](/concepts/data-attributes) for the rest.
|
||||
|
||||
## Source
|
||||
|
||||
<Accordion title="editorial-flash-overlay.html">
|
||||
|
||||
```html
|
||||
```html editorial-flash-overlay.html
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
@@ -173,7 +154,36 @@ Move it in time with `data-start`. Put it on a different timeline row with
|
||||
</html>
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
</CatalogSlot>
|
||||
|
||||
<CatalogSlot slot="install">
|
||||
|
||||
<InstallCommand command="npx hyperframes add editorial-flash-overlay" item="editorial-flash-overlay" />
|
||||
|
||||
That writes one file: `compositions/editorial-flash-overlay.html`.
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
<CatalogSlot slot="docs">
|
||||
|
||||
## Add it to your video
|
||||
|
||||
It runs for 4 seconds at 1920×1080. Paste this into your composition:
|
||||
|
||||
```html index.html
|
||||
<div
|
||||
data-composition-id="editorial-flash-overlay"
|
||||
data-composition-src="compositions/editorial-flash-overlay.html"
|
||||
data-start="0"
|
||||
data-duration="4"
|
||||
data-track-index="1"
|
||||
data-width="1920"
|
||||
data-height="1080"
|
||||
></div>
|
||||
```
|
||||
|
||||
Move it in time with `data-start`. Put it on a different timeline row with
|
||||
`data-track-index`. See [data attributes](/concepts/data-attributes) for the rest.
|
||||
|
||||
{/* hf:generated-footer */}
|
||||
|
||||
@@ -184,3 +194,7 @@ Tagged `flash` `editorial` `social` `light` `transition` `overlay` `media-treatm
|
||||
- [Browse the complete Catalog](/catalog)
|
||||
- [Add assets and Catalog items in Studio](/studio/assets-and-blocks)
|
||||
- [Build a richer composition](/go-further)
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
</CatalogDetail>
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
---
|
||||
title: "Flash Through White"
|
||||
description: "Shader transition with white flash crossfade"
|
||||
mode: "frame"
|
||||
---
|
||||
|
||||
import { CatalogDetail, CatalogSlot } from "/snippets/catalog-detail.jsx";
|
||||
import { InstallCommand } from "/snippets/install-command.jsx";
|
||||
|
||||
## Install
|
||||
<CatalogDetail
|
||||
previewSrc="/public/catalog/blocks/flash-through-white.json"
|
||||
compositionId="flash-through-white"
|
||||
compositionSrc="compositions/flash-through-white.html"
|
||||
title="Flash Through White"
|
||||
description="Shader transition with white flash crossfade"
|
||||
variables={[]}
|
||||
meta={{"duration":4,"width":1920,"height":1080,"category":"Shader Transitions","badge":"Stable","codeLines":366}}
|
||||
attribution={{"path":"registry/blocks/flash-through-white","tags":["transition","shader"]}}
|
||||
hasCode
|
||||
rawUrl="https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/flash-through-white/flash-through-white.html"
|
||||
>
|
||||
|
||||
<InstallCommand command="npx hyperframes add flash-through-white" item="flash-through-white" />
|
||||
<CatalogSlot slot="code">
|
||||
|
||||
That writes one file: `compositions/flash-through-white.html`.
|
||||
|
||||
<iframe
|
||||
className="w-full aspect-video rounded-xl border-0 bg-zinc-100 dark:bg-zinc-800"
|
||||
title="flash-through-white preview"
|
||||
loading="lazy"
|
||||
srcDoc={`<!doctype html><html><head><meta charset="utf-8"><style>html,body{margin:0;height:100%;overflow:hidden;background:transparent}hyperframes-player{display:block;width:100%;height:100%}</style><script src="https://cdn.jsdelivr.net/npm/@hyperframes/player@latest/dist/hyperframes-player.global.js"><\/script></head><body><script>fetch("/public/catalog/blocks/flash-through-white.json").then(function(r){return r.json()}).then(function(d){var p=document.createElement("hyperframes-player");p.setAttribute("srcdoc",d.html);p.setAttribute("controls","");p.setAttribute("autoplay","");p.setAttribute("loop","");p.setAttribute("muted","");p.setAttribute("poster","https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/flash-through-white.png");document.body.appendChild(p)});<\/script></body></html>`}
|
||||
/>
|
||||
|
||||
## Source
|
||||
|
||||
<Accordion title="flash-through-white.html">
|
||||
|
||||
```html
|
||||
```html flash-through-white.html
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
@@ -391,16 +391,37 @@ That writes one file: `compositions/flash-through-white.html`.
|
||||
</html>
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
</CatalogSlot>
|
||||
|
||||
## Usage
|
||||
<CatalogSlot slot="install">
|
||||
|
||||
After installing, add the block to your host composition:
|
||||
<InstallCommand command="npx hyperframes add flash-through-white" item="flash-through-white" />
|
||||
|
||||
```html
|
||||
<div data-composition-id="flash-through-white" data-composition-src="compositions/flash-through-white.html" data-start="0" data-duration="4" data-track-index="1" data-width="1920" data-height="1080"></div>
|
||||
That writes one file: `compositions/flash-through-white.html`.
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
<CatalogSlot slot="docs">
|
||||
|
||||
## Add it to your video
|
||||
|
||||
It runs for 4 seconds at 1920×1080. Paste this into your composition:
|
||||
|
||||
```html index.html
|
||||
<div
|
||||
data-composition-id="flash-through-white"
|
||||
data-composition-src="compositions/flash-through-white.html"
|
||||
data-start="0"
|
||||
data-duration="4"
|
||||
data-track-index="1"
|
||||
data-width="1920"
|
||||
data-height="1080"
|
||||
></div>
|
||||
```
|
||||
|
||||
Move it in time with `data-start`. Put it on a different timeline row with
|
||||
`data-track-index`. See [data attributes](/concepts/data-attributes) for the rest.
|
||||
|
||||
{/* hf:generated-footer */}
|
||||
|
||||
Tagged `transition` `shader`.
|
||||
@@ -410,3 +431,7 @@ Tagged `transition` `shader`.
|
||||
- [Browse the complete Catalog](/catalog)
|
||||
- [Add assets and Catalog items in Studio](/studio/assets-and-blocks)
|
||||
- [Build a richer composition](/go-further)
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
</CatalogDetail>
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
---
|
||||
title: "Flowchart Vertical"
|
||||
description: "Portrait animated decision tree with SVG connectors, sticky-note nodes, cursor interaction, and typing correction"
|
||||
mode: "frame"
|
||||
---
|
||||
|
||||
import { CatalogDetail, CatalogSlot } from "/snippets/catalog-detail.jsx";
|
||||
import { InstallCommand } from "/snippets/install-command.jsx";
|
||||
|
||||
## Install
|
||||
<CatalogDetail
|
||||
previewSrc="/public/catalog/blocks/flowchart-vertical.json"
|
||||
compositionId="flowchart-vertical"
|
||||
compositionSrc="compositions/flowchart-vertical.html"
|
||||
title="Flowchart Vertical"
|
||||
description="Portrait animated decision tree with SVG connectors, sticky-note nodes, cursor interaction, and typing correction"
|
||||
variables={[]}
|
||||
meta={{"duration":12,"width":1440,"height":2560,"category":"Blocks","badge":"Stable","codeLines":459}}
|
||||
attribution={{"path":"registry/blocks/flowchart-vertical","tags":["diagram","flowchart","interactive","portrait"]}}
|
||||
hasCode
|
||||
rawUrl="https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/flowchart-vertical/flowchart-vertical.html"
|
||||
>
|
||||
|
||||
<InstallCommand command="npx hyperframes add flowchart-vertical" item="flowchart-vertical" />
|
||||
<CatalogSlot slot="code">
|
||||
|
||||
That writes one file: `compositions/flowchart-vertical.html`.
|
||||
|
||||
<iframe
|
||||
className="w-full aspect-video rounded-xl border-0 bg-zinc-100 dark:bg-zinc-800"
|
||||
title="flowchart-vertical preview"
|
||||
loading="lazy"
|
||||
srcDoc={`<!doctype html><html><head><meta charset="utf-8"><style>html,body{margin:0;height:100%;overflow:hidden;background:transparent}hyperframes-player{display:block;width:100%;height:100%}</style><script src="https://cdn.jsdelivr.net/npm/@hyperframes/player@latest/dist/hyperframes-player.global.js"><\/script></head><body><script>fetch("/public/catalog/blocks/flowchart-vertical.json").then(function(r){return r.json()}).then(function(d){var p=document.createElement("hyperframes-player");p.setAttribute("srcdoc",d.html);p.setAttribute("controls","");p.setAttribute("autoplay","");p.setAttribute("loop","");p.setAttribute("muted","");p.setAttribute("poster","https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/flowchart-vertical.png");document.body.appendChild(p)});<\/script></body></html>`}
|
||||
/>
|
||||
|
||||
## Source
|
||||
|
||||
<Accordion title="flowchart-vertical.html">
|
||||
|
||||
```html
|
||||
```html flowchart-vertical.html
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
@@ -484,16 +484,37 @@ That writes one file: `compositions/flowchart-vertical.html`.
|
||||
</html>
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
</CatalogSlot>
|
||||
|
||||
## Usage
|
||||
<CatalogSlot slot="install">
|
||||
|
||||
After installing, add the block to your host composition:
|
||||
<InstallCommand command="npx hyperframes add flowchart-vertical" item="flowchart-vertical" />
|
||||
|
||||
```html
|
||||
<div data-composition-id="flowchart-vertical" data-composition-src="compositions/flowchart-vertical.html" data-start="0" data-duration="12" data-track-index="1" data-width="1440" data-height="2560"></div>
|
||||
That writes one file: `compositions/flowchart-vertical.html`.
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
<CatalogSlot slot="docs">
|
||||
|
||||
## Add it to your video
|
||||
|
||||
It runs for 12 seconds at 1440×2560. Paste this into your composition:
|
||||
|
||||
```html index.html
|
||||
<div
|
||||
data-composition-id="flowchart-vertical"
|
||||
data-composition-src="compositions/flowchart-vertical.html"
|
||||
data-start="0"
|
||||
data-duration="12"
|
||||
data-track-index="1"
|
||||
data-width="1440"
|
||||
data-height="2560"
|
||||
></div>
|
||||
```
|
||||
|
||||
Move it in time with `data-start`. Put it on a different timeline row with
|
||||
`data-track-index`. See [data attributes](/concepts/data-attributes) for the rest.
|
||||
|
||||
{/* hf:generated-footer */}
|
||||
|
||||
Tagged `diagram` `flowchart` `interactive` `portrait`.
|
||||
@@ -503,3 +524,7 @@ Tagged `diagram` `flowchart` `interactive` `portrait`.
|
||||
- [Browse the complete Catalog](/catalog)
|
||||
- [Add assets and Catalog items in Studio](/studio/assets-and-blocks)
|
||||
- [Build a richer composition](/go-further)
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
</CatalogDetail>
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
---
|
||||
title: "Flowchart"
|
||||
description: "Animated decision tree with SVG connectors, sticky-note nodes, cursor interaction, and typing correction"
|
||||
mode: "frame"
|
||||
---
|
||||
|
||||
import { CatalogDetail, CatalogSlot } from "/snippets/catalog-detail.jsx";
|
||||
import { InstallCommand } from "/snippets/install-command.jsx";
|
||||
|
||||
## Install
|
||||
<CatalogDetail
|
||||
previewSrc="/public/catalog/blocks/flowchart.json"
|
||||
compositionId="flowchart"
|
||||
compositionSrc="compositions/flowchart.html"
|
||||
title="Flowchart"
|
||||
description="Animated decision tree with SVG connectors, sticky-note nodes, cursor interaction, and typing correction"
|
||||
variables={[]}
|
||||
meta={{"duration":12,"width":1920,"height":1080,"category":"Blocks","badge":"Stable","codeLines":459}}
|
||||
attribution={{"path":"registry/blocks/flowchart","tags":["diagram","flowchart","interactive"]}}
|
||||
hasCode
|
||||
rawUrl="https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/flowchart/flowchart.html"
|
||||
>
|
||||
|
||||
<InstallCommand command="npx hyperframes add flowchart" item="flowchart" />
|
||||
<CatalogSlot slot="code">
|
||||
|
||||
That writes one file: `compositions/flowchart.html`.
|
||||
|
||||
<iframe
|
||||
className="w-full aspect-video rounded-xl border-0 bg-zinc-100 dark:bg-zinc-800"
|
||||
title="flowchart preview"
|
||||
loading="lazy"
|
||||
srcDoc={`<!doctype html><html><head><meta charset="utf-8"><style>html,body{margin:0;height:100%;overflow:hidden;background:transparent}hyperframes-player{display:block;width:100%;height:100%}</style><script src="https://cdn.jsdelivr.net/npm/@hyperframes/player@latest/dist/hyperframes-player.global.js"><\/script></head><body><script>fetch("/public/catalog/blocks/flowchart.json").then(function(r){return r.json()}).then(function(d){var p=document.createElement("hyperframes-player");p.setAttribute("srcdoc",d.html);p.setAttribute("controls","");p.setAttribute("autoplay","");p.setAttribute("loop","");p.setAttribute("muted","");p.setAttribute("poster","https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/flowchart.png");document.body.appendChild(p)});<\/script></body></html>`}
|
||||
/>
|
||||
|
||||
## Source
|
||||
|
||||
<Accordion title="flowchart.html">
|
||||
|
||||
```html
|
||||
```html flowchart.html
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
@@ -484,16 +484,37 @@ That writes one file: `compositions/flowchart.html`.
|
||||
</html>
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
</CatalogSlot>
|
||||
|
||||
## Usage
|
||||
<CatalogSlot slot="install">
|
||||
|
||||
After installing, add the block to your host composition:
|
||||
<InstallCommand command="npx hyperframes add flowchart" item="flowchart" />
|
||||
|
||||
```html
|
||||
<div data-composition-id="flowchart" data-composition-src="compositions/flowchart.html" data-start="0" data-duration="12" data-track-index="1" data-width="1920" data-height="1080"></div>
|
||||
That writes one file: `compositions/flowchart.html`.
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
<CatalogSlot slot="docs">
|
||||
|
||||
## Add it to your video
|
||||
|
||||
It runs for 12 seconds at 1920×1080. Paste this into your composition:
|
||||
|
||||
```html index.html
|
||||
<div
|
||||
data-composition-id="flowchart"
|
||||
data-composition-src="compositions/flowchart.html"
|
||||
data-start="0"
|
||||
data-duration="12"
|
||||
data-track-index="1"
|
||||
data-width="1920"
|
||||
data-height="1080"
|
||||
></div>
|
||||
```
|
||||
|
||||
Move it in time with `data-start`. Put it on a different timeline row with
|
||||
`data-track-index`. See [data attributes](/concepts/data-attributes) for the rest.
|
||||
|
||||
{/* hf:generated-footer */}
|
||||
|
||||
Tagged `diagram` `flowchart` `interactive`.
|
||||
@@ -503,3 +524,7 @@ Tagged `diagram` `flowchart` `interactive`.
|
||||
- [Browse the complete Catalog](/catalog)
|
||||
- [Add assets and Catalog items in Studio](/studio/assets-and-blocks)
|
||||
- [Build a richer composition](/go-further)
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
</CatalogDetail>
|
||||
|
||||
@@ -1,47 +1,28 @@
|
||||
---
|
||||
title: "Freeze-Frame Dressing"
|
||||
description: "Timeline-driven paper, tape, and flash dressing for a freeze-frame or background-removed subject"
|
||||
mode: "frame"
|
||||
---
|
||||
|
||||
import { CatalogDetail, CatalogSlot } from "/snippets/catalog-detail.jsx";
|
||||
import { InstallCommand } from "/snippets/install-command.jsx";
|
||||
|
||||
## Install
|
||||
<CatalogDetail
|
||||
previewSrc="/public/catalog/blocks/freeze-frame-dressing.json"
|
||||
compositionId="freeze-frame-dressing"
|
||||
compositionSrc="compositions/freeze-frame-dressing.html"
|
||||
title="Freeze-Frame Dressing"
|
||||
description="Timeline-driven paper, tape, and flash dressing for a freeze-frame or background-removed subject"
|
||||
variables={[]}
|
||||
meta={{"duration":4,"width":1920,"height":1080,"category":"Social Overlays","badge":"Stable","codeLines":120}}
|
||||
attribution={{"path":"registry/blocks/freeze-frame-dressing","tags":["freeze-frame","cutout","scrapbook","paper","social","overlay","media-treatment-overlay"]}}
|
||||
hasCode
|
||||
rawUrl="https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/freeze-frame-dressing/freeze-frame-dressing.html"
|
||||
>
|
||||
|
||||
<InstallCommand command="npx hyperframes add freeze-frame-dressing" item="freeze-frame-dressing" />
|
||||
<CatalogSlot slot="code">
|
||||
|
||||
That writes one file: `compositions/freeze-frame-dressing.html`.
|
||||
|
||||
<iframe
|
||||
className="w-full aspect-video rounded-xl border-0 bg-zinc-100 dark:bg-zinc-800"
|
||||
title="freeze-frame-dressing preview"
|
||||
loading="lazy"
|
||||
srcDoc={`<!doctype html><html><head><meta charset="utf-8"><style>html,body{margin:0;height:100%;overflow:hidden;background:transparent}hyperframes-player{display:block;width:100%;height:100%}</style><script src="https://cdn.jsdelivr.net/npm/@hyperframes/player@latest/dist/hyperframes-player.global.js"><\/script></head><body><script>fetch("/public/catalog/blocks/freeze-frame-dressing.json").then(function(r){return r.json()}).then(function(d){var p=document.createElement("hyperframes-player");p.setAttribute("srcdoc",d.html);p.setAttribute("controls","");p.setAttribute("autoplay","");p.setAttribute("loop","");p.setAttribute("muted","");p.setAttribute("poster","https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/freeze-frame-dressing.png");document.body.appendChild(p)});<\/script></body></html>`}
|
||||
/>
|
||||
|
||||
## Add it to your video
|
||||
|
||||
It runs for 4 seconds at 1920×1080. Paste this into your composition:
|
||||
|
||||
```html index.html
|
||||
<div
|
||||
data-composition-id="freeze-frame-dressing"
|
||||
data-composition-src="compositions/freeze-frame-dressing.html"
|
||||
data-start="0"
|
||||
data-duration="4"
|
||||
data-track-index="1"
|
||||
data-width="1920"
|
||||
data-height="1080"
|
||||
></div>
|
||||
```
|
||||
|
||||
Move it in time with `data-start`. Put it on a different timeline row with
|
||||
`data-track-index`. See [data attributes](/concepts/data-attributes) for the rest.
|
||||
|
||||
## Source
|
||||
|
||||
<Accordion title="freeze-frame-dressing.html">
|
||||
|
||||
```html
|
||||
```html freeze-frame-dressing.html
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
@@ -164,7 +145,36 @@ Move it in time with `data-start`. Put it on a different timeline row with
|
||||
</html>
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
</CatalogSlot>
|
||||
|
||||
<CatalogSlot slot="install">
|
||||
|
||||
<InstallCommand command="npx hyperframes add freeze-frame-dressing" item="freeze-frame-dressing" />
|
||||
|
||||
That writes one file: `compositions/freeze-frame-dressing.html`.
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
<CatalogSlot slot="docs">
|
||||
|
||||
## Add it to your video
|
||||
|
||||
It runs for 4 seconds at 1920×1080. Paste this into your composition:
|
||||
|
||||
```html index.html
|
||||
<div
|
||||
data-composition-id="freeze-frame-dressing"
|
||||
data-composition-src="compositions/freeze-frame-dressing.html"
|
||||
data-start="0"
|
||||
data-duration="4"
|
||||
data-track-index="1"
|
||||
data-width="1920"
|
||||
data-height="1080"
|
||||
></div>
|
||||
```
|
||||
|
||||
Move it in time with `data-start`. Put it on a different timeline row with
|
||||
`data-track-index`. See [data attributes](/concepts/data-attributes) for the rest.
|
||||
|
||||
{/* hf:generated-footer */}
|
||||
|
||||
@@ -175,3 +185,7 @@ Tagged `freeze-frame` `cutout` `scrapbook` `paper` `social` `overlay` `media-tre
|
||||
- [Browse the complete Catalog](/catalog)
|
||||
- [Add assets and Catalog items in Studio](/studio/assets-and-blocks)
|
||||
- [Build a richer composition](/go-further)
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
</CatalogDetail>
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,24 +1,27 @@
|
||||
---
|
||||
title: "Gallery Tunnel"
|
||||
description: "An infinite corridor of panels: a square tunnel receding to a vanishing point, its floor, ceiling and walls tiled with saturated colour slabs and caller-supplied images that swell toward camera and exit past you, the far end dissolving into fog."
|
||||
mode: "frame"
|
||||
---
|
||||
|
||||
import { CatalogDetail, CatalogSlot } from "/snippets/catalog-detail.jsx";
|
||||
import { InstallCommand } from "/snippets/install-command.jsx";
|
||||
import { VariablesExplorer } from "/snippets/variables-explorer.jsx";
|
||||
|
||||
## Install
|
||||
|
||||
<InstallCommand command="npx hyperframes add gallery-tunnel" item="gallery-tunnel" />
|
||||
|
||||
That writes one file: `compositions/gallery-tunnel.html`.
|
||||
|
||||
<VariablesExplorer
|
||||
<CatalogDetail
|
||||
previewSrc="/public/catalog/blocks/gallery-tunnel.json"
|
||||
compositionId="gallery-tunnel"
|
||||
compositionSrc="compositions/gallery-tunnel.html"
|
||||
title="Gallery Tunnel"
|
||||
description="An infinite corridor of panels: a square tunnel receding to a vanishing point, its floor, ceiling and walls tiled with saturated colour slabs and caller-supplied images that swell toward camera and exit past you, the far end dissolving into fog."
|
||||
variables={[{"id":"images","type":"string","label":"Image slots: comma-separated paths, tiled into the panels (e.g. assets/shot-1.png,assets/shot-2.png)","default":"","placeholder":"assets/shot-1.png,assets/shot-2.png"},{"id":"imageMix","type":"number","label":"Image share of filled slots (0 = all colour, 1 = all images)","default":0.5,"min":0,"max":1,"step":0.05},{"id":"speed","type":"number","label":"Flight speed","default":3,"min":0,"max":8,"step":0.1,"unit":"segments/s"},{"id":"palette","type":"string","label":"Slab palette: comma-separated hex colours","default":"#FF6A00,#AB54F7,#EA3737,#0072E3,#00AA3C,#FFB200"},{"id":"grid","type":"number","label":"Grid density: slots per wall face per axis (2 = 4 per face, 16 per segment)","default":2,"min":1,"max":4,"step":1},{"id":"fill","type":"number","label":"Slot fill rate","default":0.5,"min":0,"max":1,"step":0.05},{"id":"fog","type":"number","label":"Fog distance: depth at which the corridor reaches pure backdrop","default":14.25,"min":3,"max":14.5,"step":0.25,"unit":"segments"},{"id":"edgeColor","type":"color","label":"Edge line colour","default":"#B0B0B0"},{"id":"edgeOpacity","type":"number","label":"Edge line opacity","default":0.5,"min":0,"max":1,"step":0.05},{"id":"backdrop","type":"color","label":"Backdrop and fog colour","default":"#000000"}]}
|
||||
meta={{"duration":12,"width":1920,"height":1080,"category":"Showcases","badge":"Experimental","codeLines":454}}
|
||||
attribution={{"path":"registry/blocks/gallery-tunnel","tags":["webgl","3d","background","showcase","depth","loop"]}}
|
||||
hasCode
|
||||
rawUrl="https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/gallery-tunnel/gallery-tunnel.html"
|
||||
>
|
||||
|
||||
<CatalogSlot slot="code">
|
||||
|
||||
```html gallery-tunnel.html
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
@@ -476,7 +479,17 @@ That writes one file: `compositions/gallery-tunnel.html`.
|
||||
</html>
|
||||
```
|
||||
|
||||
</VariablesExplorer>
|
||||
</CatalogSlot>
|
||||
|
||||
<CatalogSlot slot="install">
|
||||
|
||||
<InstallCommand command="npx hyperframes add gallery-tunnel" item="gallery-tunnel" />
|
||||
|
||||
That writes one file: `compositions/gallery-tunnel.html`.
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
<CatalogSlot slot="docs">
|
||||
|
||||
## Add it to your video
|
||||
|
||||
@@ -541,469 +554,6 @@ defaults, so this behaves exactly like the preview above until you change one:
|
||||
></div>
|
||||
```
|
||||
|
||||
## Source
|
||||
|
||||
<Accordion title="gallery-tunnel.html">
|
||||
|
||||
```html
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=1920, height=1080" />
|
||||
<title>Gallery Tunnel</title>
|
||||
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/three@0.147.0/build/three.min.js"></script>
|
||||
<style>
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
body {
|
||||
background: #000;
|
||||
overflow: hidden;
|
||||
}
|
||||
#gt-root {
|
||||
position: relative;
|
||||
width: 1920px;
|
||||
height: 1080px;
|
||||
overflow: hidden;
|
||||
}
|
||||
#gt-backdrop {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: #000000;
|
||||
}
|
||||
#gt-canvas {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 1920px;
|
||||
height: 1080px;
|
||||
}
|
||||
/* Off-screen decode well: the <img> elements live in the DOM so the page
|
||||
load event waits for them, but they are never composited themselves —
|
||||
WebGL reads them as textures. The well is clipped to 1px; the images
|
||||
inside keep their natural layout size on purpose, because texImage2D
|
||||
uploads an <img> at its LAID-OUT size — sizing them down with CSS
|
||||
uploads a 1x1 texture and every panel samples one dark pixel. */
|
||||
#gt-assets {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<!--
|
||||
Content slots. Every panel in the corridor is a slot, and `images` fills a
|
||||
share of them with your own artwork — drop the files in the project's
|
||||
assets/ and list them, either by editing the `images` default below or from
|
||||
the host composition:
|
||||
|
||||
<div
|
||||
data-composition-id="gallery-tunnel"
|
||||
data-composition-src="compositions/gallery-tunnel.html"
|
||||
data-start="0" data-duration="12" data-track-index="0"
|
||||
data-width="1920" data-height="1080"
|
||||
style="--images: assets/shot-1.png, assets/shot-2.png, assets/shot-3.png; --imagemix: 0.7"
|
||||
></div>
|
||||
|
||||
Any number of images works; they are dealt to slots by hash, so one image
|
||||
recurs at different depths rather than appearing once. Each is contain-fit
|
||||
inside its slot, so a 16:9 screenshot keeps its aspect. `imageMix` sets the
|
||||
image-vs-colour split (1 = every filled slot is an image), `fill` how many
|
||||
slots are filled at all.
|
||||
-->
|
||||
<div
|
||||
id="gt-root"
|
||||
data-composition-id="gallery-tunnel"
|
||||
data-root="true"
|
||||
data-width="1920"
|
||||
data-height="1080"
|
||||
data-start="0"
|
||||
data-duration="12"
|
||||
data-composition-variables='[
|
||||
{"id":"images","type":"string","label":"Image slots: comma-separated paths, tiled into the panels (e.g. assets/shot-1.png,assets/shot-2.png)","default":"","placeholder":"assets/shot-1.png,assets/shot-2.png"},
|
||||
{"id":"imageMix","type":"number","label":"Image share of filled slots (0 = all colour, 1 = all images)","default":0.5,"min":0,"max":1,"step":0.05},
|
||||
{"id":"speed","type":"number","label":"Flight speed","default":3,"min":0,"max":8,"step":0.1,"unit":"segments/s"},
|
||||
{"id":"palette","type":"string","label":"Slab palette: comma-separated hex colours","default":"#FF6A00,#AB54F7,#EA3737,#0072E3,#00AA3C,#FFB200"},
|
||||
{"id":"grid","type":"number","label":"Grid density: slots per wall face per axis (2 = 4 per face, 16 per segment)","default":2,"min":1,"max":4,"step":1},
|
||||
{"id":"fill","type":"number","label":"Slot fill rate","default":0.5,"min":0,"max":1,"step":0.05},
|
||||
{"id":"fog","type":"number","label":"Fog distance: depth at which the corridor reaches pure backdrop","default":14.25,"min":3,"max":14.5,"step":0.25,"unit":"segments"},
|
||||
{"id":"edgeColor","type":"color","label":"Edge line colour","default":"#B0B0B0"},
|
||||
{"id":"edgeOpacity","type":"number","label":"Edge line opacity","default":0.5,"min":0,"max":1,"step":0.05},
|
||||
{"id":"backdrop","type":"color","label":"Backdrop and fog colour","default":"#000000"}
|
||||
]'
|
||||
>
|
||||
<div id="gt-backdrop"></div>
|
||||
<canvas id="gt-canvas" width="1920" height="1080"></canvas>
|
||||
<div id="gt-assets" aria-hidden="true" data-layout-allow-overflow></div>
|
||||
|
||||
<!-- Driver clip: gives HyperFrames a timed element to own on track 0. -->
|
||||
<div
|
||||
id="gt-drv"
|
||||
class="clip"
|
||||
data-start="0"
|
||||
data-duration="12"
|
||||
data-track-index="0"
|
||||
style="position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none"
|
||||
></div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function () {
|
||||
var DUR = 12;
|
||||
var W = 1920;
|
||||
var H = 1080;
|
||||
|
||||
// Fixed tunnel geometry, measured off the reference.
|
||||
var TUNNEL_WIDTH = 2;
|
||||
var TUNNEL_HEIGHT = 1.8;
|
||||
var SEGMENT_DEPTH = 1;
|
||||
var NUM_SEGMENTS = 15;
|
||||
var LINE_RADIUS = 0.003;
|
||||
var FOV = 75;
|
||||
var INSET = 0.94; // slab gutter inside its cell, so tiles read as discrete
|
||||
var SKIN = 0.002; // push slabs just behind the wall plane so edges stay on top
|
||||
|
||||
var ROOT = document.getElementById("gt-root");
|
||||
var CS = getComputedStyle(ROOT);
|
||||
|
||||
// `data-composition-variables` is the single owner of every declared
|
||||
// default — parse it rather than repeating each default a second time
|
||||
// in JS. `window.__hyperframes` is NOT guaranteed to exist yet when this
|
||||
// inline script runs, so it can only ever be an override, never the
|
||||
// source. Precedence: host CSS custom property, runtime variable bag,
|
||||
// declared default.
|
||||
var DECL = {};
|
||||
JSON.parse(ROOT.getAttribute("data-composition-variables") || "[]").forEach(function (v) {
|
||||
DECL[v.id] = v.default;
|
||||
});
|
||||
var HF = window.__hyperframes;
|
||||
var V = (HF && HF.getVariables && HF.getVariables()) || {};
|
||||
|
||||
function raw(id) {
|
||||
var css = CS.getPropertyValue("--" + id.toLowerCase()).trim();
|
||||
if (css !== "") return css;
|
||||
if (V[id] !== undefined && V[id] !== "") return V[id];
|
||||
return DECL[id];
|
||||
}
|
||||
function num(id) {
|
||||
var n = parseFloat(raw(id));
|
||||
return isFinite(n) ? n : 0;
|
||||
}
|
||||
function str(id) {
|
||||
var s = raw(id);
|
||||
return typeof s === "string" ? s.trim() : "";
|
||||
}
|
||||
function list(id) {
|
||||
return str(id)
|
||||
.split(",")
|
||||
.map(function (s) {
|
||||
return s.trim();
|
||||
})
|
||||
.filter(function (s) {
|
||||
return s !== "";
|
||||
});
|
||||
}
|
||||
|
||||
var SPEED = num("speed");
|
||||
var GRID = Math.max(1, Math.min(4, Math.round(num("grid"))));
|
||||
var FILL = num("fill");
|
||||
var IMAGE_MIX = num("imageMix");
|
||||
var FOG_FAR = Math.max(3, Math.min(14.5, num("fog")));
|
||||
var EDGE_OPACITY = num("edgeOpacity");
|
||||
var EDGE_COLOR = str("edgeColor");
|
||||
var BACKDROP = str("backdrop");
|
||||
var PALETTE = list("palette");
|
||||
var IMAGE_SRCS = list("images");
|
||||
|
||||
document.getElementById("gt-backdrop").style.background = BACKDROP;
|
||||
|
||||
// How far behind the camera a segment travels before it recycles. Tying
|
||||
// it to the fog distance is the whole trick: the recycle boundary lands
|
||||
// at exactly FOG_FAR, so a segment reappears already fogged to pure
|
||||
// backdrop and nothing ever pops in at the far end. One owner for the
|
||||
// invariant — move the fog and the boundary follows.
|
||||
var BACK = Math.max(0, Math.min(1.5, NUM_SEGMENTS * SEGMENT_DEPTH - FOG_FAR));
|
||||
|
||||
// Deterministic 2D integer hash — the only source of "randomness".
|
||||
// Content at frame N is a pure function of (absolute segment, slot).
|
||||
function hash2(a, b) {
|
||||
var h = Math.imul(a | 0, 0x27d4eb2d) ^ Math.imul(b | 0, 0x165667b1);
|
||||
h = Math.imul(h ^ (h >>> 15), 0x2545f491);
|
||||
h ^= h >>> 13;
|
||||
return (h >>> 0) / 4294967296;
|
||||
}
|
||||
|
||||
var THREEJS = window.THREE;
|
||||
var canvas = document.getElementById("gt-canvas");
|
||||
var renderer = new THREEJS.WebGLRenderer({
|
||||
canvas: canvas,
|
||||
antialias: true,
|
||||
alpha: false,
|
||||
preserveDrawingBuffer: true,
|
||||
});
|
||||
renderer.setPixelRatio(1);
|
||||
renderer.setSize(W, H, false);
|
||||
renderer.setClearColor(new THREEJS.Color(BACKDROP), 1);
|
||||
|
||||
var scene = new THREEJS.Scene();
|
||||
scene.fog = new THREEJS.Fog(new THREEJS.Color(BACKDROP), 1, FOG_FAR * SEGMENT_DEPTH);
|
||||
|
||||
// Camera sits still at the origin looking down -z; the corridor moves.
|
||||
// Same picture as a moving camera, and the modulo keeps every coordinate
|
||||
// bounded no matter how long the clip runs.
|
||||
var camera = new THREEJS.PerspectiveCamera(FOV, W / H, 0.05, 40);
|
||||
camera.position.set(0, 0, 0);
|
||||
camera.lookAt(0, 0, -1);
|
||||
|
||||
var PLANE = new THREEJS.PlaneGeometry(1, 1);
|
||||
|
||||
var COLOR_MATS = PALETTE.map(function (hex) {
|
||||
return new THREEJS.MeshBasicMaterial({ color: new THREEJS.Color(hex), fog: true });
|
||||
});
|
||||
if (COLOR_MATS.length === 0) {
|
||||
COLOR_MATS.push(new THREEJS.MeshBasicMaterial({ color: 0xffffff, fog: true }));
|
||||
}
|
||||
|
||||
// Caller-supplied content slots. Real <img> elements (so the page load
|
||||
// event waits for the decode) used directly as WebGL textures.
|
||||
var assets = document.getElementById("gt-assets");
|
||||
var IMAGES = IMAGE_SRCS.map(function (src, k) {
|
||||
var el = document.createElement("img");
|
||||
el.id = "gt-img-" + k;
|
||||
el.alt = "";
|
||||
el.src = src;
|
||||
assets.appendChild(el);
|
||||
|
||||
var tex = new THREEJS.Texture(el);
|
||||
tex.minFilter = THREEJS.LinearFilter;
|
||||
tex.magFilter = THREEJS.LinearFilter;
|
||||
tex.generateMipmaps = false;
|
||||
var rec = {
|
||||
aspect: 0,
|
||||
mat: new THREEJS.MeshBasicMaterial({ map: tex, fog: true }),
|
||||
};
|
||||
function ready() {
|
||||
if (!el.naturalWidth) return;
|
||||
rec.aspect = el.naturalWidth / el.naturalHeight;
|
||||
tex.needsUpdate = true;
|
||||
}
|
||||
el.addEventListener("load", ready);
|
||||
ready(); // a cache hit can complete before the listener is attached
|
||||
return rec;
|
||||
});
|
||||
|
||||
var edgeMat = new THREEJS.MeshBasicMaterial({
|
||||
color: new THREEJS.Color(EDGE_COLOR),
|
||||
fog: true,
|
||||
transparent: true,
|
||||
opacity: EDGE_OPACITY,
|
||||
});
|
||||
|
||||
var HW = TUNNEL_WIDTH / 2;
|
||||
var HH = TUNNEL_HEIGHT / 2;
|
||||
var cellZ = SEGMENT_DEPTH / GRID;
|
||||
var cellX = TUNNEL_WIDTH / GRID;
|
||||
var cellY = TUNNEL_HEIGHT / GRID;
|
||||
|
||||
// Four faces of a segment box. `place` maps a cell to a world position
|
||||
// inside the segment (origin at the segment's near boundary, growing
|
||||
// toward -z); `rot` orients the unit plane into that face, inward-facing.
|
||||
// `cw`/`ch` are the cell's extent in the plane's own local axes.
|
||||
var FACES = [
|
||||
{
|
||||
// floor
|
||||
rot: [-Math.PI / 2, 0, 0],
|
||||
cw: cellX,
|
||||
ch: cellZ,
|
||||
place: function (iu, iz) {
|
||||
return [-HW + (iu + 0.5) * cellX, -HH - SKIN, -(iz + 0.5) * cellZ];
|
||||
},
|
||||
},
|
||||
{
|
||||
// ceiling
|
||||
rot: [Math.PI / 2, 0, 0],
|
||||
cw: cellX,
|
||||
ch: cellZ,
|
||||
place: function (iu, iz) {
|
||||
return [-HW + (iu + 0.5) * cellX, HH + SKIN, -(iz + 0.5) * cellZ];
|
||||
},
|
||||
},
|
||||
{
|
||||
// left wall
|
||||
rot: [0, Math.PI / 2, 0],
|
||||
cw: cellZ,
|
||||
ch: cellY,
|
||||
place: function (iu, iz) {
|
||||
return [-HW - SKIN, -HH + (iu + 0.5) * cellY, -(iz + 0.5) * cellZ];
|
||||
},
|
||||
},
|
||||
{
|
||||
// right wall
|
||||
rot: [0, -Math.PI / 2, 0],
|
||||
cw: cellZ,
|
||||
ch: cellY,
|
||||
place: function (iu, iz) {
|
||||
return [HW + SKIN, -HH + (iu + 0.5) * cellY, -(iz + 0.5) * cellZ];
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
var UP = new THREEJS.Vector3(0, 1, 0);
|
||||
// Edges are solid tube geometry, not lines: a cylinder thickens with
|
||||
// perspective the way a real moulding does, which is the whole reason
|
||||
// the corridor reads as built rather than drawn.
|
||||
function tube(group, ax, ay, az, bx, by, bz) {
|
||||
var dx = bx - ax,
|
||||
dy = by - ay,
|
||||
dz = bz - az;
|
||||
var len = Math.sqrt(dx * dx + dy * dy + dz * dz);
|
||||
if (len < 1e-6) return;
|
||||
var mesh = new THREEJS.Mesh(
|
||||
new THREEJS.CylinderGeometry(LINE_RADIUS, LINE_RADIUS, len, 6, 1),
|
||||
edgeMat,
|
||||
);
|
||||
mesh.position.set((ax + bx) / 2, (ay + by) / 2, (az + bz) / 2);
|
||||
mesh.quaternion.setFromUnitVectors(UP, new THREEJS.Vector3(dx / len, dy / len, dz / len));
|
||||
group.add(mesh);
|
||||
}
|
||||
|
||||
function buildEdges(group) {
|
||||
var D = SEGMENT_DEPTH;
|
||||
var k, j, u, z;
|
||||
// The four corner runs (and any interior wall divisions) come from the
|
||||
// side walls; floor/ceiling only contribute their interior lines, so
|
||||
// corners are drawn once and never double-blend at 50% opacity.
|
||||
for (k = 1; k < GRID; k++) {
|
||||
u = -HW + k * cellX;
|
||||
tube(group, u, -HH, 0, u, -HH, -D);
|
||||
tube(group, u, HH, 0, u, HH, -D);
|
||||
}
|
||||
for (k = 0; k <= GRID; k++) {
|
||||
u = -HH + k * cellY;
|
||||
tube(group, -HW, u, 0, -HW, u, -D);
|
||||
tube(group, HW, u, 0, HW, u, -D);
|
||||
}
|
||||
// Ring lines at each depth boundary. The far boundary belongs to the
|
||||
// next segment, so stop one short and the tiling stays seamless.
|
||||
for (j = 0; j < GRID; j++) {
|
||||
z = -j * cellZ;
|
||||
tube(group, -HW, -HH, z, HW, -HH, z);
|
||||
tube(group, -HW, HH, z, HW, HH, z);
|
||||
tube(group, -HW, -HH, z, -HW, HH, z);
|
||||
tube(group, HW, -HH, z, HW, HH, z);
|
||||
}
|
||||
}
|
||||
|
||||
var segs = [];
|
||||
for (var i = 0; i < NUM_SEGMENTS; i++) {
|
||||
var group = new THREEJS.Group();
|
||||
var panels = [];
|
||||
for (var f = 0; f < FACES.length; f++) {
|
||||
var face = FACES[f];
|
||||
for (var iz = 0; iz < GRID; iz++) {
|
||||
for (var iu = 0; iu < GRID; iu++) {
|
||||
var mesh = new THREEJS.Mesh(PLANE, COLOR_MATS[0]);
|
||||
var p = face.place(iu, iz);
|
||||
mesh.position.set(p[0], p[1], p[2]);
|
||||
mesh.rotation.set(face.rot[0], face.rot[1], face.rot[2]);
|
||||
mesh.visible = false;
|
||||
group.add(mesh);
|
||||
panels.push({ mesh: mesh, cw: face.cw * INSET, ch: face.ch * INSET });
|
||||
}
|
||||
}
|
||||
}
|
||||
buildEdges(group);
|
||||
scene.add(group);
|
||||
segs.push({ group: group, panels: panels });
|
||||
}
|
||||
|
||||
// Contain-fit so a supplied screenshot keeps its aspect inside the slot
|
||||
// instead of being squashed to the cell (wall cells are portrait).
|
||||
function fit(p, ar) {
|
||||
if (!ar) {
|
||||
p.mesh.scale.set(p.cw, p.ch, 1);
|
||||
return;
|
||||
}
|
||||
if (ar > p.cw / p.ch) p.mesh.scale.set(p.cw, p.cw / ar, 1);
|
||||
else p.mesh.scale.set(p.ch * ar, p.ch, 1);
|
||||
}
|
||||
|
||||
// Everything about frame t is computed from t alone. `d` is the distance
|
||||
// flown in segments; `a` is the ABSOLUTE segment number occupying pool
|
||||
// slot i right now, which is what content hashes and the every-other
|
||||
// rhythm key off — so a segment carries the same panels every time it is
|
||||
// looked at, no matter how the timeline is scrubbed.
|
||||
function draw(t) {
|
||||
var d = SPEED * t;
|
||||
for (var i = 0; i < NUM_SEGMENTS; i++) {
|
||||
var seg = segs[i];
|
||||
var a = i + NUM_SEGMENTS * Math.ceil((d - i - BACK) / NUM_SEGMENTS);
|
||||
seg.group.position.z = -(a - d) * SEGMENT_DEPTH;
|
||||
|
||||
var lit = a % 2 === 0;
|
||||
var panels = seg.panels;
|
||||
for (var s = 0; s < panels.length; s++) {
|
||||
var p = panels[s];
|
||||
if (!lit || hash2(a, s * 4 + 1) >= FILL) {
|
||||
p.mesh.visible = false;
|
||||
continue;
|
||||
}
|
||||
p.mesh.visible = true;
|
||||
if (IMAGES.length > 0 && hash2(a, s * 4 + 2) < IMAGE_MIX) {
|
||||
var im = IMAGES[Math.floor(hash2(a, s * 4 + 4) * IMAGES.length) % IMAGES.length];
|
||||
p.mesh.material = im.mat;
|
||||
fit(p, im.aspect);
|
||||
} else {
|
||||
var ci = Math.floor(hash2(a, s * 4 + 3) * COLOR_MATS.length) % COLOR_MATS.length;
|
||||
p.mesh.material = COLOR_MATS[ci];
|
||||
p.mesh.scale.set(p.cw, p.ch, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
renderer.render(scene, camera);
|
||||
}
|
||||
|
||||
window.__timelines = window.__timelines || {};
|
||||
var tl = gsap.timeline({ paused: true });
|
||||
|
||||
// Repaint from a property SETTER, not from onUpdate: gsap's seek(t)
|
||||
// suppresses events by default, so an onUpdate callback silently never
|
||||
// fires on a scrub and the canvas freezes on frame 0. Tweened values are
|
||||
// always written during render, suppressed or not.
|
||||
var driver = { _t: 0 };
|
||||
Object.defineProperty(driver, "t", {
|
||||
get: function () {
|
||||
return this._t;
|
||||
},
|
||||
set: function (v) {
|
||||
this._t = v;
|
||||
draw(v);
|
||||
},
|
||||
});
|
||||
tl.to(driver, { t: DUR, duration: DUR, ease: "none" }, 0);
|
||||
window.__timelines["gallery-tunnel"] = tl;
|
||||
|
||||
draw(0);
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
|
||||
{/* hf:generated-footer */}
|
||||
|
||||
Tagged `webgl` `3d` `background` `showcase` `depth` `loop`.
|
||||
@@ -1013,3 +563,7 @@ Tagged `webgl` `3d` `background` `showcase` `depth` `loop`.
|
||||
- [Browse the complete Catalog](/catalog)
|
||||
- [Add assets and Catalog items in Studio](/studio/assets-and-blocks)
|
||||
- [Build a richer composition](/go-further)
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
</CatalogDetail>
|
||||
|
||||
@@ -0,0 +1,419 @@
|
||||
---
|
||||
title: "Glass Shard Title"
|
||||
description: "Glass shards fly in through fog and tile themselves into the headline."
|
||||
mode: "frame"
|
||||
---
|
||||
|
||||
import { CatalogDetail, CatalogSlot } from "/snippets/catalog-detail.jsx";
|
||||
import { InstallCommand } from "/snippets/install-command.jsx";
|
||||
|
||||
<CatalogDetail
|
||||
previewSrc="/public/catalog/blocks/glass-shard-title.json"
|
||||
compositionId="glass-shard-title"
|
||||
compositionSrc="compositions/glass-shard-title/glass-shard-title.html"
|
||||
title="Glass Shard Title"
|
||||
description="Glass shards fly in through fog and tile themselves into the headline."
|
||||
variables={[{"id":"headline","type":"string","label":"Headline","default":"Designed in glass","maxLength":40},{"id":"tileCount","type":"number","label":"Tiles","default":8,"min":1,"max":400,"step":1},{"id":"roundness","type":"number","label":"Roundness (0-1)","default":0.4,"min":0,"max":1,"step":0.01},{"id":"bevel","type":"number","label":"Bevel (0-1)","default":0.35,"min":0,"max":1,"step":0.01},{"id":"meshSmooth","type":"number","label":"Surface smoothness (0-1)","default":0.6,"min":0,"max":1,"step":0.01},{"id":"flyInTime","type":"number","label":"Fly-in time (s)","default":2.3,"min":0.2,"max":6,"step":0.05},{"id":"flyOutTime","type":"number","label":"Fly-out time (s)","default":2.4,"min":0.2,"max":6,"step":0.05},{"id":"stagger","type":"number","label":"Stagger (0-1)","default":0.27,"min":0,"max":1,"step":0.01},{"id":"flyInRotation","type":"number","label":"Fly-in rotation (0-60)","description":"Amount of inbound tumbling over the entire flight. 0 disables flight rotation; final orientation lands with each shard position.","default":5,"min":0,"max":60,"step":0.1},{"id":"flyOutRotation","type":"number","label":"Fly-out rotation (0-6)","default":6,"min":0,"max":6,"step":0.1},{"id":"chaos","type":"number","label":"Outline chaos (0-1)","default":0.25,"min":0,"max":1,"step":0.01},{"id":"padding","type":"number","label":"Edge padding (0-1)","default":1,"min":0,"max":1,"step":0.01},{"id":"sizeVariance","type":"number","label":"Size variance (0-1)","default":0.35,"min":0,"max":1,"step":0.01},{"id":"gap","type":"number","label":"Tile gap","default":0.03,"min":0,"max":0.3,"step":0.002},{"id":"easePow","type":"number","label":"Fly-in position ease (1-10)","description":"Shapes lateral and depth travel. Rotation uses the same staggered flight window with a gentle settle through arrival.","default":10,"min":1,"max":10,"step":0.1},{"id":"sideDist","type":"number","label":"Side fly distance","default":3,"min":0,"max":60,"step":0.5},{"id":"zDist","type":"number","label":"Z fly-from (negative)","default":-42,"min":-400,"max":0,"step":2},{"id":"zOut","type":"number","label":"Z fly-out to (+ = past camera)","default":14,"min":-400,"max":60,"step":2},{"id":"fog","type":"number","label":"Fog distance (0 = off)","default":36,"min":0,"max":400,"step":2}]}
|
||||
meta={{"duration":12.16,"width":1920,"height":1080,"category":"3D motion","badge":"Stable","codeLines":348}}
|
||||
attribution={{"path":"registry/blocks/glass-shard-title","tags":["3d-motion","title-card","glass","voronoi","shards","fog","hdr"]}}
|
||||
hasCode
|
||||
rawUrl="https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/glass-shard-title/glass-shard-title.html"
|
||||
>
|
||||
|
||||
<CatalogSlot slot="code">
|
||||
|
||||
```html glass-shard-title.html
|
||||
<!doctype html>
|
||||
<html
|
||||
lang="en"
|
||||
data-composition-variables='[{"id":"headline","type":"string","label":"Headline","default":"Designed in glass","maxLength":40},{"id":"tileCount","type":"number","label":"Tiles","default":8,"min":1,"max":400,"step":1},{"id":"roundness","type":"number","label":"Roundness (0-1)","default":0.4,"min":0,"max":1,"step":0.01},{"id":"bevel","type":"number","label":"Bevel (0-1)","default":0.35,"min":0,"max":1,"step":0.01},{"id":"meshSmooth","type":"number","label":"Surface smoothness (0-1)","default":0.6,"min":0,"max":1,"step":0.01},{"id":"flyInTime","type":"number","label":"Fly-in time (s)","default":2.3,"min":0.2,"max":6,"step":0.05},{"id":"flyOutTime","type":"number","label":"Fly-out time (s)","default":2.4,"min":0.2,"max":6,"step":0.05},{"id":"stagger","type":"number","label":"Stagger (0-1)","default":0.27,"min":0,"max":1,"step":0.01},{"id":"flyInRotation","type":"number","label":"Fly-in rotation (0-60)","description":"Amount of inbound tumbling over the entire flight. 0 disables flight rotation; final orientation lands with each shard position.","default":5,"min":0,"max":60,"step":0.1},{"id":"flyOutRotation","type":"number","label":"Fly-out rotation (0-6)","default":6,"min":0,"max":6,"step":0.1},{"id":"chaos","type":"number","label":"Outline chaos (0-1)","default":0.25,"min":0,"max":1,"step":0.01},{"id":"padding","type":"number","label":"Edge padding (0-1)","default":1,"min":0,"max":1,"step":0.01},{"id":"sizeVariance","type":"number","label":"Size variance (0-1)","default":0.35,"min":0,"max":1,"step":0.01},{"id":"gap","type":"number","label":"Tile gap","default":0.03,"min":0,"max":0.3,"step":0.002},{"id":"easePow","type":"number","label":"Fly-in position ease (1-10)","description":"Shapes lateral and depth travel. Rotation uses the same staggered flight window with a gentle settle through arrival.","default":10,"min":1,"max":10,"step":0.1},{"id":"sideDist","type":"number","label":"Side fly distance","default":3,"min":0,"max":60,"step":0.5},{"id":"zDist","type":"number","label":"Z fly-from (negative)","default":-42,"min":-400,"max":0,"step":2},{"id":"zOut","type":"number","label":"Z fly-out to (+ = past camera)","default":14,"min":-400,"max":60,"step":2},{"id":"fog","type":"number","label":"Fog distance (0 = off)","default":36,"min":0,"max":400,"step":2}]'
|
||||
>
|
||||
<head>
|
||||
<script>
|
||||
/* ?play standalone mode only: this file lives in compositions/ but its
|
||||
asset paths are project-root-relative (they resolve there when the
|
||||
runtime inlines it into the host). Never runs for Studio/render. */
|
||||
if (new URLSearchParams(location.search).has("play")) document.write('<base href="../">');
|
||||
</script>
|
||||
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=1920, height=1080" />
|
||||
<title>Glass Shard Title</title>
|
||||
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/d3-delaunay@6.0.4/dist/d3-delaunay.min.js"></script>
|
||||
<style>
|
||||
@font-face {
|
||||
font-family: "Geist";
|
||||
src: url("assets/fonts/Geist-Regular.ttf") format("truetype");
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "Geist";
|
||||
src: url("assets/fonts/Geist-SemiBold.ttf") format("truetype");
|
||||
font-weight: 600;
|
||||
font-style: normal;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "Geist";
|
||||
src: url("assets/fonts/Geist-Bold.ttf") format("truetype");
|
||||
font-weight: 700;
|
||||
font-style: normal;
|
||||
}
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
html,
|
||||
body {
|
||||
width: 1920px;
|
||||
height: 1080px;
|
||||
overflow: hidden;
|
||||
background: #0a0a0f;
|
||||
}
|
||||
body {
|
||||
font-family: "Geist", ui-sans-serif, system-ui, sans-serif;
|
||||
}
|
||||
#gst-root {
|
||||
position: relative;
|
||||
width: 1920px;
|
||||
height: 1080px;
|
||||
overflow: hidden;
|
||||
background: #0a0a0f; /* flat — no gradients, no glow */
|
||||
}
|
||||
.clip {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
}
|
||||
#gst-canvas {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
#gst-fontload {
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div
|
||||
data-hf-id="hf-51pv"
|
||||
id="gst-root"
|
||||
data-composition-id="glass-shard-title"
|
||||
data-start="0"
|
||||
data-duration="12.16"
|
||||
data-width="1920"
|
||||
data-height="1080"
|
||||
>
|
||||
<div
|
||||
data-hf-id="hf-lsju"
|
||||
id="gst-canvas-clip"
|
||||
class="clip"
|
||||
data-start="0"
|
||||
data-duration="12.16"
|
||||
data-track-index="1"
|
||||
data-media-start="0"
|
||||
></div>
|
||||
<div
|
||||
data-hf-id="hf-uo1i"
|
||||
id="gst-fontload"
|
||||
aria-hidden="true"
|
||||
data-start="0"
|
||||
data-duration="12.16"
|
||||
data-media-start="0"
|
||||
>
|
||||
<span data-hf-id="hf-kqtg" style="font-weight: 400">Aa</span
|
||||
><span data-hf-id="hf-7bxm" style="font-weight: 600">Aa</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
/* ================================================================
|
||||
glass-shard-title — iridescent glass shards fly in from the left
|
||||
and right edges, assemble into the headline, idle with a slight
|
||||
rotation, then fly back out and leave the frame empty.
|
||||
|
||||
The ONLY content is the headline. Flat dark background.
|
||||
|
||||
Seek-safety: the timeline drives two master progresses (tin, tout)
|
||||
plus the material resolve; every per-shard position/rotation is a
|
||||
pure function of those masters and st.time. No rAF, no
|
||||
integrators, no Math.random(). Shard layout is seeded and built
|
||||
after fonts load (cell selection samples the rasterised text).
|
||||
================================================================ */
|
||||
window.__timelines = window.__timelines || {};
|
||||
/* Studio's variables/SDK parser fails on any composition with a <canvas>
|
||||
in its MARKUP (verified by bisection) — so the canvas is created at
|
||||
runtime instead. Identical for preview/render; invisible to the parser.
|
||||
|
||||
Guarded: if this script is evaluated twice in one document a second
|
||||
<canvas id="gst-canvas"> would be appended, getElementById would keep
|
||||
returning the FIRST one, and the second three.js renderer would be
|
||||
built on a canvas that already owns a live WebGL context. */
|
||||
(function () {
|
||||
if (document.getElementById("gst-canvas")) return;
|
||||
var c = document.createElement("canvas");
|
||||
c.id = "gst-canvas";
|
||||
c.width = 1920;
|
||||
c.height = 1080;
|
||||
document.getElementById("gst-canvas-clip").appendChild(c);
|
||||
})();
|
||||
|
||||
window.gstMulberry32 = gstMulberry32;
|
||||
function gstMulberry32(seed) {
|
||||
return function () {
|
||||
var s = (seed = (seed + 0x6d2b79f5) | 0);
|
||||
var t = Math.imul(s ^ (s >>> 15), 1 | s);
|
||||
t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;
|
||||
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
|
||||
};
|
||||
}
|
||||
|
||||
(function () {
|
||||
var DUR = 9;
|
||||
|
||||
/* ---- Studio variables ---- */
|
||||
var vars = {
|
||||
headline: "Designed in glass",
|
||||
tileCount: 8,
|
||||
roundness: 0.4,
|
||||
bevel: 0.35,
|
||||
meshSmooth: 0.6,
|
||||
flyInTime: 2.3,
|
||||
flyOutTime: 2.4,
|
||||
stagger: 0.27,
|
||||
flyInRotation: 5,
|
||||
flyOutRotation: 6,
|
||||
chaos: 0.25,
|
||||
padding: 1,
|
||||
sizeVariance: 0.35,
|
||||
gap: 0.03,
|
||||
easePow: 10,
|
||||
sideDist: 3,
|
||||
zDist: -42,
|
||||
zOut: 14,
|
||||
fog: 36,
|
||||
};
|
||||
try {
|
||||
if (window.__hyperframes && window.__hyperframes.getVariables) {
|
||||
var v = window.__hyperframes.getVariables() || {};
|
||||
if (v.headline) vars.headline = String(v.headline);
|
||||
if (v.title) vars.headline = String(v.title); /* legacy id */
|
||||
if (v.tileCount !== undefined && isFinite(Number(v.tileCount)))
|
||||
vars.tileCount = Number(v.tileCount);
|
||||
if (v.roundness !== undefined && isFinite(Number(v.roundness)))
|
||||
vars.roundness = Number(v.roundness);
|
||||
if (v.bevel !== undefined && isFinite(Number(v.bevel))) vars.bevel = Number(v.bevel);
|
||||
if (v.meshSmooth !== undefined && isFinite(Number(v.meshSmooth)))
|
||||
vars.meshSmooth = Number(v.meshSmooth);
|
||||
if (v.flyInTime !== undefined && isFinite(Number(v.flyInTime)))
|
||||
vars.flyInTime = Number(v.flyInTime);
|
||||
if (v.flyOutTime !== undefined && isFinite(Number(v.flyOutTime)))
|
||||
vars.flyOutTime = Number(v.flyOutTime);
|
||||
if (v.stagger !== undefined && isFinite(Number(v.stagger)))
|
||||
vars.stagger = Number(v.stagger);
|
||||
/* Legacy callers may still provide spin. New controls override it
|
||||
independently; the old multiplier/default keeps its meaning. */
|
||||
if (v.spin !== undefined && isFinite(Number(v.spin))) {
|
||||
vars.flyInRotation = vars.flyOutRotation = Math.max(0, Math.min(6, Number(v.spin)));
|
||||
}
|
||||
if (v.flyInRotation !== undefined && isFinite(Number(v.flyInRotation)))
|
||||
vars.flyInRotation = Number(v.flyInRotation);
|
||||
if (v.flyOutRotation !== undefined && isFinite(Number(v.flyOutRotation)))
|
||||
vars.flyOutRotation = Number(v.flyOutRotation);
|
||||
if (v.zOut !== undefined && isFinite(Number(v.zOut))) vars.zOut = Number(v.zOut);
|
||||
if (v.chaos !== undefined && isFinite(Number(v.chaos))) vars.chaos = Number(v.chaos);
|
||||
if (v.padding !== undefined && isFinite(Number(v.padding)))
|
||||
vars.padding = Number(v.padding);
|
||||
if (v.sizeVariance !== undefined && isFinite(Number(v.sizeVariance)))
|
||||
vars.sizeVariance = Number(v.sizeVariance);
|
||||
if (v.gap !== undefined && isFinite(Number(v.gap))) vars.gap = Number(v.gap);
|
||||
if (v.easePow !== undefined && isFinite(Number(v.easePow)))
|
||||
vars.easePow = Number(v.easePow);
|
||||
if (v.sideDist !== undefined && isFinite(Number(v.sideDist)))
|
||||
vars.sideDist = Number(v.sideDist);
|
||||
if (v.zDist !== undefined && isFinite(Number(v.zDist))) vars.zDist = Number(v.zDist);
|
||||
if (v.fog !== undefined && isFinite(Number(v.fog))) vars.fog = Number(v.fog);
|
||||
}
|
||||
} catch (e) {
|
||||
/* keep defaults */
|
||||
}
|
||||
vars.tileCount = Math.max(1, Math.min(400, Math.round(vars.tileCount)));
|
||||
vars.roundness = Math.max(0, Math.min(1, vars.roundness));
|
||||
vars.bevel = Math.max(0, Math.min(1, vars.bevel));
|
||||
vars.meshSmooth = Math.max(0, Math.min(1, vars.meshSmooth));
|
||||
vars.flyInTime = Math.max(0.2, Math.min(6, vars.flyInTime));
|
||||
vars.flyOutTime = Math.max(0.2, Math.min(6, vars.flyOutTime));
|
||||
vars.stagger = Math.max(0, Math.min(1, vars.stagger));
|
||||
vars.flyInRotation = Math.max(0, Math.min(60, vars.flyInRotation));
|
||||
vars.flyOutRotation = Math.max(0, Math.min(6, vars.flyOutRotation));
|
||||
vars.zOut = Math.max(-400, Math.min(60, vars.zOut));
|
||||
vars.chaos = Math.max(0, Math.min(1, vars.chaos));
|
||||
vars.padding = Math.max(0, Math.min(1, vars.padding));
|
||||
vars.sizeVariance = Math.max(0, Math.min(1, vars.sizeVariance));
|
||||
vars.gap = Math.max(0, Math.min(0.3, vars.gap));
|
||||
vars.easePow = Math.max(1, Math.min(10, vars.easePow));
|
||||
vars.sideDist = Math.max(0, Math.min(60, vars.sideDist));
|
||||
/* zDist is a coordinate: accept "-200" style input, clamp behind pane. */
|
||||
vars.zDist = Math.min(0, Math.max(-400, vars.zDist > 0 ? -vars.zDist : vars.zDist));
|
||||
vars.fog = Math.max(0, Math.min(400, vars.fog));
|
||||
|
||||
/* ---- Phase windows (speed compresses fly phases, hold absorbs) ---- */
|
||||
var IN_START = 0.12;
|
||||
var OUT_END = DUR - 0.35;
|
||||
/* Explicit flight durations; if in + out + a minimum hold exceed the
|
||||
composition, both scale down proportionally. */
|
||||
var MIN_HOLD = 0.5;
|
||||
var avail = OUT_END - IN_START - MIN_HOLD;
|
||||
var tIn = vars.flyInTime,
|
||||
tOut = vars.flyOutTime;
|
||||
if (tIn + tOut > avail) {
|
||||
var k = avail / (tIn + tOut);
|
||||
tIn *= k;
|
||||
tOut *= k;
|
||||
}
|
||||
var IN_WINDOW = tIn;
|
||||
var OUT_WINDOW = tOut;
|
||||
var OUT_START = OUT_END - OUT_WINDOW;
|
||||
|
||||
/* Scripted state read by renderScene. */
|
||||
var st = {
|
||||
time: 0,
|
||||
tin: 0 /* master fly-in progress 0..1 */,
|
||||
tout: 0 /* master fly-out progress 0..1 */,
|
||||
thickness: 0.5,
|
||||
roughness: 0.07,
|
||||
ior: 1.52,
|
||||
};
|
||||
|
||||
window.__GST = { DUR: DUR, st: st, vars: vars };
|
||||
|
||||
/* ---- Timeline: paused, registered, hf-seek wired ---- */
|
||||
var tl = gsap.timeline({ paused: true });
|
||||
tl.eventCallback("onUpdate", function () {
|
||||
if (window.__gstRender) window.__gstRender();
|
||||
});
|
||||
/* Studio's player seeks with GSAP events suppressed and dispatches an
|
||||
hf-seek CustomEvent instead — re-sync and repaint so the stage follows. */
|
||||
/* Exactly one listener and one live timeline per document. A second
|
||||
evaluation of this script would otherwise stack another hf-seek
|
||||
listener and leave the previous paused timeline registered, so every
|
||||
seek would drive N timelines and repaint N times. Declared as a
|
||||
named function (not an arrow) so the reference read back off window
|
||||
is identical and removeEventListener actually matches. */
|
||||
function gstOnSeek(event) {
|
||||
var d = event && event.detail;
|
||||
if (d && typeof d.time === "number")
|
||||
tl.totalTime(Math.min(d.time, tl.totalDuration()), true);
|
||||
var fn = tl.eventCallback("onUpdate");
|
||||
if (fn) fn();
|
||||
}
|
||||
if (window.__gstSeekHandler) window.removeEventListener("hf-seek", window.__gstSeekHandler);
|
||||
if (window.__gstTimeline && window.__gstTimeline !== tl) {
|
||||
try {
|
||||
window.__gstTimeline.kill();
|
||||
} catch (e) {
|
||||
/* best effort */
|
||||
}
|
||||
}
|
||||
window.__gstSeekHandler = gstOnSeek;
|
||||
window.__gstTimeline = tl;
|
||||
window.addEventListener("hf-seek", gstOnSeek);
|
||||
/* Release the listener and the timeline with the document. Skipped for
|
||||
a bfcache'd page, which is coming back with this stage still live. */
|
||||
window.addEventListener("pagehide", function (event) {
|
||||
if (event && event.persisted === true) return;
|
||||
window.removeEventListener("hf-seek", gstOnSeek);
|
||||
window.__gstSeekHandler = null;
|
||||
window.__gstTimeline = null;
|
||||
try {
|
||||
tl.kill();
|
||||
} catch (e) {
|
||||
/* best effort */
|
||||
}
|
||||
});
|
||||
|
||||
tl.to(st, { time: DUR, duration: DUR, ease: "none" }, 0);
|
||||
tl.to(st, { tin: 1, duration: IN_WINDOW, ease: "none" }, IN_START);
|
||||
tl.to(st, { tout: 1, duration: OUT_WINDOW, ease: "none" }, OUT_START);
|
||||
|
||||
/* Glass stays clear throughout. The old frosted->clear resolve made
|
||||
still-flying shards transmit a BLURRED copy of the already-seated
|
||||
text — the ghost headline — so it is gone. */
|
||||
|
||||
window.__timelines["glass-shard-title"] = tl;
|
||||
})();
|
||||
</script>
|
||||
|
||||
<script src="assets/glass-main.js"></script>
|
||||
<script>
|
||||
/* Standalone playback: open this file with ?play (add &loop to repeat).
|
||||
Inert without the param — Studio and the renderer never add it, so
|
||||
editing and deterministic renders are unaffected. */
|
||||
(function () {
|
||||
var q = new URLSearchParams(location.search);
|
||||
if (!q.has("play")) return;
|
||||
var tick = setInterval(function () {
|
||||
var map = window.__timelines || {};
|
||||
var ids = Object.keys(map);
|
||||
if (!ids.length) return;
|
||||
clearInterval(tick);
|
||||
for (var i = 0; i < ids.length; i++) {
|
||||
var tl = map[ids[i]];
|
||||
if (q.has("loop")) tl.repeat(-1);
|
||||
tl.play(0);
|
||||
}
|
||||
}, 100);
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
<CatalogSlot slot="install">
|
||||
|
||||
<InstallCommand command="npx hyperframes add glass-shard-title" item="glass-shard-title" />
|
||||
|
||||
That writes `compositions/glass-shard-title/glass-shard-title.html`, plus 11 supporting files under `compositions/glass-shard-title/assets/`, `compositions/glass-shard-title/assets/fonts/`, and `compositions/glass-shard-title/`.
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
<CatalogSlot slot="docs">
|
||||
|
||||
## Add it to your video
|
||||
|
||||
It runs for 12.16 seconds at 1920×1080. Paste this into your composition:
|
||||
|
||||
```html index.html
|
||||
<div
|
||||
data-composition-id="glass-shard-title"
|
||||
data-composition-src="compositions/glass-shard-title/glass-shard-title.html"
|
||||
data-start="0"
|
||||
data-duration="12.16"
|
||||
data-track-index="1"
|
||||
data-width="1920"
|
||||
data-height="1080"
|
||||
></div>
|
||||
```
|
||||
|
||||
Move it in time with `data-start`. Put it on a different timeline row with
|
||||
`data-track-index`. See [data attributes](/concepts/data-attributes) for the rest.
|
||||
|
||||
{/* hf:generated-footer */}
|
||||
|
||||
Tagged `3d-motion` `title-card` `glass` `voronoi` `shards` `fog` `hdr`.
|
||||
|
||||
## Related topics
|
||||
|
||||
- [Browse the complete Catalog](/catalog)
|
||||
- [Add assets and Catalog items in Studio](/studio/assets-and-blocks)
|
||||
- [Build a richer composition](/go-further)
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
</CatalogDetail>
|
||||
@@ -1,28 +1,28 @@
|
||||
---
|
||||
title: "Glitch"
|
||||
description: "Shader transition with digital glitch artifacts"
|
||||
mode: "frame"
|
||||
---
|
||||
|
||||
import { CatalogDetail, CatalogSlot } from "/snippets/catalog-detail.jsx";
|
||||
import { InstallCommand } from "/snippets/install-command.jsx";
|
||||
|
||||
## Install
|
||||
<CatalogDetail
|
||||
previewSrc="/public/catalog/blocks/glitch.json"
|
||||
compositionId="glitch"
|
||||
compositionSrc="compositions/glitch.html"
|
||||
title="Glitch"
|
||||
description="Shader transition with digital glitch artifacts"
|
||||
variables={[]}
|
||||
meta={{"duration":4,"width":1920,"height":1080,"category":"Shader Transitions","badge":"Stable","codeLines":366}}
|
||||
attribution={{"path":"registry/blocks/glitch","tags":["transition","shader"]}}
|
||||
hasCode
|
||||
rawUrl="https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/glitch/glitch.html"
|
||||
>
|
||||
|
||||
<InstallCommand command="npx hyperframes add glitch" item="glitch" />
|
||||
<CatalogSlot slot="code">
|
||||
|
||||
That writes one file: `compositions/glitch.html`.
|
||||
|
||||
<iframe
|
||||
className="w-full aspect-video rounded-xl border-0 bg-zinc-100 dark:bg-zinc-800"
|
||||
title="glitch preview"
|
||||
loading="lazy"
|
||||
srcDoc={`<!doctype html><html><head><meta charset="utf-8"><style>html,body{margin:0;height:100%;overflow:hidden;background:transparent}hyperframes-player{display:block;width:100%;height:100%}</style><script src="https://cdn.jsdelivr.net/npm/@hyperframes/player@latest/dist/hyperframes-player.global.js"><\/script></head><body><script>fetch("/public/catalog/blocks/glitch.json").then(function(r){return r.json()}).then(function(d){var p=document.createElement("hyperframes-player");p.setAttribute("srcdoc",d.html);p.setAttribute("controls","");p.setAttribute("autoplay","");p.setAttribute("loop","");p.setAttribute("muted","");p.setAttribute("poster","https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/glitch.png");document.body.appendChild(p)});<\/script></body></html>`}
|
||||
/>
|
||||
|
||||
## Source
|
||||
|
||||
<Accordion title="glitch.html">
|
||||
|
||||
```html
|
||||
```html glitch.html
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
@@ -391,16 +391,37 @@ That writes one file: `compositions/glitch.html`.
|
||||
</html>
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
</CatalogSlot>
|
||||
|
||||
## Usage
|
||||
<CatalogSlot slot="install">
|
||||
|
||||
After installing, add the block to your host composition:
|
||||
<InstallCommand command="npx hyperframes add glitch" item="glitch" />
|
||||
|
||||
```html
|
||||
<div data-composition-id="glitch" data-composition-src="compositions/glitch.html" data-start="0" data-duration="4" data-track-index="1" data-width="1920" data-height="1080"></div>
|
||||
That writes one file: `compositions/glitch.html`.
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
<CatalogSlot slot="docs">
|
||||
|
||||
## Add it to your video
|
||||
|
||||
It runs for 4 seconds at 1920×1080. Paste this into your composition:
|
||||
|
||||
```html index.html
|
||||
<div
|
||||
data-composition-id="glitch"
|
||||
data-composition-src="compositions/glitch.html"
|
||||
data-start="0"
|
||||
data-duration="4"
|
||||
data-track-index="1"
|
||||
data-width="1920"
|
||||
data-height="1080"
|
||||
></div>
|
||||
```
|
||||
|
||||
Move it in time with `data-start`. Put it on a different timeline row with
|
||||
`data-track-index`. See [data attributes](/concepts/data-attributes) for the rest.
|
||||
|
||||
{/* hf:generated-footer */}
|
||||
|
||||
Tagged `transition` `shader`.
|
||||
@@ -410,3 +431,7 @@ Tagged `transition` `shader`.
|
||||
- [Browse the complete Catalog](/catalog)
|
||||
- [Add assets and Catalog items in Studio](/studio/assets-and-blocks)
|
||||
- [Build a richer composition](/go-further)
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
</CatalogDetail>
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
---
|
||||
title: "Gravitational Lens"
|
||||
description: "Shader transition with gravitational lensing distortion"
|
||||
mode: "frame"
|
||||
---
|
||||
|
||||
import { CatalogDetail, CatalogSlot } from "/snippets/catalog-detail.jsx";
|
||||
import { InstallCommand } from "/snippets/install-command.jsx";
|
||||
|
||||
## Install
|
||||
<CatalogDetail
|
||||
previewSrc="/public/catalog/blocks/gravitational-lens.json"
|
||||
compositionId="gravitational-lens"
|
||||
compositionSrc="compositions/gravitational-lens.html"
|
||||
title="Gravitational Lens"
|
||||
description="Shader transition with gravitational lensing distortion"
|
||||
variables={[]}
|
||||
meta={{"duration":4,"width":1920,"height":1080,"category":"Shader Transitions","badge":"Stable","codeLines":365}}
|
||||
attribution={{"path":"registry/blocks/gravitational-lens","tags":["transition","shader"]}}
|
||||
hasCode
|
||||
rawUrl="https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/gravitational-lens/gravitational-lens.html"
|
||||
>
|
||||
|
||||
<InstallCommand command="npx hyperframes add gravitational-lens" item="gravitational-lens" />
|
||||
<CatalogSlot slot="code">
|
||||
|
||||
That writes one file: `compositions/gravitational-lens.html`.
|
||||
|
||||
<iframe
|
||||
className="w-full aspect-video rounded-xl border-0 bg-zinc-100 dark:bg-zinc-800"
|
||||
title="gravitational-lens preview"
|
||||
loading="lazy"
|
||||
srcDoc={`<!doctype html><html><head><meta charset="utf-8"><style>html,body{margin:0;height:100%;overflow:hidden;background:transparent}hyperframes-player{display:block;width:100%;height:100%}</style><script src="https://cdn.jsdelivr.net/npm/@hyperframes/player@latest/dist/hyperframes-player.global.js"><\/script></head><body><script>fetch("/public/catalog/blocks/gravitational-lens.json").then(function(r){return r.json()}).then(function(d){var p=document.createElement("hyperframes-player");p.setAttribute("srcdoc",d.html);p.setAttribute("controls","");p.setAttribute("autoplay","");p.setAttribute("loop","");p.setAttribute("muted","");p.setAttribute("poster","https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/gravitational-lens.png");document.body.appendChild(p)});<\/script></body></html>`}
|
||||
/>
|
||||
|
||||
## Source
|
||||
|
||||
<Accordion title="gravitational-lens.html">
|
||||
|
||||
```html
|
||||
```html gravitational-lens.html
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
@@ -390,16 +390,37 @@ That writes one file: `compositions/gravitational-lens.html`.
|
||||
</html>
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
</CatalogSlot>
|
||||
|
||||
## Usage
|
||||
<CatalogSlot slot="install">
|
||||
|
||||
After installing, add the block to your host composition:
|
||||
<InstallCommand command="npx hyperframes add gravitational-lens" item="gravitational-lens" />
|
||||
|
||||
```html
|
||||
<div data-composition-id="gravitational-lens" data-composition-src="compositions/gravitational-lens.html" data-start="0" data-duration="4" data-track-index="1" data-width="1920" data-height="1080"></div>
|
||||
That writes one file: `compositions/gravitational-lens.html`.
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
<CatalogSlot slot="docs">
|
||||
|
||||
## Add it to your video
|
||||
|
||||
It runs for 4 seconds at 1920×1080. Paste this into your composition:
|
||||
|
||||
```html index.html
|
||||
<div
|
||||
data-composition-id="gravitational-lens"
|
||||
data-composition-src="compositions/gravitational-lens.html"
|
||||
data-start="0"
|
||||
data-duration="4"
|
||||
data-track-index="1"
|
||||
data-width="1920"
|
||||
data-height="1080"
|
||||
></div>
|
||||
```
|
||||
|
||||
Move it in time with `data-start`. Put it on a different timeline row with
|
||||
`data-track-index`. See [data attributes](/concepts/data-attributes) for the rest.
|
||||
|
||||
{/* hf:generated-footer */}
|
||||
|
||||
Tagged `transition` `shader`.
|
||||
@@ -409,3 +430,7 @@ Tagged `transition` `shader`.
|
||||
- [Browse the complete Catalog](/catalog)
|
||||
- [Add assets and Catalog items in Studio](/studio/assets-and-blocks)
|
||||
- [Build a richer composition](/go-further)
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
</CatalogDetail>
|
||||
|
||||
@@ -1,24 +1,27 @@
|
||||
---
|
||||
title: "Halftone Field"
|
||||
description: "A full-bleed halftone light field: a grid of dots whose size and colour track a slow flowing simplex-noise field, so broad bands of light sweep across a mostly-black frame like a giant LED wall showing a plasma. Built to run under type."
|
||||
mode: "frame"
|
||||
---
|
||||
|
||||
import { CatalogDetail, CatalogSlot } from "/snippets/catalog-detail.jsx";
|
||||
import { InstallCommand } from "/snippets/install-command.jsx";
|
||||
import { VariablesExplorer } from "/snippets/variables-explorer.jsx";
|
||||
|
||||
## Install
|
||||
|
||||
<InstallCommand command="npx hyperframes add halftone-field" item="halftone-field" />
|
||||
|
||||
That writes one file: `compositions/halftone-field.html`.
|
||||
|
||||
<VariablesExplorer
|
||||
<CatalogDetail
|
||||
previewSrc="/public/catalog/blocks/halftone-field.json"
|
||||
compositionId="halftone-field"
|
||||
compositionSrc="compositions/halftone-field.html"
|
||||
title="Halftone Field"
|
||||
description="A full-bleed halftone light field: a grid of dots whose size and colour track a slow flowing simplex-noise field, so broad bands of light sweep across a mostly-black frame like a giant LED wall showing a plasma. Built to run under type."
|
||||
variables={[{"id":"frequency","type":"number","label":"Field frequency","default":2,"min":1,"max":10,"step":0.1},{"id":"speed","type":"number","label":"Flow speed","default":5,"min":0,"max":10,"step":0.1},{"id":"cellSize","type":"number","label":"Cell size","default":54,"min":1,"max":100,"step":1},{"id":"gamma","type":"number","label":"Gamma (midtone crush)","default":12,"min":1,"max":20,"step":0.1},{"id":"paletteBias","type":"number","label":"Palette bias","default":2,"min":0,"max":20,"step":0.5},{"id":"palette1","type":"color","label":"Palette stop 1 (lows)","default":"#00AAFF"},{"id":"palette2","type":"color","label":"Palette stop 2","default":"#C2FF67"},{"id":"palette3","type":"color","label":"Palette stop 3","default":"#6600FF"},{"id":"palette4","type":"color","label":"Palette stop 4 (crests)","default":"#000000"},{"id":"background","type":"color","label":"Background","default":"#000000"},{"id":"quantize","type":"boolean","label":"Quantise to 30fps (stepped)","default":false}]}
|
||||
meta={{"duration":10,"width":1920,"height":1080,"category":"Texture","badge":"Experimental","codeLines":378}}
|
||||
attribution={{"path":"registry/blocks/halftone-field","tags":["webgl","shader","background","texture","halftone"]}}
|
||||
hasCode
|
||||
rawUrl="https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/halftone-field/halftone-field.html"
|
||||
>
|
||||
|
||||
<CatalogSlot slot="code">
|
||||
|
||||
```html halftone-field.html
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
@@ -400,7 +403,17 @@ void main() {
|
||||
</html>
|
||||
```
|
||||
|
||||
</VariablesExplorer>
|
||||
</CatalogSlot>
|
||||
|
||||
<CatalogSlot slot="install">
|
||||
|
||||
<InstallCommand command="npx hyperframes add halftone-field" item="halftone-field" />
|
||||
|
||||
That writes one file: `compositions/halftone-field.html`.
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
<CatalogSlot slot="docs">
|
||||
|
||||
## Add it to your video
|
||||
|
||||
@@ -467,393 +480,6 @@ defaults, so this behaves exactly like the preview above until you change one:
|
||||
></div>
|
||||
```
|
||||
|
||||
## Source
|
||||
|
||||
<Accordion title="halftone-field.html">
|
||||
|
||||
```html
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=1920, height=1080" />
|
||||
<title>Halftone Field</title>
|
||||
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
|
||||
<style>
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
body {
|
||||
background: #000;
|
||||
overflow: hidden;
|
||||
}
|
||||
#hlf-root {
|
||||
position: relative;
|
||||
width: 1920px;
|
||||
height: 1080px;
|
||||
overflow: hidden;
|
||||
}
|
||||
#hlf-backdrop {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: #000000;
|
||||
}
|
||||
#hlf-canvas {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 1920px;
|
||||
height: 1080px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div
|
||||
id="hlf-root"
|
||||
data-composition-id="halftone-field"
|
||||
data-root="true"
|
||||
data-width="1920"
|
||||
data-height="1080"
|
||||
data-start="0"
|
||||
data-duration="10"
|
||||
data-composition-variables='[
|
||||
{"id":"frequency","type":"number","label":"Field frequency","default":2,"min":1,"max":10,"step":0.1},
|
||||
{"id":"speed","type":"number","label":"Flow speed","default":5,"min":0,"max":10,"step":0.1},
|
||||
{"id":"cellSize","type":"number","label":"Cell size","default":54,"min":1,"max":100,"step":1},
|
||||
{"id":"gamma","type":"number","label":"Gamma (midtone crush)","default":12,"min":1,"max":20,"step":0.1},
|
||||
{"id":"paletteBias","type":"number","label":"Palette bias","default":2,"min":0,"max":20,"step":0.5},
|
||||
{"id":"palette1","type":"color","label":"Palette stop 1 (lows)","default":"#00AAFF"},
|
||||
{"id":"palette2","type":"color","label":"Palette stop 2","default":"#C2FF67"},
|
||||
{"id":"palette3","type":"color","label":"Palette stop 3","default":"#6600FF"},
|
||||
{"id":"palette4","type":"color","label":"Palette stop 4 (crests)","default":"#000000"},
|
||||
{"id":"background","type":"color","label":"Background","default":"#000000"},
|
||||
{"id":"quantize","type":"boolean","label":"Quantise to 30fps (stepped)","default":false}
|
||||
]'
|
||||
>
|
||||
<div id="hlf-backdrop"></div>
|
||||
<canvas id="hlf-canvas" width="1920" height="1080"></canvas>
|
||||
|
||||
<!-- Driver clip: gives HyperFrames a timed element to own on track 0. -->
|
||||
<div
|
||||
id="hlf-drv"
|
||||
class="clip"
|
||||
data-start="0"
|
||||
data-duration="10"
|
||||
data-track-index="0"
|
||||
style="position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none"
|
||||
></div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function () {
|
||||
var DUR = 10;
|
||||
var W = 1920;
|
||||
var H = 1080;
|
||||
|
||||
var V = (window.__hyperframes && window.__hyperframes.getVariables()) || {};
|
||||
var CS = getComputedStyle(document.getElementById("hlf-root"));
|
||||
|
||||
// The runtime defines every declared variable as `--<id>` on the root,
|
||||
// so a host stylesheet can override one there too. Read the custom
|
||||
// property first, fall back to the declared value when it is unset.
|
||||
function raw(id) {
|
||||
var css = CS.getPropertyValue("--" + id.toLowerCase()).trim();
|
||||
return css !== "" ? css : V[id];
|
||||
}
|
||||
function num(id, fallback) {
|
||||
var n = parseFloat(raw(id));
|
||||
return isFinite(n) ? n : fallback;
|
||||
}
|
||||
function bool(id) {
|
||||
var v = raw(id);
|
||||
return v === true || /^(true|1|on|yes)$/i.test(String(v));
|
||||
}
|
||||
// #RGB / #RRGGBB -> [r, g, b] in 0..1. Anything else falls back.
|
||||
function rgb(id, fallback) {
|
||||
var s = String(raw(id) || "").trim();
|
||||
var m = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(s);
|
||||
if (!m) s = fallback;
|
||||
else s = "#" + m[1];
|
||||
var hex = s.slice(1);
|
||||
if (hex.length === 3) {
|
||||
hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];
|
||||
}
|
||||
var n = parseInt(hex, 16);
|
||||
return [((n >> 16) & 255) / 255, ((n >> 8) & 255) / 255, (n & 255) / 255];
|
||||
}
|
||||
// The reference exposes 1-10 / 1-100 / 1-20 dials and maps them onto
|
||||
// the ranges the shader actually wants.
|
||||
function mapLinear(x, a1, a2, b1, b2) {
|
||||
return b1 + ((x - a1) * (b2 - b1)) / (a2 - a1);
|
||||
}
|
||||
|
||||
var FREQ = mapLinear(num("frequency", 2), 1, 10, 0.3, 6);
|
||||
var SPEED = num("speed", 5) * 0.05;
|
||||
var CELL = mapLinear(num("cellSize", 54), 1, 100, 6, 60);
|
||||
var GAMMA = mapLinear(num("gamma", 12), 1, 20, 0.5, 8);
|
||||
var BIAS = num("paletteBias", 2) * 0.05;
|
||||
var PAL = [
|
||||
rgb("palette1", "#00AAFF"),
|
||||
rgb("palette2", "#C2FF67"),
|
||||
rgb("palette3", "#6600FF"),
|
||||
rgb("palette4", "#000000"),
|
||||
];
|
||||
var BG = rgb("background", "#000000");
|
||||
var QUANTIZE = bool("quantize");
|
||||
|
||||
document.getElementById("hlf-backdrop").style.background =
|
||||
"rgb(" +
|
||||
BG.map(function (c) {
|
||||
return Math.round(c * 255);
|
||||
}).join(",") +
|
||||
")";
|
||||
|
||||
var canvas = document.getElementById("hlf-canvas");
|
||||
var gl =
|
||||
canvas.getContext("webgl", {
|
||||
alpha: false,
|
||||
antialias: false,
|
||||
depth: false,
|
||||
stencil: false,
|
||||
preserveDrawingBuffer: true,
|
||||
powerPreference: "high-performance",
|
||||
}) ||
|
||||
canvas.getContext("experimental-webgl", {
|
||||
alpha: false,
|
||||
preserveDrawingBuffer: true,
|
||||
});
|
||||
|
||||
var VERT = "attribute vec2 aPos;\nvoid main() { gl_Position = vec4(aPos, 0.0, 1.0); }";
|
||||
|
||||
// Single pass. The reference renders the colour field into a target and
|
||||
// samples it at each cell centre, but the field is analytic, so
|
||||
// evaluating it at the cell centre here is the same value without the
|
||||
// round trip through a texture.
|
||||
var FRAG = `
|
||||
precision highp float;
|
||||
uniform vec2 uRes;
|
||||
uniform float uTime;
|
||||
uniform float uFreq;
|
||||
uniform float uSpeed;
|
||||
uniform float uCell;
|
||||
uniform float uGamma;
|
||||
uniform float uBias;
|
||||
uniform vec3 uPal0;
|
||||
uniform vec3 uPal1;
|
||||
uniform vec3 uPal2;
|
||||
uniform vec3 uPal3;
|
||||
uniform vec3 uBg;
|
||||
|
||||
// 3D simplex noise — Ashima Arts / Stefan Gustavson (MIT).
|
||||
vec3 mod289(vec3 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; }
|
||||
vec4 mod289(vec4 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; }
|
||||
vec4 permute(vec4 x) { return mod289(((x * 34.0) + 1.0) * x); }
|
||||
vec4 taylorInvSqrt(vec4 r) { return 1.79284291400159 - 0.85373472095314 * r; }
|
||||
|
||||
float snoise(vec3 v) {
|
||||
const vec2 C = vec2(1.0 / 6.0, 1.0 / 3.0);
|
||||
const vec4 D = vec4(0.0, 0.5, 1.0, 2.0);
|
||||
|
||||
vec3 i = floor(v + dot(v, C.yyy));
|
||||
vec3 x0 = v - i + dot(i, C.xxx);
|
||||
|
||||
vec3 g = step(x0.yzx, x0.xyz);
|
||||
vec3 l = 1.0 - g;
|
||||
vec3 i1 = min(g.xyz, l.zxy);
|
||||
vec3 i2 = max(g.xyz, l.zxy);
|
||||
|
||||
vec3 x1 = x0 - i1 + C.xxx;
|
||||
vec3 x2 = x0 - i2 + C.yyy;
|
||||
vec3 x3 = x0 - D.yyy;
|
||||
|
||||
i = mod289(i);
|
||||
vec4 p = permute(permute(permute(
|
||||
i.z + vec4(0.0, i1.z, i2.z, 1.0)) +
|
||||
i.y + vec4(0.0, i1.y, i2.y, 1.0)) +
|
||||
i.x + vec4(0.0, i1.x, i2.x, 1.0));
|
||||
|
||||
float n_ = 0.142857142857;
|
||||
vec3 ns = n_ * D.wyz - D.xzx;
|
||||
|
||||
vec4 j = p - 49.0 * floor(p * ns.z * ns.z);
|
||||
|
||||
vec4 x_ = floor(j * ns.z);
|
||||
vec4 y_ = floor(j - 7.0 * x_);
|
||||
|
||||
vec4 x = x_ * ns.x + ns.yyyy;
|
||||
vec4 y = y_ * ns.x + ns.yyyy;
|
||||
vec4 h = 1.0 - abs(x) - abs(y);
|
||||
|
||||
vec4 b0 = vec4(x.xy, y.xy);
|
||||
vec4 b1 = vec4(x.zw, y.zw);
|
||||
|
||||
vec4 s0 = floor(b0) * 2.0 + 1.0;
|
||||
vec4 s1 = floor(b1) * 2.0 + 1.0;
|
||||
vec4 sh = -step(h, vec4(0.0));
|
||||
|
||||
vec4 a0 = b0.xzyw + s0.xzyw * sh.xxyy;
|
||||
vec4 a1 = b1.xzyw + s1.xzyw * sh.zzww;
|
||||
|
||||
vec3 p0 = vec3(a0.xy, h.x);
|
||||
vec3 p1 = vec3(a0.zw, h.y);
|
||||
vec3 p2 = vec3(a1.xy, h.z);
|
||||
vec3 p3 = vec3(a1.zw, h.w);
|
||||
|
||||
vec4 norm = taylorInvSqrt(vec4(dot(p0, p0), dot(p1, p1), dot(p2, p2), dot(p3, p3)));
|
||||
p0 *= norm.x;
|
||||
p1 *= norm.y;
|
||||
p2 *= norm.z;
|
||||
p3 *= norm.w;
|
||||
|
||||
vec4 m = max(0.6 - vec4(dot(x0, x0), dot(x1, x1), dot(x2, x2), dot(x3, x3)), 0.0);
|
||||
m = m * m;
|
||||
return 42.0 * dot(m * m, vec4(dot(p0, x0), dot(p1, x1), dot(p2, x2), dot(p3, x3)));
|
||||
}
|
||||
|
||||
// Full-saturation, full-value HSV: only the hue ramp survives.
|
||||
vec3 hue2rgb(float h) {
|
||||
return clamp(abs(mod(h * 6.0 + vec3(0.0, 4.0, 2.0), 6.0) - 3.0) - 1.0, 0.0, 1.0);
|
||||
}
|
||||
|
||||
// Piecewise-linear ramp across the four palette stops.
|
||||
vec3 ramp(float v) {
|
||||
float x = clamp(v, 0.0, 1.0) * 3.0;
|
||||
vec3 c = mix(uPal0, uPal1, clamp(x, 0.0, 1.0));
|
||||
c = mix(c, uPal2, clamp(x - 1.0, 0.0, 1.0));
|
||||
c = mix(c, uPal3, clamp(x - 2.0, 0.0, 1.0));
|
||||
return c;
|
||||
}
|
||||
|
||||
void main() {
|
||||
// One sample per cell, taken at the cell centre.
|
||||
vec2 cell = floor(gl_FragCoord.xy / uCell);
|
||||
vec2 centre = (cell + 0.5) * uCell;
|
||||
|
||||
vec2 st = centre / uRes;
|
||||
st.x *= uRes.x / uRes.y;
|
||||
|
||||
float hue = abs(snoise(vec3(st * uFreq, uTime * uSpeed)));
|
||||
vec3 field = hue2rgb(hue);
|
||||
|
||||
// The gamma is the whole look: it crushes the midtones so most of the frame
|
||||
// stays background and only the crests of the field open up.
|
||||
float gray = pow(dot(field, vec3(0.2126, 0.7152, 0.0722)), uGamma);
|
||||
float v = clamp(gray + uBias, 0.0, 1.0);
|
||||
|
||||
float radius = v * 0.5 * uCell;
|
||||
float d = length(gl_FragCoord.xy - centre);
|
||||
float mask = 1.0 - smoothstep(radius - 1.0, radius + 1.0, d);
|
||||
|
||||
gl_FragColor = vec4(mix(uBg, ramp(v), mask), 1.0);
|
||||
}
|
||||
`;
|
||||
|
||||
var uni = {};
|
||||
var ready = false;
|
||||
|
||||
function compile(type, src) {
|
||||
var sh = gl.createShader(type);
|
||||
gl.shaderSource(sh, src);
|
||||
gl.compileShader(sh);
|
||||
if (!gl.getShaderParameter(sh, gl.COMPILE_STATUS)) {
|
||||
throw new Error("halftone-field shader: " + gl.getShaderInfoLog(sh));
|
||||
}
|
||||
return sh;
|
||||
}
|
||||
|
||||
if (gl) {
|
||||
var prog = gl.createProgram();
|
||||
gl.attachShader(prog, compile(gl.VERTEX_SHADER, VERT));
|
||||
gl.attachShader(prog, compile(gl.FRAGMENT_SHADER, FRAG));
|
||||
gl.linkProgram(prog);
|
||||
if (!gl.getProgramParameter(prog, gl.LINK_STATUS)) {
|
||||
throw new Error("halftone-field link: " + gl.getProgramInfoLog(prog));
|
||||
}
|
||||
gl.useProgram(prog);
|
||||
|
||||
var buf = gl.createBuffer();
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, buf);
|
||||
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1, -1, 3, -1, -1, 3]), gl.STATIC_DRAW);
|
||||
var loc = gl.getAttribLocation(prog, "aPos");
|
||||
gl.enableVertexAttribArray(loc);
|
||||
gl.vertexAttribPointer(loc, 2, gl.FLOAT, false, 0, 0);
|
||||
|
||||
[
|
||||
"uRes",
|
||||
"uTime",
|
||||
"uFreq",
|
||||
"uSpeed",
|
||||
"uCell",
|
||||
"uGamma",
|
||||
"uBias",
|
||||
"uPal0",
|
||||
"uPal1",
|
||||
"uPal2",
|
||||
"uPal3",
|
||||
"uBg",
|
||||
].forEach(function (n) {
|
||||
uni[n] = gl.getUniformLocation(prog, n);
|
||||
});
|
||||
|
||||
gl.viewport(0, 0, W, H);
|
||||
gl.uniform2f(uni.uRes, W, H);
|
||||
gl.uniform1f(uni.uFreq, FREQ);
|
||||
gl.uniform1f(uni.uSpeed, SPEED);
|
||||
gl.uniform1f(uni.uCell, CELL);
|
||||
gl.uniform1f(uni.uGamma, GAMMA);
|
||||
gl.uniform1f(uni.uBias, BIAS);
|
||||
gl.uniform3f(uni.uPal0, PAL[0][0], PAL[0][1], PAL[0][2]);
|
||||
gl.uniform3f(uni.uPal1, PAL[1][0], PAL[1][1], PAL[1][2]);
|
||||
gl.uniform3f(uni.uPal2, PAL[2][0], PAL[2][1], PAL[2][2]);
|
||||
gl.uniform3f(uni.uPal3, PAL[3][0], PAL[3][1], PAL[3][2]);
|
||||
gl.uniform3f(uni.uBg, BG[0], BG[1], BG[2]);
|
||||
ready = true;
|
||||
}
|
||||
|
||||
// Every frame is computed from t alone — the field is a closed-form
|
||||
// function of position and time. No accumulator, no clock, no PRNG.
|
||||
// `quantize` snaps t to a 30fps grid for the stepped LED-wall look.
|
||||
function draw(t) {
|
||||
if (!ready) return;
|
||||
gl.uniform1f(uni.uTime, QUANTIZE ? Math.floor(t * 30) / 30 : t);
|
||||
gl.drawArrays(gl.TRIANGLES, 0, 3);
|
||||
gl.flush();
|
||||
}
|
||||
|
||||
window.__timelines = window.__timelines || {};
|
||||
var tl = gsap.timeline({ paused: true });
|
||||
|
||||
// The canvas is repainted from a property SETTER, not from onUpdate:
|
||||
// gsap's seek(t) suppresses events by default, so an onUpdate callback
|
||||
// silently never fires on a scrub and the canvas freezes on frame 0.
|
||||
// Tweened values are always written during render, suppressed or not,
|
||||
// so this fires on every seek — and hands us the frame time directly.
|
||||
var driver = { _t: 0 };
|
||||
Object.defineProperty(driver, "t", {
|
||||
get: function () {
|
||||
return this._t;
|
||||
},
|
||||
set: function (v) {
|
||||
this._t = v;
|
||||
draw(v);
|
||||
},
|
||||
});
|
||||
tl.to(driver, { t: DUR, duration: DUR, ease: "none" }, 0);
|
||||
window.__timelines["halftone-field"] = tl;
|
||||
|
||||
draw(0);
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
|
||||
{/* hf:generated-footer */}
|
||||
|
||||
Tagged `webgl` `shader` `background` `texture` `halftone`.
|
||||
@@ -863,3 +489,7 @@ Tagged `webgl` `shader` `background` `texture` `halftone`.
|
||||
- [Browse the complete Catalog](/catalog)
|
||||
- [Add assets and Catalog items in Studio](/studio/assets-and-blocks)
|
||||
- [Build a richer composition](/go-further)
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
</CatalogDetail>
|
||||
|
||||
@@ -1,42 +1,28 @@
|
||||
---
|
||||
title: "HeyGen Avatar Promo Card"
|
||||
description: "A portrait HeyGen offer card pairs a moving avatar mosaic with bold promotional typography and a branded callout"
|
||||
mode: "frame"
|
||||
---
|
||||
|
||||
import { CatalogDetail, CatalogSlot } from "/snippets/catalog-detail.jsx";
|
||||
import { InstallCommand } from "/snippets/install-command.jsx";
|
||||
|
||||
## Install
|
||||
<CatalogDetail
|
||||
previewSrc="/public/catalog/blocks/heygen-avatar-promo-card.json"
|
||||
compositionId="heygen-avatar-promo-card"
|
||||
compositionSrc="compositions/heygen-avatar-promo-card.html"
|
||||
title="HeyGen Avatar Promo Card"
|
||||
description="A portrait HeyGen offer card pairs a moving avatar mosaic with bold promotional typography and a branded callout"
|
||||
variables={[{"id":"brandLogo","type":"image","role":"content","label":"Brand logo","description":"Horizontal transparent wordmark used in the header and belt.","default":"assets/heygen-logo.svg"},{"id":"titleLine1","type":"string","role":"content","label":"Headline line 1","description":"First headline line.","default":"Your avatar,"},{"id":"titleLine2","type":"string","role":"content","label":"Headline line 2","description":"Second headline line.","default":"by HeyGen."},{"id":"subhead","type":"string","role":"content","label":"Subhead","description":"Supporting line under the headline.","default":"AI video, camera-free."},{"id":"pill1","type":"string","role":"content","label":"Pill 1","description":"First feature pill.","default":"4K AVATARS"},{"id":"pill2","type":"string","role":"content","label":"Pill 2","description":"Second feature pill.","default":"NO CAMERA NEEDED"},{"id":"offerValue","type":"string","role":"content","label":"Offer value","description":"Large offer value in the light tile.","default":"25%"},{"id":"offerSuffix","type":"string","role":"content","label":"Offer suffix","description":"Large offer suffix in the adjacent light tile.","default":"OFF"},{"id":"codeLabel","type":"string","role":"content","label":"Promo code label","description":"Small label above the promo code.","default":"CODE"},{"id":"promoPrefix","type":"string","role":"content","label":"Promo code prefix","description":"Accent-colored promo-code prefix.","default":"GEN"},{"id":"promoSuffix","type":"string","role":"content","label":"Promo code suffix","description":"Light promo-code suffix.","default":"25"},{"id":"canvasColor","type":"color","role":"style","label":"Canvas color","description":"Main card background.","default":"#eaf6fa"},{"id":"inkColor","type":"color","role":"style","label":"Ink color","description":"Primary headline, pill, and offer ink.","default":"#0a1418"},{"id":"mutedInkColor","type":"color","role":"style","label":"Muted ink color","description":"Subhead ink.","default":"#47545b"},{"id":"accentColor","type":"color","role":"style","label":"Accent color","description":"Brand accent used by tiles and promo-code prefix.","default":"#00c3ff"},{"id":"lightTileColor","type":"color","role":"style","label":"Light tile color","description":"Light belt-tile surface.","default":"#f7fcfe"},{"id":"darkTileColor","type":"color","role":"style","label":"Dark tile color","description":"Dark belt-tile surface.","default":"#0b1215"}]}
|
||||
meta={{"duration":10,"width":1080,"height":1920,"category":"Showcases","badge":"Stable","codeLines":426}}
|
||||
attribution={{"author":"HeyGen","path":"registry/blocks/heygen-avatar-promo-card","tags":["showcase","avatar","promotion","vertical"]}}
|
||||
hasCode
|
||||
rawUrl="https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/heygen-avatar-promo-card/heygen-avatar-promo-card.html"
|
||||
>
|
||||
|
||||
<InstallCommand command="npx hyperframes add heygen-avatar-promo-card" item="heygen-avatar-promo-card" />
|
||||
<CatalogSlot slot="code">
|
||||
|
||||
That writes `compositions/heygen-avatar-promo-card.html`, plus 15 supporting files under `assets/` and `assets/fonts/`.
|
||||
|
||||
<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/heygen-avatar-promo-card.mp4" poster="https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/heygen-avatar-promo-card.png" autoPlay muted loop playsInline />
|
||||
|
||||
## Add it to your video
|
||||
|
||||
It runs for 10 seconds at 1080×1920. Paste this into your composition:
|
||||
|
||||
```html index.html
|
||||
<div
|
||||
data-composition-id="heygen-avatar-promo-card"
|
||||
data-composition-src="compositions/heygen-avatar-promo-card.html"
|
||||
data-start="0"
|
||||
data-duration="10"
|
||||
data-track-index="1"
|
||||
data-width="1080"
|
||||
data-height="1920"
|
||||
></div>
|
||||
```
|
||||
|
||||
Move it in time with `data-start`. Put it on a different timeline row with
|
||||
`data-track-index`. See [data attributes](/concepts/data-attributes) for the rest.
|
||||
|
||||
## Source
|
||||
|
||||
<Accordion title="heygen-avatar-promo-card.html">
|
||||
|
||||
```html
|
||||
```html heygen-avatar-promo-card.html
|
||||
<!doctype html>
|
||||
<html
|
||||
lang="en"
|
||||
@@ -465,7 +451,36 @@ Move it in time with `data-start`. Put it on a different timeline row with
|
||||
</html>
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
</CatalogSlot>
|
||||
|
||||
<CatalogSlot slot="install">
|
||||
|
||||
<InstallCommand command="npx hyperframes add heygen-avatar-promo-card" item="heygen-avatar-promo-card" />
|
||||
|
||||
That writes `compositions/heygen-avatar-promo-card.html`, plus 15 supporting files under `assets/` and `assets/fonts/`.
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
<CatalogSlot slot="docs">
|
||||
|
||||
## Add it to your video
|
||||
|
||||
It runs for 10 seconds at 1080×1920. Paste this into your composition:
|
||||
|
||||
```html index.html
|
||||
<div
|
||||
data-composition-id="heygen-avatar-promo-card"
|
||||
data-composition-src="compositions/heygen-avatar-promo-card.html"
|
||||
data-start="0"
|
||||
data-duration="10"
|
||||
data-track-index="1"
|
||||
data-width="1080"
|
||||
data-height="1920"
|
||||
></div>
|
||||
```
|
||||
|
||||
Move it in time with `data-start`. Put it on a different timeline row with
|
||||
`data-track-index`. See [data attributes](/concepts/data-attributes) for the rest.
|
||||
|
||||
{/* hf:generated-footer */}
|
||||
|
||||
@@ -478,3 +493,7 @@ Created by HeyGen.
|
||||
- [Browse the complete Catalog](/catalog)
|
||||
- [Add assets and Catalog items in Studio](/studio/assets-and-blocks)
|
||||
- [Build a richer composition](/go-further)
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
</CatalogDetail>
|
||||
|
||||
@@ -1,47 +1,28 @@
|
||||
---
|
||||
title: "Sketched Frame"
|
||||
description: "Media in a hand-drawn border with corner doodles and a handwritten caption; the unit tilts slightly and boils (images inside the block; keep live footage in the host)"
|
||||
mode: "frame"
|
||||
---
|
||||
|
||||
import { CatalogDetail, CatalogSlot } from "/snippets/catalog-detail.jsx";
|
||||
import { InstallCommand } from "/snippets/install-command.jsx";
|
||||
|
||||
## Install
|
||||
<CatalogDetail
|
||||
previewSrc="/public/catalog/blocks/hw-frame.json"
|
||||
compositionId="hw-frame"
|
||||
compositionSrc="compositions/hw-frame.html"
|
||||
title="Sketched Frame"
|
||||
description="Media in a hand-drawn border with corner doodles and a handwritten caption; the unit tilts slightly and boils (images inside the block; keep live footage in the host)"
|
||||
variables={[]}
|
||||
meta={{"duration":8,"width":1920,"height":1080,"category":"Blocks","badge":"Stable","codeLines":302}}
|
||||
attribution={{"path":"registry/blocks/hw-frame","tags":["handwritten","frame","media"]}}
|
||||
hasCode
|
||||
rawUrl="https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/hw-frame/hw-frame.html"
|
||||
>
|
||||
|
||||
<InstallCommand command="npx hyperframes add hw-frame" item="hw-frame" />
|
||||
<CatalogSlot slot="code">
|
||||
|
||||
That writes `compositions/hw-frame.html`, plus 1 supporting file under `assets/fonts/`.
|
||||
|
||||
<iframe
|
||||
className="w-full aspect-video rounded-xl border-0 bg-zinc-100 dark:bg-zinc-800"
|
||||
title="hw-frame preview"
|
||||
loading="lazy"
|
||||
srcDoc={`<!doctype html><html><head><meta charset="utf-8"><style>html,body{margin:0;height:100%;overflow:hidden;background:transparent}hyperframes-player{display:block;width:100%;height:100%}</style><script src="https://cdn.jsdelivr.net/npm/@hyperframes/player@latest/dist/hyperframes-player.global.js"><\/script></head><body><script>fetch("/public/catalog/blocks/hw-frame.json").then(function(r){return r.json()}).then(function(d){var p=document.createElement("hyperframes-player");p.setAttribute("srcdoc",d.html);p.setAttribute("controls","");p.setAttribute("autoplay","");p.setAttribute("loop","");p.setAttribute("muted","");p.setAttribute("poster","https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/hw-frame.png");document.body.appendChild(p)});<\/script></body></html>`}
|
||||
/>
|
||||
|
||||
## Add it to your video
|
||||
|
||||
It runs for 8 seconds at 1920×1080. Paste this into your composition:
|
||||
|
||||
```html index.html
|
||||
<div
|
||||
data-composition-id="hw-frame"
|
||||
data-composition-src="compositions/hw-frame.html"
|
||||
data-start="0"
|
||||
data-duration="8"
|
||||
data-track-index="1"
|
||||
data-width="1920"
|
||||
data-height="1080"
|
||||
></div>
|
||||
```
|
||||
|
||||
Move it in time with `data-start`. Put it on a different timeline row with
|
||||
`data-track-index`. See [data attributes](/concepts/data-attributes) for the rest.
|
||||
|
||||
## Source
|
||||
|
||||
<Accordion title="hw-frame.html">
|
||||
|
||||
```html
|
||||
```html hw-frame.html
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
@@ -346,7 +327,36 @@ Move it in time with `data-start`. Put it on a different timeline row with
|
||||
</html>
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
</CatalogSlot>
|
||||
|
||||
<CatalogSlot slot="install">
|
||||
|
||||
<InstallCommand command="npx hyperframes add hw-frame" item="hw-frame" />
|
||||
|
||||
That writes `compositions/hw-frame.html`, plus 1 supporting file under `assets/fonts/`.
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
<CatalogSlot slot="docs">
|
||||
|
||||
## Add it to your video
|
||||
|
||||
It runs for 8 seconds at 1920×1080. Paste this into your composition:
|
||||
|
||||
```html index.html
|
||||
<div
|
||||
data-composition-id="hw-frame"
|
||||
data-composition-src="compositions/hw-frame.html"
|
||||
data-start="0"
|
||||
data-duration="8"
|
||||
data-track-index="1"
|
||||
data-width="1920"
|
||||
data-height="1080"
|
||||
></div>
|
||||
```
|
||||
|
||||
Move it in time with `data-start`. Put it on a different timeline row with
|
||||
`data-track-index`. See [data attributes](/concepts/data-attributes) for the rest.
|
||||
|
||||
{/* hf:generated-footer */}
|
||||
|
||||
@@ -357,3 +367,7 @@ Tagged `handwritten` `frame` `media`.
|
||||
- [Browse the complete Catalog](/catalog)
|
||||
- [Add assets and Catalog items in Studio](/studio/assets-and-blocks)
|
||||
- [Build a richer composition](/go-further)
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
</CatalogDetail>
|
||||
|
||||
@@ -1,47 +1,28 @@
|
||||
---
|
||||
title: "Text Along a Path"
|
||||
description: "Handwriting flowing along any SVG path with a per-character reveal; the path is the only geometry input"
|
||||
mode: "frame"
|
||||
---
|
||||
|
||||
import { CatalogDetail, CatalogSlot } from "/snippets/catalog-detail.jsx";
|
||||
import { InstallCommand } from "/snippets/install-command.jsx";
|
||||
|
||||
## Install
|
||||
<CatalogDetail
|
||||
previewSrc="/public/catalog/blocks/hw-path-text.json"
|
||||
compositionId="hw-path-text"
|
||||
compositionSrc="compositions/hw-path-text.html"
|
||||
title="Text Along a Path"
|
||||
description="Handwriting flowing along any SVG path with a per-character reveal; the path is the only geometry input"
|
||||
variables={[]}
|
||||
meta={{"duration":6,"width":1920,"height":1080,"category":"Typography & Text","badge":"Stable","codeLines":194}}
|
||||
attribution={{"path":"registry/blocks/hw-path-text","tags":["handwritten","typography","path","overlay"]}}
|
||||
hasCode
|
||||
rawUrl="https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/hw-path-text/hw-path-text.html"
|
||||
>
|
||||
|
||||
<InstallCommand command="npx hyperframes add hw-path-text" item="hw-path-text" />
|
||||
<CatalogSlot slot="code">
|
||||
|
||||
That writes `compositions/hw-path-text.html`, plus 1 supporting file under `assets/fonts/`.
|
||||
|
||||
<iframe
|
||||
className="w-full aspect-video rounded-xl border-0 bg-zinc-100 dark:bg-zinc-800"
|
||||
title="hw-path-text preview"
|
||||
loading="lazy"
|
||||
srcDoc={`<!doctype html><html><head><meta charset="utf-8"><style>html,body{margin:0;height:100%;overflow:hidden;background:transparent}hyperframes-player{display:block;width:100%;height:100%}</style><script src="https://cdn.jsdelivr.net/npm/@hyperframes/player@latest/dist/hyperframes-player.global.js"><\/script></head><body><script>fetch("/public/catalog/blocks/hw-path-text.json").then(function(r){return r.json()}).then(function(d){var p=document.createElement("hyperframes-player");p.setAttribute("srcdoc",d.html);p.setAttribute("controls","");p.setAttribute("autoplay","");p.setAttribute("loop","");p.setAttribute("muted","");p.setAttribute("poster","https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/hw-path-text.png");document.body.appendChild(p)});<\/script></body></html>`}
|
||||
/>
|
||||
|
||||
## Add it to your video
|
||||
|
||||
It runs for 6 seconds at 1920×1080. Paste this into your composition:
|
||||
|
||||
```html index.html
|
||||
<div
|
||||
data-composition-id="hw-path-text"
|
||||
data-composition-src="compositions/hw-path-text.html"
|
||||
data-start="0"
|
||||
data-duration="6"
|
||||
data-track-index="1"
|
||||
data-width="1920"
|
||||
data-height="1080"
|
||||
></div>
|
||||
```
|
||||
|
||||
Move it in time with `data-start`. Put it on a different timeline row with
|
||||
`data-track-index`. See [data attributes](/concepts/data-attributes) for the rest.
|
||||
|
||||
## Source
|
||||
|
||||
<Accordion title="hw-path-text.html">
|
||||
|
||||
```html
|
||||
```html hw-path-text.html
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
@@ -190,8 +171,10 @@ Move it in time with `data-start`. Put it on a different timeline row with
|
||||
});
|
||||
};
|
||||
|
||||
document.getElementById("hw-pt-path").setAttribute("d", CONFIG.d);
|
||||
if (CONFIG.showGuide) document.getElementById("hw-pt-guide").setAttribute("d", CONFIG.d);
|
||||
var pathEl = document.getElementById("hw-pt-path");
|
||||
var guide = document.getElementById("hw-pt-guide");
|
||||
pathEl.setAttribute("d", CONFIG.d);
|
||||
if (CONFIG.showGuide) guide.setAttribute("d", CONFIG.d);
|
||||
document.getElementById("hw-pt-text").setAttribute("font-size", CONFIG.fontSize);
|
||||
|
||||
/* per-char tspans along the path */
|
||||
@@ -206,7 +189,6 @@ Move it in time with `data-start`. Put it on a different timeline row with
|
||||
var tl = gsap.timeline({ paused: true });
|
||||
|
||||
/* guide draws first, then the handwriting types along it */
|
||||
var guide = document.getElementById("hw-pt-guide");
|
||||
if (CONFIG.showGuide) {
|
||||
var glen = guide.getTotalLength();
|
||||
gsap.set(guide, { strokeDasharray: "2 14", strokeDashoffset: glen });
|
||||
@@ -237,7 +219,36 @@ Move it in time with `data-start`. Put it on a different timeline row with
|
||||
</html>
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
</CatalogSlot>
|
||||
|
||||
<CatalogSlot slot="install">
|
||||
|
||||
<InstallCommand command="npx hyperframes add hw-path-text" item="hw-path-text" />
|
||||
|
||||
That writes `compositions/hw-path-text.html`, plus 1 supporting file under `assets/fonts/`.
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
<CatalogSlot slot="docs">
|
||||
|
||||
## Add it to your video
|
||||
|
||||
It runs for 6 seconds at 1920×1080. Paste this into your composition:
|
||||
|
||||
```html index.html
|
||||
<div
|
||||
data-composition-id="hw-path-text"
|
||||
data-composition-src="compositions/hw-path-text.html"
|
||||
data-start="0"
|
||||
data-duration="6"
|
||||
data-track-index="1"
|
||||
data-width="1920"
|
||||
data-height="1080"
|
||||
></div>
|
||||
```
|
||||
|
||||
Move it in time with `data-start`. Put it on a different timeline row with
|
||||
`data-track-index`. See [data attributes](/concepts/data-attributes) for the rest.
|
||||
|
||||
{/* hf:generated-footer */}
|
||||
|
||||
@@ -248,3 +259,7 @@ Tagged `handwritten` `typography` `path` `overlay`.
|
||||
- [Browse the complete Catalog](/catalog)
|
||||
- [Add assets and Catalog items in Studio](/studio/assets-and-blocks)
|
||||
- [Build a richer composition](/go-further)
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
</CatalogDetail>
|
||||
|
||||
@@ -1,47 +1,28 @@
|
||||
---
|
||||
title: "Hand-Drawn Flowchart"
|
||||
description: "Wobbled boxes with handwritten labels joined by curved connectors, drawing on in sequence from a node list"
|
||||
mode: "frame"
|
||||
---
|
||||
|
||||
import { CatalogDetail, CatalogSlot } from "/snippets/catalog-detail.jsx";
|
||||
import { InstallCommand } from "/snippets/install-command.jsx";
|
||||
|
||||
## Install
|
||||
<CatalogDetail
|
||||
previewSrc="/public/catalog/blocks/hw-pipeline.json"
|
||||
compositionId="hw-pipeline"
|
||||
compositionSrc="compositions/hw-pipeline.html"
|
||||
title="Hand-Drawn Flowchart"
|
||||
description="Wobbled boxes with handwritten labels joined by curved connectors, drawing on in sequence from a node list"
|
||||
variables={[]}
|
||||
meta={{"duration":7,"width":1920,"height":1080,"category":"Blocks","badge":"Stable","codeLines":317}}
|
||||
attribution={{"path":"registry/blocks/hw-pipeline","tags":["handwritten","diagram","data-viz","overlay"]}}
|
||||
hasCode
|
||||
rawUrl="https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/hw-pipeline/hw-pipeline.html"
|
||||
>
|
||||
|
||||
<InstallCommand command="npx hyperframes add hw-pipeline" item="hw-pipeline" />
|
||||
<CatalogSlot slot="code">
|
||||
|
||||
That writes `compositions/hw-pipeline.html`, plus 1 supporting file under `assets/fonts/`.
|
||||
|
||||
<iframe
|
||||
className="w-full aspect-video rounded-xl border-0 bg-zinc-100 dark:bg-zinc-800"
|
||||
title="hw-pipeline preview"
|
||||
loading="lazy"
|
||||
srcDoc={`<!doctype html><html><head><meta charset="utf-8"><style>html,body{margin:0;height:100%;overflow:hidden;background:transparent}hyperframes-player{display:block;width:100%;height:100%}</style><script src="https://cdn.jsdelivr.net/npm/@hyperframes/player@latest/dist/hyperframes-player.global.js"><\/script></head><body><script>fetch("/public/catalog/blocks/hw-pipeline.json").then(function(r){return r.json()}).then(function(d){var p=document.createElement("hyperframes-player");p.setAttribute("srcdoc",d.html);p.setAttribute("controls","");p.setAttribute("autoplay","");p.setAttribute("loop","");p.setAttribute("muted","");p.setAttribute("poster","https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/hw-pipeline.png");document.body.appendChild(p)});<\/script></body></html>`}
|
||||
/>
|
||||
|
||||
## Add it to your video
|
||||
|
||||
It runs for 7 seconds at 1920×1080. Paste this into your composition:
|
||||
|
||||
```html index.html
|
||||
<div
|
||||
data-composition-id="hw-pipeline"
|
||||
data-composition-src="compositions/hw-pipeline.html"
|
||||
data-start="0"
|
||||
data-duration="7"
|
||||
data-track-index="1"
|
||||
data-width="1920"
|
||||
data-height="1080"
|
||||
></div>
|
||||
```
|
||||
|
||||
Move it in time with `data-start`. Put it on a different timeline row with
|
||||
`data-track-index`. See [data attributes](/concepts/data-attributes) for the rest.
|
||||
|
||||
## Source
|
||||
|
||||
<Accordion title="hw-pipeline.html">
|
||||
|
||||
```html
|
||||
```html hw-pipeline.html
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
@@ -361,7 +342,36 @@ Move it in time with `data-start`. Put it on a different timeline row with
|
||||
</html>
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
</CatalogSlot>
|
||||
|
||||
<CatalogSlot slot="install">
|
||||
|
||||
<InstallCommand command="npx hyperframes add hw-pipeline" item="hw-pipeline" />
|
||||
|
||||
That writes `compositions/hw-pipeline.html`, plus 1 supporting file under `assets/fonts/`.
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
<CatalogSlot slot="docs">
|
||||
|
||||
## Add it to your video
|
||||
|
||||
It runs for 7 seconds at 1920×1080. Paste this into your composition:
|
||||
|
||||
```html index.html
|
||||
<div
|
||||
data-composition-id="hw-pipeline"
|
||||
data-composition-src="compositions/hw-pipeline.html"
|
||||
data-start="0"
|
||||
data-duration="7"
|
||||
data-track-index="1"
|
||||
data-width="1920"
|
||||
data-height="1080"
|
||||
></div>
|
||||
```
|
||||
|
||||
Move it in time with `data-start`. Put it on a different timeline row with
|
||||
`data-track-index`. See [data attributes](/concepts/data-attributes) for the rest.
|
||||
|
||||
{/* hf:generated-footer */}
|
||||
|
||||
@@ -372,3 +382,7 @@ Tagged `handwritten` `diagram` `data-viz` `overlay`.
|
||||
- [Browse the complete Catalog](/catalog)
|
||||
- [Add assets and Catalog items in Studio](/studio/assets-and-blocks)
|
||||
- [Build a richer composition](/go-further)
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
</CatalogDetail>
|
||||
|
||||
@@ -1,47 +1,28 @@
|
||||
---
|
||||
title: "Scribble Transition"
|
||||
description: "Overlay transition: scribble bands accumulate to cover the frame with a solid backstop, the host cuts beneath, and the scribble clears"
|
||||
mode: "frame"
|
||||
---
|
||||
|
||||
import { CatalogDetail, CatalogSlot } from "/snippets/catalog-detail.jsx";
|
||||
import { InstallCommand } from "/snippets/install-command.jsx";
|
||||
|
||||
## Install
|
||||
<CatalogDetail
|
||||
previewSrc="/public/catalog/blocks/hw-scribble-transition.json"
|
||||
compositionId="hw-scribble-transition"
|
||||
compositionSrc="compositions/hw-scribble-transition.html"
|
||||
title="Scribble Transition"
|
||||
description="Overlay transition: scribble bands accumulate to cover the frame with a solid backstop, the host cuts beneath, and the scribble clears"
|
||||
variables={[]}
|
||||
meta={{"duration":2.2,"width":1920,"height":1080,"category":"CSS Transitions","badge":"Stable","codeLines":152}}
|
||||
attribution={{"path":"registry/blocks/hw-scribble-transition","tags":["handwritten","transition","wipe"]}}
|
||||
hasCode
|
||||
rawUrl="https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/hw-scribble-transition/hw-scribble-transition.html"
|
||||
>
|
||||
|
||||
<InstallCommand command="npx hyperframes add hw-scribble-transition" item="hw-scribble-transition" />
|
||||
<CatalogSlot slot="code">
|
||||
|
||||
That writes one file: `compositions/hw-scribble-transition.html`.
|
||||
|
||||
<iframe
|
||||
className="w-full aspect-video rounded-xl border-0 bg-zinc-100 dark:bg-zinc-800"
|
||||
title="hw-scribble-transition preview"
|
||||
loading="lazy"
|
||||
srcDoc={`<!doctype html><html><head><meta charset="utf-8"><style>html,body{margin:0;height:100%;overflow:hidden;background:transparent}hyperframes-player{display:block;width:100%;height:100%}</style><script src="https://cdn.jsdelivr.net/npm/@hyperframes/player@latest/dist/hyperframes-player.global.js"><\/script></head><body><script>fetch("/public/catalog/blocks/hw-scribble-transition.json").then(function(r){return r.json()}).then(function(d){var p=document.createElement("hyperframes-player");p.setAttribute("srcdoc",d.html);p.setAttribute("controls","");p.setAttribute("autoplay","");p.setAttribute("loop","");p.setAttribute("muted","");p.setAttribute("poster","https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/hw-scribble-transition.png");document.body.appendChild(p)});<\/script></body></html>`}
|
||||
/>
|
||||
|
||||
## Add it to your video
|
||||
|
||||
It runs for 2.2 seconds at 1920×1080. Paste this into your composition:
|
||||
|
||||
```html index.html
|
||||
<div
|
||||
data-composition-id="hw-scribble-transition"
|
||||
data-composition-src="compositions/hw-scribble-transition.html"
|
||||
data-start="0"
|
||||
data-duration="2.2"
|
||||
data-track-index="1"
|
||||
data-width="1920"
|
||||
data-height="1080"
|
||||
></div>
|
||||
```
|
||||
|
||||
Move it in time with `data-start`. Put it on a different timeline row with
|
||||
`data-track-index`. See [data attributes](/concepts/data-attributes) for the rest.
|
||||
|
||||
## Source
|
||||
|
||||
<Accordion title="hw-scribble-transition.html">
|
||||
|
||||
```html
|
||||
```html hw-scribble-transition.html
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
@@ -196,7 +177,36 @@ Move it in time with `data-start`. Put it on a different timeline row with
|
||||
</html>
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
</CatalogSlot>
|
||||
|
||||
<CatalogSlot slot="install">
|
||||
|
||||
<InstallCommand command="npx hyperframes add hw-scribble-transition" item="hw-scribble-transition" />
|
||||
|
||||
That writes one file: `compositions/hw-scribble-transition.html`.
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
<CatalogSlot slot="docs">
|
||||
|
||||
## Add it to your video
|
||||
|
||||
It runs for 2.2 seconds at 1920×1080. Paste this into your composition:
|
||||
|
||||
```html index.html
|
||||
<div
|
||||
data-composition-id="hw-scribble-transition"
|
||||
data-composition-src="compositions/hw-scribble-transition.html"
|
||||
data-start="0"
|
||||
data-duration="2.2"
|
||||
data-track-index="1"
|
||||
data-width="1920"
|
||||
data-height="1080"
|
||||
></div>
|
||||
```
|
||||
|
||||
Move it in time with `data-start`. Put it on a different timeline row with
|
||||
`data-track-index`. See [data attributes](/concepts/data-attributes) for the rest.
|
||||
|
||||
{/* hf:generated-footer */}
|
||||
|
||||
@@ -207,3 +217,7 @@ Tagged `handwritten` `transition` `wipe`.
|
||||
- [Browse the complete Catalog](/catalog)
|
||||
- [Add assets and Catalog items in Studio](/studio/assets-and-blocks)
|
||||
- [Build a richer composition](/go-further)
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
</CatalogDetail>
|
||||
|
||||
@@ -1,47 +1,28 @@
|
||||
---
|
||||
title: "Speech Bubble"
|
||||
description: "Hand-drawn speech bubble with a positionable tail: outline draws on, paper fill fades in, handwritten text types per character; boils"
|
||||
mode: "frame"
|
||||
---
|
||||
|
||||
import { CatalogDetail, CatalogSlot } from "/snippets/catalog-detail.jsx";
|
||||
import { InstallCommand } from "/snippets/install-command.jsx";
|
||||
|
||||
## Install
|
||||
<CatalogDetail
|
||||
previewSrc="/public/catalog/blocks/hw-text-cloud.json"
|
||||
compositionId="hw-text-cloud"
|
||||
compositionSrc="compositions/hw-text-cloud.html"
|
||||
title="Speech Bubble"
|
||||
description="Hand-drawn speech bubble with a positionable tail: outline draws on, paper fill fades in, handwritten text types per character; boils"
|
||||
variables={[]}
|
||||
meta={{"duration":6,"width":1920,"height":1080,"category":"Blocks","badge":"Stable","codeLines":286}}
|
||||
attribution={{"path":"registry/blocks/hw-text-cloud","tags":["handwritten","speech-bubble","overlay"]}}
|
||||
hasCode
|
||||
rawUrl="https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/hw-text-cloud/hw-text-cloud.html"
|
||||
>
|
||||
|
||||
<InstallCommand command="npx hyperframes add hw-text-cloud" item="hw-text-cloud" />
|
||||
<CatalogSlot slot="code">
|
||||
|
||||
That writes `compositions/hw-text-cloud.html`, plus 1 supporting file under `assets/fonts/`.
|
||||
|
||||
<iframe
|
||||
className="w-full aspect-video rounded-xl border-0 bg-zinc-100 dark:bg-zinc-800"
|
||||
title="hw-text-cloud preview"
|
||||
loading="lazy"
|
||||
srcDoc={`<!doctype html><html><head><meta charset="utf-8"><style>html,body{margin:0;height:100%;overflow:hidden;background:transparent}hyperframes-player{display:block;width:100%;height:100%}</style><script src="https://cdn.jsdelivr.net/npm/@hyperframes/player@latest/dist/hyperframes-player.global.js"><\/script></head><body><script>fetch("/public/catalog/blocks/hw-text-cloud.json").then(function(r){return r.json()}).then(function(d){var p=document.createElement("hyperframes-player");p.setAttribute("srcdoc",d.html);p.setAttribute("controls","");p.setAttribute("autoplay","");p.setAttribute("loop","");p.setAttribute("muted","");p.setAttribute("poster","https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/hw-text-cloud.png");document.body.appendChild(p)});<\/script></body></html>`}
|
||||
/>
|
||||
|
||||
## Add it to your video
|
||||
|
||||
It runs for 6 seconds at 1920×1080. Paste this into your composition:
|
||||
|
||||
```html index.html
|
||||
<div
|
||||
data-composition-id="hw-text-cloud"
|
||||
data-composition-src="compositions/hw-text-cloud.html"
|
||||
data-start="0"
|
||||
data-duration="6"
|
||||
data-track-index="1"
|
||||
data-width="1920"
|
||||
data-height="1080"
|
||||
></div>
|
||||
```
|
||||
|
||||
Move it in time with `data-start`. Put it on a different timeline row with
|
||||
`data-track-index`. See [data attributes](/concepts/data-attributes) for the rest.
|
||||
|
||||
## Source
|
||||
|
||||
<Accordion title="hw-text-cloud.html">
|
||||
|
||||
```html
|
||||
```html hw-text-cloud.html
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
@@ -272,8 +253,10 @@ Move it in time with `data-start`. Put it on a different timeline row with
|
||||
var svg = document.getElementById("hw-tc-svg");
|
||||
svg.setAttribute("viewBox", "0 0 " + CONFIG.w + " " + CONFIG.h);
|
||||
var d = cloudPath(CONFIG.w, CONFIG.h, CONFIG.tip, CONFIG.seed);
|
||||
document.getElementById("hw-tc-fill").setAttribute("d", d);
|
||||
document.getElementById("hw-tc-stroke").setAttribute("d", d);
|
||||
var fillEl = document.getElementById("hw-tc-fill");
|
||||
var stroke = document.getElementById("hw-tc-stroke");
|
||||
fillEl.setAttribute("d", d);
|
||||
stroke.setAttribute("d", d);
|
||||
|
||||
var text = document.getElementById("hw-tc-text");
|
||||
text.style.left = CONFIG.w * 0.12 + "px";
|
||||
@@ -293,7 +276,6 @@ Move it in time with `data-start`. Put it on a different timeline row with
|
||||
var tl = gsap.timeline({ paused: true });
|
||||
|
||||
/* draw outline, fade fill, type text */
|
||||
var stroke = document.getElementById("hw-tc-stroke");
|
||||
var len = stroke.getTotalLength();
|
||||
gsap.set(stroke, { strokeDasharray: len, strokeDashoffset: len });
|
||||
var t0 = CONFIG.animationIn ? 0.3 : 0;
|
||||
@@ -329,7 +311,36 @@ Move it in time with `data-start`. Put it on a different timeline row with
|
||||
</html>
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
</CatalogSlot>
|
||||
|
||||
<CatalogSlot slot="install">
|
||||
|
||||
<InstallCommand command="npx hyperframes add hw-text-cloud" item="hw-text-cloud" />
|
||||
|
||||
That writes `compositions/hw-text-cloud.html`, plus 1 supporting file under `assets/fonts/`.
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
<CatalogSlot slot="docs">
|
||||
|
||||
## Add it to your video
|
||||
|
||||
It runs for 6 seconds at 1920×1080. Paste this into your composition:
|
||||
|
||||
```html index.html
|
||||
<div
|
||||
data-composition-id="hw-text-cloud"
|
||||
data-composition-src="compositions/hw-text-cloud.html"
|
||||
data-start="0"
|
||||
data-duration="6"
|
||||
data-track-index="1"
|
||||
data-width="1920"
|
||||
data-height="1080"
|
||||
></div>
|
||||
```
|
||||
|
||||
Move it in time with `data-start`. Put it on a different timeline row with
|
||||
`data-track-index`. See [data attributes](/concepts/data-attributes) for the rest.
|
||||
|
||||
{/* hf:generated-footer */}
|
||||
|
||||
@@ -340,3 +351,7 @@ Tagged `handwritten` `speech-bubble` `overlay`.
|
||||
- [Browse the complete Catalog](/catalog)
|
||||
- [Add assets and Catalog items in Studio](/studio/assets-and-blocks)
|
||||
- [Build a richer composition](/go-further)
|
||||
|
||||
</CatalogSlot>
|
||||
|
||||
</CatalogDetail>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user