Activated Cloud
← App Store

HyperFrames Core

Activated Cloud✓ Officialactivated/hyperframes-core

No ratings yet0 installsv1.0.0Updated Oct 6, 2026● Unknown

Free · Apache-2.0

About

The HyperFrames composition contract: how to build one renderable HTML-to-video project. Covers root and clip data attributes, the clip class, tracks, sub-compositions, variables, framework-owned media, deterministic rendering, editing recipes and validation. Read it before you write or edit composition HTML. Not for motion recipes (hyperframes-animation), zoom and camera keyframes (hyperframes-keyframes), CLI commands (hyperframes-cli), the end-to-end workflow and brief (hyperframes) or timeline layout conventions (hyperframes-studio).

Media

Documentation

From SKILL.md · v1.0.0 · what the agent reads when it loads this skill13 files: SKILL.md, references/CREDITS.md, references/composition-patterns.md, references/computer-setup.md, references/creator-editing-recipes.md, references/data-attributes.md…

HyperFrames Core

HyperFrames renders video from HTML. A composition is an HTML file whose DOM declares timing with data-* attributes, whose animation runtime is seekable, and whose media playback is owned by the framework. This skill is the technical contract for building one HyperFrames project: the same time always gives the same frame, and lint and check finish with zero findings. The body is the build guide; per-topic detail lives in references/, read on demand.

When to use

  • "Make a 15 second title card / promo / explainer in HyperFrames": before you write the first line of composition HTML.
  • "Add this clip, image or music", "cut, trim, reorder, slow down, freeze, crossfade or swap this file".
  • "Split this into scenes", "reuse this scene with different text", "make the title a variable".
  • lint or check reports a code you do not recognise, or a render comes out blank, frozen, silent or unstyled.
  • Opening an existing HyperFrames project to change it.

Neighbours, loaded with skill_view when the task needs them: hyperframes (the end-to-end workflow: brief, storyboard, production and review), hyperframes-animation (motion rules, blueprints, runtime adapters), hyperframes-keyframes (zooms, punch-ins, camera moves), hyperframes-cli (commands and render flags), hyperframes-studio (timeline layout and safe zones), hyperframes-audio (mixing placed audio), media-use (sourcing voice, music, images).

What you need

  • Your computer set up: run the check in references/computer-setup.md first, every job. Node 22+ and FFmpeg are not installed by default, and installs outside /home/user vanish when the computer is rebuilt. Every CLI call in this skill assumes this line first: export PATH="$HOME/.local/node/bin:$PATH" HYPERFRAMES_NO_TELEMETRY=1 HYPERFRAMES_SKIP_SKILLS=1
  • The project in ~/Desktop/<your name> - Work space/<job>/<project>/, scaffolded with npx hyperframes init <project> --non-interactive (add --tailwind only for Tailwind work).
  • Canvas size (1920x1080, 1080x1920, 1080x1080) and length in seconds from the brief. If the brief does not say, ask with clarify.
  • Media already inside assets/: voice from text_to_speech, pictures from image_generate, music and SFX only with a licence recorded in assets/LICENSES.md.
File in references/ Read it to
minimal-composition.md start from the smallest renderable composition skeleton
composition-patterns.md choose monolithic or modular; structure a modular index.html; pick a sub-composition archetype
data-attributes.md look up any data-* (root, clip, sub-composition host, legacy aliases); use class="clip"
tracks-and-clips.md see what data-track-index does and does not control, z-index, relative clip timing, npx hyperframes timeline
creator-editing-recipes.md copy cut, trim, reorder, retime, freeze, camera, mask, crossfade and audio recipes, with their limits
sub-compositions.md wire a sub-composition (host attributes, <template>, per-instance variables) and animate inside it
variables-and-media.md declare variables; place <video> and <audio>, set volume, trim
determinism-rules.md build a seekable timeline; determinism bans; layout and text fit
full-screen-motion.md author full-frame motion with a shared background
tailwind.md work in a Tailwind v4 project (init --tailwind; its runtime contract differs from Studio's v3)
computer-setup.md install and check Node, FFmpeg and the render browser

For animation runtime specifics (GSAP API, Lottie, Three.js and the rest), use hyperframes-animation and its references/adapters/<runtime>.md.

Rules

Two root forms (not interchangeable)

  • Standalone (top-level index.html): the root <div data-composition-id="…"> sits directly in <body>, with no <template> wrapper. Wrapping a standalone root hides all content, and lint rejects it (standalone_composition_wrapped_in_template, error).
  • Sub-composition (loaded via data-composition-src): wrap the root in <template>. This is the shape to write: the loader also accepts a plain full document and falls back to its <body>, but the templated form is what the examples and tooling assume.
  • Warning, transport rule: for a templated sub-composition the assembler drops the file's own <head> <style> and <script> (the hasTemplate gate in packages/core/src/compiler/compositionAssembly.ts, in the open-source HyperFrames repository), so put <style> and <script> inside the template. <link> is hoisted either way.
  • Warning, host-id convention: give the host slot, the inner template root and the window.__timelines["<id>"] key the same id. A different local id is supported (the assembler falls back to the first root in the file), but the mismatch is silent and can leave a scene frozen at render (Pitfall 2 in references/sub-compositions.md), so match them unless you have a reason not to.

File shape, host wiring and the pre-render checklist: references/sub-compositions.md.

Root must be sized (silent layout bug)

The standalone root authors width and height: 100%. Canvas size is data-width and data-height; the runtime stamps those pixels onto the composition root. Do not hardcode 1920px or 1080px on #root. Skeleton: references/minimal-composition.md.

One paused timeline

Each composition registers exactly one gsap.timeline({ paused: true }) at window.__timelines["<id>"], keyed by the root data-composition-id. Building it inside an async callback (document.fonts.ready) is supported; what matters is that you register only after the build completes. Render length is the root's data-duration, not the timeline's length: a timeline that runs past it is cut off, and one that ends early holds its last frame. Omit the root data-duration and the length is inferred instead (timeline, media window, or adapter). You do not need window.__timelines = window.__timelines || {}: the runtime creates the registry before your inline scripts run, and lint no longer asks for it. Do not nest sub-timelines into the host by hand; the runtime auto-nests registered child timelines. Full contract, including non-GSAP runtimes: references/determinism-rules.md and the adapters in hyperframes-animation.

First-pass lint gotchas (a guaranteed first build failure)

lint does catch these, but only after the fact. Write them right the first time:

  • Never pair a CSS initial transform with a GSAP tween on the same property: the CSS value and the tween's start fight, and lint rejects it with gsap_css_transform_conflict. Set the initial state inside the tween with gsap.fromTo(el, { x: -40 }, { x: 0 }) instead of a CSS transform: translateX(-40px).
  • Never put crossorigin on <video> or <audio>. lint rejects it unconditionally with media_crossorigin_breaks_preview (error), including for canvas, WebGL and WebAudio readback. There is no suppression.
  • Never give a <video data-start> an ancestor that also carries data-start. lint rejects it with video_nested_in_timed_element (error). Time the wrapper or the video, not both.
  • Every <audio> needs an id. lint rejects it with media_missing_id, and an id-less <audio> is never picked up by the mixer, so the render is silent.
  • Never tween a .clip with autoAlpha or visibility: lint rejects it with gsap_animates_clip_element. Animate a child instead.
  • A named CSS font-family needs an in-file @font-face pointing at a font file shipped in the project, or lint fires font_family_without_font_face. Exempt (CLI 0.8.111): the families the CLI bundles and substitutes itself (Inter, Montserrat, Outfit, Nunito, Oswald, League Gothic, Archivo Black, Space Mono, IBM Plex Mono, JetBrains Mono, EB Garamond, Playfair Display, Source Code Pro, Noto Sans JP, Roboto, Open Sans, Lato, Poppins) and common system names it maps onto them, so Arial or Helvetica renders as Inter and Georgia as EB Garamond. For anything else, ship the file (references/computer-setup.md, section 9).
  • Sub-composition #root uses width and height: 100% (or inset: 0), not hardcoded 1920px and 1080px. Canvas size is data-width and data-height.
  • A root-level clip holding block-level children (a <section class="clip"> with an <h1> inside) warns nested_structure_needs_subcomposition, an error inside Studio. Make the visible element the clip, or move the scene into a sub-composition (references/composition-patterns.md).

A lint error also switches off the layout and contrast audits: check then reports 0 sample(s) and 0/0 text checks, which reads like a clean file but means nothing ran. Clear lint errors before you trust those numbers.

Non-negotiables (silent bugs automated gates may miss)

  • No render-time clocks, unseeded Math.random, network or input state. repeat: -1 only under a finite root data-duration (export clips to it); otherwise use a finite count. See references/determinism-rules.md.
  • Never tween display, visibility or autoAlpha on a .clip element. The framework owns clip visibility, and lint rejects it (gsap_animates_clip_element). Animate a child instead.
  • No <br> in body text; transformed elements must be block-level and sized; pulsing absolute decoratives need clearance at their peak. See references/determinism-rules.md.
  • <video> and <audio> are found by a flat document query, so the framework seeks and decodes them at any nesting depth (including inside a sub-composition <template> or a wrapper). One hard limit: lint errors if a <video data-start> sits inside another plain element that also has data-start, and the failure is real (wrong source frames, then the clip vanishes mid-slot), so put the timing on the wrapper or on the video, never both. Sub-composition hosts are exempt: media inside a sub-composition renders correctly. The other caveat is about timelines, not placement: a sub-composition timeline cannot animate host-root elements. See references/variables-and-media.md.
  • Keep every id unique across the assembled page (prefix sub-composition ids with the composition id, #<id>-hero) so your own #id CSS and getElementById calls resolve. Frame injection no longer depends on it: the compiler stamps a document-unique data-hf-render-id on every video[src], audio[src] and img[src]. Media that uses <source> children instead of a src attribute is not stamped, so unique ids still matter there. See references/composition-patterns.md.
  • A full-screen fill on the composition root is fine on a normal render. It is dropped only on the layered-composite path (HDR content, or a composition using shader transitions), where the engine forces every composition root transparent so the layer beneath shows through. With shader transitions or HDR media, put the fill on a full-bleed child (position: absolute; inset: 0). See references/full-screen-motion.md.

Method

  1. Set up and plan. Run the check in references/computer-setup.md, scaffold the project, and use todo for anything with more than two scenes. Fix the canvas, length and scene list before writing HTML.
  2. Pick the architecture (references/composition-patterns.md). One scene with simple content: monolithic, the visible element as the clip (references/minimal-composition.md). Anything with several scenes or structured scenes: modular, with a thin index.html (scene slots, root-level <audio>, a near-empty root timeline) and one compositions/<scene-id>.html per scene.
  3. Write the root. Standalone form, id="root", data-composition-id, data-width, data-height, data-duration; CSS position: relative; width: 100%; height: 100%; overflow: hidden.
  4. Build the static end state first, in HTML and CSS with flex, grid, padding and max-width (layout contract in references/determinism-rules.md). Animate from and to it later.
  5. Lay out clips in time (references/data-attributes.md, references/tracks-and-clips.md). Each clip gets id, data-start, data-duration (required for div and hosts; images default to 3 s; media to its length), and class="clip" on visible elements. The window is half-open [start, start + duration): back-to-back clips use b.start = a.start + a.duration, and an animation's end state lands slightly before the clip ends. data-track-index is a display lane only; layering is CSS z-index. Relative starts (data-start="intro + 2") need spaces round the operator and fail silently to 0, so snapshot them.
  6. Wire sub-compositions (references/sub-compositions.md): host data-composition-id equals the file's root id and its timeline key; <style> and <script> inside <template>; root styled by #root; ids prefixed with the scene id; entrances with gsap.fromTo. The host's data-duration is the scene's visible window.
  7. Place media (references/variables-and-media.md): a <video> with sound is playsinline data-has-audio="true", a silent one muted playsinline; every media element has an id; no crossorigin; never call play(), pause or seek. Continuous music sits at the root on a high track (10). Volume over time is a data-automation lane, never also a tween. Cuts, trims, speed, freezes and swaps: references/creator-editing-recipes.md.
  8. Declare variables when the owner wants reusable text, colours or images: declarations on <html>, data-var-text, data-var-src and var(--id) bindings, data-variable-values per instance, --variables at render. Variables cannot change the root data-duration.
  9. Animate: one paused timeline per composition, registered at the end of the build, keyed by its composition id. Motion choices come from hyperframes-animation; zooms and camera moves from hyperframes-keyframes.
  10. Validate. npx hyperframes lint until it reports 0 errors and you have fixed or can justify every warning; then npx hyperframes check until it reports 0 findings across lint, runtime, layout, motion and contrast. Then snapshot the middle of every scene and look:
    SNAP=$(mktemp -d); npx hyperframes snapshot --at 1.5,9,15 --describe false -o "$SNAP" && ls "$SNAP"
    ffmpeg -v error -pattern_type glob -i "$SNAP/*.png" -vf "scale=480:-1,tile=3x2" -frames:v 1 "$SNAP/sheet.png"
    
    Run vision_analyze on sheet.png (one contact sheet, not many single frames): styled text, the right scene at each time, nothing blank or cut off. Studio preview (npx hyperframes preview) is your own tool only: start it with terminal(background=true), look with browser_navigate and browser_vision, and never hand the owner a localhost URL.
  11. Review, then render. Send the owner a review package: the key-frame PNGs and, for motion, a draft (npx hyperframes render . --quality draft --output draft.mp4 in terminal(background=true, notify_on_complete=true)), attached in your reply as MEDIA:/absolute/path/to/file, and ask "render the final, or what changes?" with clarify or in the reply. Render --quality delivery only after the owner says yes, or straight away when the owner said "just make it". Render flags and failures: hyperframes-cli.

Editing an existing composition

  • Read the files first. Preserve unrelated timing, tracks, IDs, variables and media paths.
  • To know what is on a project's timeline (tracks, clips, starts, ends, what plays), run npx hyperframes timeline (or --json) instead of reading index.html and every sub-composition file.
  • Match existing composition IDs and timeline keys.
  • Adding a clip: set its data-start and data-duration intentionally against the clips around it. data-track-index is a Studio display lane, not a timing constraint, so it does not need to be free.
  • A clip that ends past the root data-duration is cut off: extend the root data-duration to the clip's end in the same edit (lint warns clip_ends_past_root_duration).
  • data-hidden on any composition element hides it in BOTH preview and render, overriding its time window; it is non-destructive and reversible, and Studio's timeline eye icon toggles it.
  • Adding a sub-composition: verify its internal data-composition-id before wiring the host.
  • When the owner says "this element", identify it from the owner's words, a frame you send, or npx hyperframes timeline --json, and confirm with clarify if it is ambiguous.

Output

  • The project folder: index.html, compositions/*.html, assets/ (with LICENSES.md for any music or SFX), passing lint and check.
  • For review: key frames (PNG) and a draft MP4, attached with MEDIA:/absolute/path, plus the question.
  • The final MP4 attached with MEDIA:/absolute/path/to/final.mp4 (never only a path), with its path, duration, resolution, fps and size from ffprobe -v error -show_entries format=duration,size:stream=width,height,r_frame_rate -of default=nw=1 final.mp4. show_card may carry the summary; copy the file to ~/Desktop/Team when teammates need it.
  • A short note: what you built or changed (clip ids and times), and any lint warning you deliberately left, with the reason.

Checks before you finish

  • lint: 0 errors. check: 0 findings, and its sample counts are not 0 sample(s) or 0/0 text checks.
  • Every window.__timelines key equals its root's data-composition-id; every host id equals its file's root id.
  • The root data-duration covers every clip's end.
  • Every <video> and <audio> has an id; every timed <video> has muted or data-has-audio="true"; none has crossorigin.
  • Projects with sub-compositions or reference-timed clips: snapshots at each scene's midpoint seen with vision_analyze.
  • After a render: read the summary's second line (see Pitfalls); the ffprobe duration matches the root data-duration; a video that should have sound has an audio stream (ffprobe -v error -select_streams a -show_entries stream=codec_name -of csv=p=0 final.mp4 prints a codec).
  • Any preview server you started is stopped, and quality-check frames are in a temporary folder, not the project.

Pitfalls

  • Centring with CSS transform: translate(-50%,-50%) on a node you then move with GSAP x or y. Lint: gsap_css_transform_conflict. Centre with flex, grid or inset, and use fromTo or xPercent and yPercent.
  • Adding a scene-exit tl.set(..., {visibility:"hidden"}). The runtime already hides timed clips. Opacity fades on inner nodes (or opacity on the .clip) are enough. Caption hard-kills are a different rule.
  • A timeline key that differs from the root data-composition-id. window.__timelines["id"] must match it; with two or more timelines a mismatch freezes the render at t = 0.
  • Not reading the render summary. After render, read the summary's second line: beginframe or screenshot capture, GPU mode, stage timings. screenshot with software gpu on Linux is the slow path, which is what a computer without a GPU gets: budget the time and render in the background.
  • Trusting check while lint has an error. The audits did not run.
  • <style> in the <head> of a sub-composition. It is dropped; the scene renders as unstyled text in the top-left.
  • Ending an animation exactly at data-duration. That frame is never shown; land it slightly before.
  • data-start="intro-0.5". Without spaces it is an id lookup that silently resolves to 0.
  • A volume tween and a volume lane on one track. The lane wins and the tween is ignored (audio_volume_double_automation).
  • Naming a font you have not shipped, or expecting Arial to look like Arial. Ship the file or pick a bundled family.
  • Handing the owner a localhost URL or a bare path. The owner cannot open either; attach the file with MEDIA:.

Versions

v1.0.0currentOct 6, 2026

Listed from the source repository.

Reviews

No reviews yet. Be the first.

Write a review