> ## Documentation Index
> Fetch the complete documentation index at: https://hyperframes-canary-calibration-notes.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Examples

> Watch finished HyperFrames videos, inspect real production projects, or start from a working template.

export const HoverVideo = ({src, poster, className, hasAudio = true}) => {
  const videoRef = useRef(null);
  const wrapRef = useRef(null);
  const [muted, setMuted] = useState(true);
  const [playing, setPlaying] = useState(false);
  const [inView, setInView] = useState(false);
  const [reduced, setReduced] = useState(() => typeof window !== "undefined" && typeof window.matchMedia === "function" && window.matchMedia("(prefers-reduced-motion: reduce)").matches);
  useEffect(() => {
    const query = window.matchMedia("(prefers-reduced-motion: reduce)");
    const onChange = () => setReduced(query.matches);
    query.addEventListener("change", onChange);
    return () => query.removeEventListener("change", onChange);
  }, []);
  useEffect(() => {
    const el = wrapRef.current;
    if (!el || typeof IntersectionObserver !== "function") {
      setInView(true);
      return;
    }
    const observer = new IntersectionObserver(entries => setInView(entries[0]?.isIntersecting ?? false), {
      rootMargin: "200px"
    });
    observer.observe(el);
    return () => observer.disconnect();
  }, []);
  useEffect(() => {
    const video = videoRef.current;
    if (!video) return;
    if (!inView) {
      video.pause();
      video.removeAttribute("src");
      video.load();
      setMuted(true);
      setPlaying(false);
      return;
    }
    if (reduced && video.muted) video.pause();
  }, [inView, reduced]);
  const start = () => {
    const video = videoRef.current;
    if (video) video.play().catch(() => {});
  };
  const toggleSound = () => {
    const video = videoRef.current;
    if (!video) return;
    const next = !muted;
    setMuted(next);
    video.muted = next;
    if (!next) start();
  };
  const togglePlay = () => {
    const video = videoRef.current;
    if (!video) return;
    if (video.paused) start(); else video.pause();
  };
  const autoplaying = inView && !reduced;
  const icon = paths => <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
      {paths}
    </svg>;
  return <div ref={wrapRef} className={`relative overflow-hidden ${className ?? ""}`}>
      <video ref={videoRef} className="absolute inset-0 h-full w-full object-cover" src={inView ? src : undefined} poster={poster} autoPlay={autoplaying} muted={muted} loop={autoplaying} playsInline preload="none" onPlay={() => setPlaying(true)} onPause={() => setPlaying(false)} onMouseEnter={() => {
    if (hasAudio && !reduced && muted) toggleSound();
  }} onMouseLeave={() => {
    if (hasAudio && !muted) toggleSound();
  }} />
      <button type="button" onClick={e => {
    e.preventDefault();
    e.stopPropagation();
    (hasAudio ? toggleSound : togglePlay)();
  }} aria-pressed={hasAudio ? !muted : playing} aria-label={hasAudio ? muted ? reduced ? "Play this preview with sound" : "Unmute this preview" : "Mute this preview" : playing ? "Pause this preview" : "Play this preview"} className="absolute left-2 top-2 z-10 inline-flex items-center gap-1 rounded-full bg-black/60 px-2.5 py-1 text-xs font-medium text-white backdrop-blur transition hover:bg-black/75 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-white">
        {hasAudio ? muted ? icon(<><path d="M11 5 6 9H2v6h4l5 4V5z" /><line x1="23" y1="9" x2="17" y2="15" /><line x1="17" y1="9" x2="23" y2="15" /></>) : icon(<><path d="M11 5 6 9H2v6h4l5 4V5z" /><path d="M15.5 8.5a5 5 0 0 1 0 7" /><path d="M18.5 5.5a9 9 0 0 1 0 13" /></>) : playing ? icon(<><rect x="6" y="4" width="4" height="16" /><rect x="14" y="4" width="4" height="16" /></>) : icon(<polygon points="5 3 19 12 5 21 5 3" />)}
        {hasAudio ? muted ? "Sound" : "On" : playing ? "Pause" : "Play"}
      </button>
    </div>;
};

export const ReplicaCompare = ({title, meta, refSrc, refPoster, replicaSrc, replicaPoster}) => {
  const wrapRef = useRef(null);
  const refVideo = useRef(null);
  const repVideo = useRef(null);
  const [muted, setMuted] = useState(true);
  const [inView, setInView] = useState(false);
  useEffect(() => {
    const el = wrapRef.current;
    if (!el || typeof IntersectionObserver !== "function") {
      setInView(true);
      return;
    }
    const observer = new IntersectionObserver(entries => setInView(entries[0]?.isIntersecting ?? false), {
      rootMargin: "200px"
    });
    observer.observe(el);
    return () => observer.disconnect();
  }, []);
  const [reduced, setReduced] = useState(() => typeof window !== "undefined" && typeof window.matchMedia === "function" && window.matchMedia("(prefers-reduced-motion: reduce)").matches);
  useEffect(() => {
    const query = window.matchMedia("(prefers-reduced-motion: reduce)");
    const onChange = () => setReduced(query.matches);
    query.addEventListener("change", onChange);
    return () => query.removeEventListener("change", onChange);
  }, []);
  useEffect(() => {
    const videos = [refVideo.current, repVideo.current];
    if (!inView) {
      for (const video of videos) {
        if (!video) continue;
        video.pause();
        video.muted = true;
        video.removeAttribute("src");
        video.load();
      }
      setMuted(true);
      return;
    }
    if (reduced) {
      for (const video of videos) if (video && video.muted) video.pause();
    }
  }, [reduced, inView]);
  useEffect(() => {
    const a = refVideo.current;
    const b = repVideo.current;
    if (!a || !b || !inView) return;
    const resync = () => {
      if (Number.isFinite(a.currentTime) && Math.abs((b.currentTime || 0) - a.currentTime) > 0.15) {
        try {
          b.currentTime = a.currentTime;
        } catch {}
      }
      if (a.paused && !b.paused) b.pause();
      if (!a.paused && b.paused) b.play().catch(() => {});
    };
    const onPlay = () => b.play().catch(() => {});
    const onPause = () => b.pause();
    a.addEventListener("timeupdate", resync);
    a.addEventListener("seeked", resync);
    a.addEventListener("play", onPlay);
    a.addEventListener("pause", onPause);
    return () => {
      a.removeEventListener("timeupdate", resync);
      a.removeEventListener("seeked", resync);
      a.removeEventListener("play", onPlay);
      a.removeEventListener("pause", onPause);
    };
  }, [reduced, inView]);
  const startBoth = () => {
    refVideo.current?.play().catch(() => {});
    repVideo.current?.play().catch(() => {});
  };
  const pauseBoth = () => {
    refVideo.current?.pause();
    repVideo.current?.pause();
  };
  const toggleSound = () => {
    const a = refVideo.current;
    if (!a) return;
    const next = !muted;
    setMuted(next);
    a.muted = next;
    if (!next) {
      startBoth();
    } else if (reduced) {
      pauseBoth();
    }
  };
  const active = inView && !reduced;
  const card = "overflow-hidden rounded-xl border border-zinc-200 bg-white dark:border-zinc-800 dark:bg-zinc-950";
  const video = "aspect-video w-full object-cover bg-zinc-100 dark:bg-zinc-900";
  const cap = "text-xs font-semibold uppercase tracking-wide";
  return <div ref={wrapRef}>
      <div className="mb-3 flex items-baseline justify-between">
        <strong className="text-sm">{title}</strong>
        <span className="text-xs text-zinc-500 dark:text-zinc-400">{meta}</span>
      </div>
      <div className="grid grid-cols-1 gap-4 md:grid-cols-2">
        <div className={`relative ${card}`}>
          <video ref={refVideo} className={video} src={inView ? refSrc : undefined} poster={refPoster} autoPlay={active} muted={muted} loop={active} playsInline preload="none" />
          <button type="button" onClick={toggleSound} aria-pressed={!muted} aria-label={muted ? reduced ? "Sound off — play the comparison with sound" : "Sound off — unmute the reference" : reduced ? "Sound on — pause the comparison" : "Sound on — mute the reference"} className="absolute left-3 top-3 z-10 flex items-center gap-1.5 rounded-full bg-black/60 px-3 py-1.5 text-xs font-medium text-white backdrop-blur transition hover:bg-black/75 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-white">
            {muted ? <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M11 5 6 9H2v6h4l5 4V5z" /><line x1="23" y1="9" x2="17" y2="15" /><line x1="17" y1="9" x2="23" y2="15" /></svg> : <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M11 5 6 9H2v6h4l5 4V5z" /><path d="M15.5 8.5a5 5 0 0 1 0 7" /><path d="M18.5 5.5a9 9 0 0 1 0 13" /></svg>}
            {muted ? "Sound off" : "Sound on"}
          </button>
          <div className="p-3">
            <span className={`${cap} text-zinc-500 dark:text-zinc-400`}>Reference — original film</span>
          </div>
        </div>
        <div className={card}>
          <video ref={repVideo} className={video} src={inView ? replicaSrc : undefined} poster={replicaPoster} autoPlay={active} muted loop={active} playsInline preload="none" />
          <div className="p-3">
            <span className={`${cap} text-zinc-700 dark:text-zinc-300`}>HyperFrames replica</span>
          </div>
        </div>
      </div>
    </div>;
};

export const DocsVideo = ({src, poster, title, autoPlay = false, loop = false, portrait = false}) => {
  const videoRef = useRef(null);
  const playerRef = useRef(null);
  const hideTimerRef = useRef(null);
  const progressFrameRef = useRef(null);
  const [enhanced, setEnhanced] = useState(false);
  const [playing, setPlaying] = useState(false);
  const [waiting, setWaiting] = useState(false);
  const [muted, setMuted] = useState(false);
  const [currentTime, setCurrentTime] = useState(0);
  const [duration, setDuration] = useState(0);
  const [playbackRate, setPlaybackRate] = useState(1);
  const [controlsVisible, setControlsVisible] = useState(false);
  const [fullscreen, setFullscreen] = useState(false);
  const [fullscreenSupported, setFullscreenSupported] = useState(false);
  const [previewing, setPreviewing] = useState(false);
  const [scrubbing, setScrubbing] = useState(false);
  const [previewTime, setPreviewTime] = useState(0);
  const [previewPosition, setPreviewPosition] = useState(0);
  const formatTime = seconds => {
    if (!Number.isFinite(seconds) || seconds < 0) return "0:00";
    const minutes = Math.floor(seconds / 60);
    const remaining = Math.floor(seconds % 60);
    return `${minutes}:${String(remaining).padStart(2, "0")}`;
  };
  const clearHideTimer = () => {
    if (hideTimerRef.current) {
      window.clearTimeout(hideTimerRef.current);
      hideTimerRef.current = null;
    }
  };
  const revealControls = () => {
    setControlsVisible(true);
    clearHideTimer();
    hideTimerRef.current = window.setTimeout(() => setControlsVisible(false), 2200);
  };
  const togglePlayback = async () => {
    const video = videoRef.current;
    if (!video) return;
    if (video.paused || video.ended) {
      if (video.ended) video.currentTime = 0;
      setWaiting(true);
      try {
        await video.play();
      } catch {
        setWaiting(false);
        setPlaying(false);
      }
    } else {
      video.pause();
      setControlsVisible(true);
    }
  };
  const toggleMute = () => {
    const video = videoRef.current;
    if (!video) return;
    if (video.muted && video.volume === 0) video.volume = 0.8;
    video.muted = !video.muted;
    setMuted(video.muted);
  };
  const seek = event => {
    const video = videoRef.current;
    if (!video) return;
    const nextTime = Number(event.target.value);
    video.currentTime = nextTime;
    setCurrentTime(nextTime);
  };
  const updateScrubPreview = (event, seekMainVideo = false) => {
    if (!duration) return;
    const rect = event.currentTarget.getBoundingClientRect();
    const ratio = Math.min(1, Math.max(0, (event.clientX - rect.left) / rect.width));
    const nextTime = ratio * duration;
    setPreviewing(true);
    setPreviewTime(nextTime);
    setPreviewPosition(ratio * 100);
    if (seekMainVideo) {
      const video = videoRef.current;
      if (video) {
        video.currentTime = nextTime;
        setCurrentTime(nextTime);
      }
    }
  };
  const cyclePlaybackRate = () => {
    const video = videoRef.current;
    if (!video) return;
    const rates = [1, 1.25, 1.5, 2];
    const currentIndex = rates.indexOf(video.playbackRate);
    const nextRate = rates[(currentIndex + 1) % rates.length];
    video.playbackRate = nextRate;
    setPlaybackRate(nextRate);
  };
  const toggleFullscreen = async () => {
    const player = playerRef.current;
    const video = videoRef.current;
    if (!player || typeof document === "undefined") return;
    try {
      if (document.fullscreenElement) {
        await document.exitFullscreen();
      } else if (player.requestFullscreen) {
        await player.requestFullscreen();
      } else if (video?.webkitEnterFullscreen) {
        video.webkitEnterFullscreen();
      }
    } catch {}
  };
  const handleKeyboard = event => {
    if (event.target !== event.currentTarget) return;
    const video = videoRef.current;
    if (!video) return;
    if (event.key === " " || event.key === "Enter") {
      event.preventDefault();
      togglePlayback();
    } else if (event.key === "ArrowLeft") {
      event.preventDefault();
      video.currentTime = Math.max(0, video.currentTime - 5);
    } else if (event.key === "ArrowRight") {
      event.preventDefault();
      video.currentTime = Math.min(duration || video.duration || 0, video.currentTime + 5);
    } else if (event.key.toLowerCase() === "m") {
      event.preventDefault();
      toggleMute();
    } else if (event.key.toLowerCase() === "f") {
      event.preventDefault();
      toggleFullscreen();
    }
  };
  useEffect(() => {
    setEnhanced(true);
    setFullscreenSupported(Boolean(playerRef.current?.requestFullscreen || videoRef.current?.webkitEnterFullscreen));
    return () => {
      clearHideTimer();
    };
  }, []);
  useEffect(() => {
    if (typeof document === "undefined") return undefined;
    const syncFullscreen = () => setFullscreen(document.fullscreenElement === playerRef.current);
    document.addEventListener("fullscreenchange", syncFullscreen);
    return () => document.removeEventListener("fullscreenchange", syncFullscreen);
  }, []);
  useEffect(() => {
    clearHideTimer();
    if (!playing) return undefined;
    hideTimerRef.current = window.setTimeout(() => setControlsVisible(false), 2200);
    return clearHideTimer;
  }, [playing]);
  useEffect(() => {
    if (!playing) return undefined;
    const updateProgress = () => {
      const video = videoRef.current;
      if (video && !video.paused) setCurrentTime(video.currentTime);
      progressFrameRef.current = window.requestAnimationFrame(updateProgress);
    };
    progressFrameRef.current = window.requestAnimationFrame(updateProgress);
    return () => {
      if (progressFrameRef.current) window.cancelAnimationFrame(progressFrameRef.current);
      progressFrameRef.current = null;
    };
  }, [playing]);
  const progress = duration > 0 ? currentTime / duration * 100 : 0;
  const replaying = duration > 0 && currentTime >= duration - 0.15;
  return <div className="hf-docs-video-block" data-portrait={portrait ? "true" : "false"}>
      <div ref={playerRef} className="hf-docs-video" role="region" aria-label={title} tabIndex={0} onKeyDown={handleKeyboard} onPointerMove={revealControls} onPointerLeave={() => setControlsVisible(false)} onFocus={revealControls} onBlur={event => {
    if (!event.currentTarget.contains(event.relatedTarget)) setControlsVisible(false);
  }}>
        <video ref={videoRef} aria-label={title} src={src} poster={poster} autoPlay={autoPlay} loop={loop} playsInline preload="metadata" controls={!enhanced} onClick={togglePlayback} onDoubleClick={toggleFullscreen} onLoadedMetadata={event => {
    const nextDuration = event.currentTarget.duration || 0;
    setDuration(nextDuration);
    setMuted(event.currentTarget.muted);
  }} onDurationChange={event => setDuration(event.currentTarget.duration || 0)} onTimeUpdate={event => setCurrentTime(event.currentTarget.currentTime)} onPlay={() => setPlaying(true)} onPause={() => setPlaying(false)} onPlaying={() => setWaiting(false)} onWaiting={() => setWaiting(true)} onCanPlay={() => setWaiting(false)} onEnded={() => {
    setPlaying(false);
    setControlsVisible(true);
  }} onVolumeChange={event => setMuted(event.currentTarget.muted)} />

        {enhanced && <>
            {!playing && (currentTime <= 0.2 || replaying) && <button type="button" className="hf-docs-video-hero-play" onClick={togglePlayback} aria-label={replaying ? "Replay video" : "Play video"}>
                <span className="hf-docs-video-hero-icon" aria-hidden="true">
                  <svg viewBox="0 0 24 24">
                    <path d="M8 5.5v13l10-6.5z" />
                  </svg>
                </span>
              </button>}

            {waiting && playing && <span className="hf-docs-video-spinner" aria-label="Loading" />}

            <div className="hf-docs-video-controls" data-visible={controlsVisible ? "true" : "false"}>
              <div className="hf-docs-video-scrub-preview" data-visible={previewing ? "true" : "false"} style={{
    "--hf-video-preview-x": `${previewPosition}%`
  }} aria-hidden="true">
                <span>{formatTime(previewTime)}</span>
              </div>

              <input className="hf-docs-video-progress" type="range" min="0" max={duration || 0} step="0.01" value={Math.min(currentTime, duration || 0)} aria-label="Video progress" aria-valuetext={`${formatTime(currentTime)} of ${formatTime(duration)}`} onChange={seek} onPointerEnter={updateScrubPreview} onPointerMove={event => updateScrubPreview(event, scrubbing || event.buttons === 1)} onPointerDown={event => {
    setScrubbing(true);
    event.currentTarget.setPointerCapture?.(event.pointerId);
    updateScrubPreview(event, true);
  }} onPointerUp={event => {
    setScrubbing(false);
    if (event.pointerType !== "mouse") setPreviewing(false);
  }} onPointerCancel={() => {
    setScrubbing(false);
    setPreviewing(false);
  }} onPointerLeave={() => {
    if (!scrubbing) setPreviewing(false);
  }} style={{
    "--hf-video-progress": `${progress}%`
  }} />

              <div className="hf-docs-video-control-row">
                <button type="button" className="hf-docs-video-control" onClick={togglePlayback} aria-label={playing ? "Pause video" : "Play video"}>
                  {playing ? <svg viewBox="0 0 24 24" aria-hidden="true">
                      <path d="M7 5h4v14H7zm6 0h4v14h-4z" />
                    </svg> : <svg viewBox="0 0 24 24" aria-hidden="true">
                      <path d="M8 5.5v13l10-6.5z" />
                    </svg>}
                </button>

                <button type="button" className="hf-docs-video-control" onClick={toggleMute} aria-label={muted ? "Unmute video" : "Mute video"}>
                  {muted ? <svg viewBox="0 0 24 24" aria-hidden="true">
                      <path d="M4 9v6h4l5 4V5L8 9zm11.5 1.1 1.4-1.4 1.6 1.6 1.6-1.6 1.4 1.4-1.6 1.6 1.6 1.6-1.4 1.4-1.6-1.6-1.6 1.6-1.4-1.4 1.6-1.6z" />
                    </svg> : <svg viewBox="0 0 24 24" aria-hidden="true">
                      <path d="M4 9v6h4l5 4V5L8 9zm11 1.2v3.6c1-.5 1.7-1.5 1.7-2.8S16 10.7 15 10.2zm0-4v2.1c2.2.6 3.7 2.5 3.7 4.7s-1.5 4.1-3.7 4.7v2.1c3.3-.7 5.7-3.5 5.7-6.8S18.3 6.9 15 6.2z" />
                    </svg>}
                </button>

                <span className="hf-docs-video-time" aria-hidden="true">
                  {formatTime(currentTime)} <span>/</span> {formatTime(duration)}
                </span>

                <span className="hf-docs-video-spacer" />

                <button type="button" className="hf-docs-video-rate" onClick={cyclePlaybackRate} aria-label={`Playback speed ${playbackRate} times`}>
                  {playbackRate}×
                </button>

                {fullscreenSupported && <button type="button" className="hf-docs-video-control" onClick={toggleFullscreen} aria-label={fullscreen ? "Exit fullscreen" : "Enter fullscreen"}>
                    {fullscreen ? <svg viewBox="0 0 24 24" aria-hidden="true">
                        <path d="M8 3H6v3H3v2h5zm8 0v5h5V6h-3V3zM3 16v2h3v3h2v-5zm13 0v5h2v-3h3v-2z" />
                      </svg> : <svg viewBox="0 0 24 24" aria-hidden="true">
                        <path d="M3 8h2V5h3V3H3zm13-5v2h3v3h2V3zM5 16H3v5h5v-2H5zm14 3h-3v2h5v-5h-2z" />
                      </svg>}
                  </button>}
              </div>
            </div>
          </>}
      </div>

    </div>;
};

## What HyperFrames can make

Every film below is 100% HyperFrames — HTML rendered frame by frame, no other tool. They are grouped by what they show off. Where a source project is public, the card links to it.

### Product & launch films

A whole product cut into a launch film — its own screens, type and colour.

<div className="not-prose grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4 my-6">
  <div className="group overflow-hidden rounded-xl border border-zinc-200 bg-white text-inherit no-underline dark:border-zinc-800 dark:bg-zinc-950">
    <HoverVideo className="aspect-video w-full object-cover bg-zinc-100 dark:bg-zinc-900" src="https://static.heygen.ai/hyperframes-oss/docs/images/showcase/launch-HF-heygen-stripe-v1-s.mp4" poster="https://static.heygen.ai/hyperframes-oss/docs/images/showcase/launch-HF-heygen-stripe-v1.jpg" />

    <div className="p-4"><strong className="block text-sm">HeyGen × Stripe</strong><span className="mt-1 block text-xs text-zinc-500 dark:text-zinc-400">Product launch, 4K · 100% HyperFrames</span></div>
  </div>

  <a className="group overflow-hidden rounded-xl border border-zinc-200 bg-white text-inherit no-underline dark:border-zinc-800 dark:bg-zinc-950" href="https://github.com/heygen-com/hyperframes-launches/tree/main/hyperframes-launch">
    <HoverVideo className="aspect-video w-full object-cover bg-zinc-100 dark:bg-zinc-900" src="https://static.heygen.ai/hyperframes-oss/docs/images/showcase/launch-hyperframes-launch-v1-s.mp4" poster="https://static.heygen.ai/hyperframes-oss/docs/images/showcase/launch-hyperframes-launch-v1.jpg" />

    <div className="p-4"><strong className="block text-sm">HyperFrames launch</strong><span className="mt-1 block text-xs text-zinc-500 dark:text-zinc-400">The framework's own launch · source included</span></div>
  </a>

  <a className="group overflow-hidden rounded-xl border border-zinc-200 bg-white text-inherit no-underline dark:border-zinc-800 dark:bg-zinc-950" href="https://github.com/heygen-com/hyperframes-launches/tree/main/website-to-hyperframes">
    <HoverVideo className="aspect-video w-full object-cover bg-zinc-100 dark:bg-zinc-900" src="https://static.heygen.ai/hyperframes-oss/docs/images/showcase/launch-website-to-hyperframes-v1-s.mp4" poster="https://static.heygen.ai/hyperframes-oss/docs/images/showcase/launch-website-to-hyperframes-v1.jpg" />

    <div className="p-4"><strong className="block text-sm">Website → video</strong><span className="mt-1 block text-xs text-zinc-500 dark:text-zinc-400">A live site becomes a promo · source included</span></div>
  </a>

  <a className="group overflow-hidden rounded-xl border border-zinc-200 bg-white text-inherit no-underline dark:border-zinc-800 dark:bg-zinc-950" href="https://github.com/heygen-com/hyperframes-launches/tree/main/spacex-launch">
    <HoverVideo className="aspect-video w-full object-cover bg-zinc-100 dark:bg-zinc-900" src="https://static.heygen.ai/hyperframes-oss/docs/images/showcase/launch-spacex-launch-v1-s.mp4" poster="https://static.heygen.ai/hyperframes-oss/docs/images/showcase/launch-spacex-launch-v1.jpg" />

    <div className="p-4"><strong className="block text-sm">SpaceX explainer</strong><span className="mt-1 block text-xs text-zinc-500 dark:text-zinc-400">Data and motion at scale · source included</span></div>
  </a>

  <div className="group overflow-hidden rounded-xl border border-zinc-200 bg-white text-inherit no-underline dark:border-zinc-800 dark:bg-zinc-950">
    <HoverVideo className="aspect-video w-full object-cover bg-zinc-100 dark:bg-zinc-900" src="https://static.heygen.ai/hyperframes-oss/docs/images/showcase/launch-k3-promo-v1-s.mp4" poster="https://static.heygen.ai/hyperframes-oss/docs/images/showcase/launch-k3-promo-v1.jpg" />

    <div className="p-4"><strong className="block text-sm">K3 promo</strong><span className="mt-1 block text-xs text-zinc-500 dark:text-zinc-400">Short product spot · 100% HyperFrames</span></div>
  </div>
</div>

### The tooling, shown working

Each film is HyperFrames demonstrating one of its own features.

<div className="not-prose grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4 my-6">
  <div className="group overflow-hidden rounded-xl border border-zinc-200 bg-white text-inherit no-underline dark:border-zinc-800 dark:bg-zinc-950">
    <HoverVideo className="aspect-video w-full object-cover bg-zinc-100 dark:bg-zinc-900" src="https://static.heygen.ai/hyperframes-oss/docs/images/showcase/launch-skills-launch-video-v1-s.mp4" poster="https://static.heygen.ai/hyperframes-oss/docs/images/showcase/launch-skills-launch-video-v1.jpg" />

    <div className="p-4"><strong className="block text-sm">Agent skills</strong><span className="mt-1 block text-xs text-zinc-500 dark:text-zinc-400">The skill system · 100% HyperFrames</span></div>
  </div>

  <div className="group overflow-hidden rounded-xl border border-zinc-200 bg-white text-inherit no-underline dark:border-zinc-800 dark:bg-zinc-950">
    <HoverVideo className="aspect-video w-full object-cover bg-zinc-100 dark:bg-zinc-900" src="https://static.heygen.ai/hyperframes-oss/docs/images/showcase/launch-keyframes-launch-v1-s.mp4" poster="https://static.heygen.ai/hyperframes-oss/docs/images/showcase/launch-keyframes-launch-v1.jpg" />

    <div className="p-4"><strong className="block text-sm">Keyframes</strong><span className="mt-1 block text-xs text-zinc-500 dark:text-zinc-400">Seek-safe animation · 100% HyperFrames</span></div>
  </div>

  <a className="group overflow-hidden rounded-xl border border-zinc-200 bg-white text-inherit no-underline dark:border-zinc-800 dark:bg-zinc-950" href="https://github.com/heygen-com/hyperframes-launches/tree/main/timeline-launch">
    <HoverVideo className="aspect-video w-full object-cover bg-zinc-100 dark:bg-zinc-900" src="https://static.heygen.ai/hyperframes-oss/docs/images/showcase/launch-timeline-launch-v1-s.mp4" poster="https://static.heygen.ai/hyperframes-oss/docs/images/showcase/launch-timeline-launch-v1.jpg" />

    <div className="p-4"><strong className="block text-sm">Timeline editor</strong><span className="mt-1 block text-xs text-zinc-500 dark:text-zinc-400">Studio timeline · source included</span></div>
  </a>

  <a className="group overflow-hidden rounded-xl border border-zinc-200 bg-white text-inherit no-underline dark:border-zinc-800 dark:bg-zinc-950" href="https://github.com/heygen-com/hyperframes-launches/tree/main/inspector-launch">
    <HoverVideo className="aspect-video w-full object-cover bg-zinc-100 dark:bg-zinc-900" src="https://static.heygen.ai/hyperframes-oss/docs/images/showcase/launch-inspector-launch-v1-s.mp4" poster="https://static.heygen.ai/hyperframes-oss/docs/images/showcase/launch-inspector-launch-v1.jpg" />

    <div className="p-4"><strong className="block text-sm">Studio inspector</strong><span className="mt-1 block text-xs text-zinc-500 dark:text-zinc-400">Editing on the canvas · source included</span></div>
  </a>

  <a className="group overflow-hidden rounded-xl border border-zinc-200 bg-white text-inherit no-underline dark:border-zinc-800 dark:bg-zinc-950" href="https://github.com/heygen-com/hyperframes-launches/tree/main/variables-launch">
    <HoverVideo className="aspect-video w-full object-cover bg-zinc-100 dark:bg-zinc-900" src="https://static.heygen.ai/hyperframes-oss/docs/images/showcase/launch-variables-launch-v1-s.mp4" poster="https://static.heygen.ai/hyperframes-oss/docs/images/showcase/launch-variables-launch-v1.jpg" />

    <div className="p-4"><strong className="block text-sm">Variables</strong><span className="mt-1 block text-xs text-zinc-500 dark:text-zinc-400">One composition, many versions · source included</span></div>
  </a>

  <a className="group overflow-hidden rounded-xl border border-zinc-200 bg-white text-inherit no-underline dark:border-zinc-800 dark:bg-zinc-950" href="https://github.com/heygen-com/hyperframes-launches/tree/main/cloud-render-launch">
    <HoverVideo className="aspect-video w-full object-cover bg-zinc-100 dark:bg-zinc-900" src="https://static.heygen.ai/hyperframes-oss/docs/images/showcase/launch-cloud-render-launch-v1-s.mp4" poster="https://static.heygen.ai/hyperframes-oss/docs/images/showcase/launch-cloud-render-launch-v1.jpg" />

    <div className="p-4"><strong className="block text-sm">Cloud rendering</strong><span className="mt-1 block text-xs text-zinc-500 dark:text-zinc-400">Render on HeyGen's cloud · source included</span></div>
  </a>
</div>

### Motion, sound & effects

Motion is the message — kinetic type, beat-synced cuts, texture and VFX.

<div className="not-prose grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4 my-6">
  <div className="group overflow-hidden rounded-xl border border-zinc-200 bg-white text-inherit no-underline dark:border-zinc-800 dark:bg-zinc-950">
    <HoverVideo className="aspect-video w-full object-cover bg-zinc-100 dark:bg-zinc-900" src="https://static.heygen.ai/hyperframes-oss/docs/images/showcase/launch-texture-launch-video-v1-s.mp4" poster="https://static.heygen.ai/hyperframes-oss/docs/images/showcase/launch-texture-launch-video-v1.jpg" />

    <div className="p-4"><strong className="block text-sm">Texture launch</strong><span className="mt-1 block text-xs text-zinc-500 dark:text-zinc-400">Motion design · 100% HyperFrames</span></div>
  </div>

  <div className="group overflow-hidden rounded-xl border border-zinc-200 bg-white text-inherit no-underline dark:border-zinc-800 dark:bg-zinc-950">
    <HoverVideo className="aspect-video w-full object-cover bg-zinc-100 dark:bg-zinc-900" src="https://static.heygen.ai/hyperframes-oss/docs/images/showcase/launch-vfx-heygen-combined-v1-s.mp4" poster="https://static.heygen.ai/hyperframes-oss/docs/images/showcase/launch-vfx-heygen-combined-v1.jpg" />

    <div className="p-4"><strong className="block text-sm">VFX reel</strong><span className="mt-1 block text-xs text-zinc-500 dark:text-zinc-400">Shaders and effects · 100% HyperFrames</span></div>
  </div>

  <div className="group overflow-hidden rounded-xl border border-zinc-200 bg-white text-inherit no-underline dark:border-zinc-800 dark:bg-zinc-950">
    <HoverVideo className="aspect-video w-full object-cover bg-zinc-100 dark:bg-zinc-900" src="https://static.heygen.ai/hyperframes-oss/docs/images/showcase/launch-music-to-video-launch-v1-s.mp4" poster="https://static.heygen.ai/hyperframes-oss/docs/images/showcase/launch-music-to-video-launch-v1.jpg" />

    <div className="p-4"><strong className="block text-sm">Music to video</strong><span className="mt-1 block text-xs text-zinc-500 dark:text-zinc-400">Cuts synced to a track · 100% HyperFrames</span></div>
  </div>

  <a className="group overflow-hidden rounded-xl border border-zinc-200 bg-white text-inherit no-underline dark:border-zinc-800 dark:bg-zinc-950" href="https://github.com/heygen-com/hyperframes-launches/tree/main/sfx-music-launch">
    <HoverVideo className="aspect-video w-full object-cover bg-zinc-100 dark:bg-zinc-900" src="https://static.heygen.ai/hyperframes-oss/docs/images/showcase/launch-sfx-music-launch-v1-s.mp4" poster="https://static.heygen.ai/hyperframes-oss/docs/images/showcase/launch-sfx-music-launch-v1.jpg" />

    <div className="p-4"><strong className="block text-sm">Sound & music</strong><span className="mt-1 block text-xs text-zinc-500 dark:text-zinc-400">SFX and music with the CLI · source included</span></div>
  </a>
</div>

### Workflows & integrations

A PR, a Figma file, a design brief — turned into a finished film.

<div className="not-prose grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4 my-6">
  <a className="group overflow-hidden rounded-xl border border-zinc-200 bg-white text-inherit no-underline dark:border-zinc-800 dark:bg-zinc-950" href="https://github.com/heygen-com/hyperframes-launches/tree/main/pr-to-video-launch">
    <HoverVideo className="aspect-video w-full object-cover bg-zinc-100 dark:bg-zinc-900" src="https://static.heygen.ai/hyperframes-oss/docs/images/showcase/launch-pr-to-video-launch-v1-s.mp4" poster="https://static.heygen.ai/hyperframes-oss/docs/images/showcase/launch-pr-to-video-launch-v1.jpg" />

    <div className="p-4"><strong className="block text-sm">PR to video</strong><span className="mt-1 block text-xs text-zinc-500 dark:text-zinc-400">A GitHub PR as a reveal · source included</span></div>
  </a>

  <a className="group overflow-hidden rounded-xl border border-zinc-200 bg-white text-inherit no-underline dark:border-zinc-800 dark:bg-zinc-950" href="https://github.com/heygen-com/hyperframes-launches/tree/main/figma-launch">
    <HoverVideo className="aspect-video w-full object-cover bg-zinc-100 dark:bg-zinc-900" src="https://static.heygen.ai/hyperframes-oss/docs/images/showcase/launch-figma-launch-v1-s.mp4" poster="https://static.heygen.ai/hyperframes-oss/docs/images/showcase/launch-figma-launch-v1.jpg" />

    <div className="p-4"><strong className="block text-sm">Figma → HyperFrames</strong><span className="mt-1 block text-xs text-zinc-500 dark:text-zinc-400">A design brought to motion · source included</span></div>
  </a>

  <div className="group overflow-hidden rounded-xl border border-zinc-200 bg-white text-inherit no-underline dark:border-zinc-800 dark:bg-zinc-950">
    <HoverVideo className="aspect-video w-full object-cover bg-zinc-100 dark:bg-zinc-900" src="https://static.heygen.ai/hyperframes-oss/docs/images/showcase/launch-claude-design-hyperframes-video-v1-s.mp4" poster="https://static.heygen.ai/hyperframes-oss/docs/images/showcase/launch-claude-design-hyperframes-video-v1.jpg" />

    <div className="p-4"><strong className="block text-sm">Claude Design</strong><span className="mt-1 block text-xs text-zinc-500 dark:text-zinc-400">From a design draft · 100% HyperFrames</span></div>
  </div>

  <a className="group overflow-hidden rounded-xl border border-zinc-200 bg-white text-inherit no-underline dark:border-zinc-800 dark:bg-zinc-950" href="https://github.com/heygen-com/hyperframes-launches/tree/main/frame-md-launch-storyboard">
    <HoverVideo className="aspect-video w-full object-cover bg-zinc-100 dark:bg-zinc-900" src="https://static.heygen.ai/hyperframes-oss/docs/images/showcase/launch-frame-md-launch-storyboard-v1-s.mp4" poster="https://static.heygen.ai/hyperframes-oss/docs/images/showcase/launch-frame-md-launch-storyboard-v1.jpg" />

    <div className="p-4"><strong className="block text-sm">FRAME.md storyboard</strong><span className="mt-1 block text-xs text-zinc-500 dark:text-zinc-400">Brief-driven direction · source included</span></div>
  </a>
</div>

### Where HyperFrames plugs in

The same engine, reached from the tools teams already use. Hover any card to hear it.

<div className="not-prose grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4 my-6">
  <div className="group overflow-hidden rounded-xl border border-zinc-200 bg-white text-inherit no-underline dark:border-zinc-800 dark:bg-zinc-950">
    <HoverVideo className="aspect-video w-full object-cover bg-zinc-100 dark:bg-zinc-900" src="https://static.heygen.ai/hyperframes-oss/docs/images/showcase/launch-integration-codex-v1-s.mp4" poster="https://static.heygen.ai/hyperframes-oss/docs/images/showcase/launch-integration-codex-v1.jpg" />

    <div className="p-4"><strong className="block text-sm">Codex plugin</strong><span className="mt-1 block text-xs text-zinc-500 dark:text-zinc-400">HyperFrames inside Codex · 100% HyperFrames</span></div>
  </div>

  <div className="group overflow-hidden rounded-xl border border-zinc-200 bg-white text-inherit no-underline dark:border-zinc-800 dark:bg-zinc-950">
    <HoverVideo className="aspect-video w-full object-cover bg-zinc-100 dark:bg-zinc-900" src="https://static.heygen.ai/hyperframes-oss/docs/images/showcase/launch-integration-vercel-v1-s.mp4" poster="https://static.heygen.ai/hyperframes-oss/docs/images/showcase/launch-integration-vercel-v1.jpg" />

    <div className="p-4"><strong className="block text-sm">Vercel</strong><span className="mt-1 block text-xs text-zinc-500 dark:text-zinc-400">Deploy from a template · 100% HyperFrames</span></div>
  </div>

  <div className="group overflow-hidden rounded-xl border border-zinc-200 bg-white text-inherit no-underline dark:border-zinc-800 dark:bg-zinc-950">
    <HoverVideo className="aspect-video w-full object-cover bg-zinc-100 dark:bg-zinc-900" src="https://static.heygen.ai/hyperframes-oss/docs/images/showcase/launch-integration-ollama-v1-s.mp4" poster="https://static.heygen.ai/hyperframes-oss/docs/images/showcase/launch-integration-ollama-v1.jpg" />

    <div className="p-4"><strong className="block text-sm">Open models (Ollama)</strong><span className="mt-1 block text-xs text-zinc-500 dark:text-zinc-400">Build with local models · 100% HyperFrames</span></div>
  </div>

  <div className="group overflow-hidden rounded-xl border border-zinc-200 bg-white text-inherit no-underline dark:border-zinc-800 dark:bg-zinc-950">
    <HoverVideo className="aspect-video w-full object-cover bg-zinc-100 dark:bg-zinc-900" src="https://static.heygen.ai/hyperframes-oss/docs/images/showcase/launch-integration-community-v1-s.mp4" poster="https://static.heygen.ai/hyperframes-oss/docs/images/showcase/launch-integration-community-v1.jpg" />

    <div className="p-4"><strong className="block text-sm">Community · hyperframes.dev</strong><span className="mt-1 block text-xs text-zinc-500 dark:text-zinc-400">Publish and remix · 100% HyperFrames</span></div>
  </div>
</div>

## Recreate any video, 1:1

Point HyperFrames at a finished video and it rebuilds the same film in editable HTML —
gestures, type, timing and all. Below, each **reference** (the original, on the left) sits
next to its **HyperFrames replica** (on the right), matched frame for frame.

<div className="not-prose my-6 space-y-8">
  <ReplicaCompare title="THE OBLIST" meta="38.58s · 60fps" refSrc="https://static.heygen.ai/hyperframes-oss/docs/images/showcase/reverse-oblist-reference-snd.mp4" refPoster="https://static.heygen.ai/hyperframes-oss/docs/images/showcase/reverse-oblist-reference.jpg" replicaSrc="https://static.heygen.ai/hyperframes-oss/docs/images/showcase/reverse-oblist-replica.mp4" replicaPoster="https://static.heygen.ai/hyperframes-oss/docs/images/showcase/reverse-oblist-replica.jpg" />

  <ReplicaCompare title="Srinika × Mercury" meta="30.56s · 25fps · avatar footage" refSrc="https://static.heygen.ai/hyperframes-oss/docs/images/showcase/reverse-srinika-reference-snd.mp4" refPoster="https://static.heygen.ai/hyperframes-oss/docs/images/showcase/reverse-srinika-reference.jpg" replicaSrc="https://static.heygen.ai/hyperframes-oss/docs/images/showcase/reverse-srinika-replica.mp4" replicaPoster="https://static.heygen.ai/hyperframes-oss/docs/images/showcase/reverse-srinika-replica.jpg" />
</div>

## Start from a template

Create a project with a working example:

```bash theme={null}
npx hyperframes init my-video --example <name>
```

| Example         | Good starting point for                      |
| --------------- | -------------------------------------------- |
| `warm-grain`    | Branding, lifestyle, and editorial work      |
| `play-mode`     | Energetic social videos and product launches |
| `swiss-grid`    | Technical, structured, and corporate stories |
| `kinetic-type`  | Type-led promos, intros, and title cards     |
| `decision-tree` | Explainable diagrams and tutorials           |
| `product-promo` | Multi-scene product showcases                |
| `nyt-graph`     | Editorial charts and data stories            |
| `vignelli`      | Bold portrait announcements                  |
| `blank`         | Agent-generated or fully custom work         |

<Tip>
  Choose a template for its structure and tone, then replace the sample content. If none fits, start
  with `blank` and let the agent build from the brief.
</Tip>

## Related topics

* [Make your first video](/quickstart)
* [Choose a creation workflow](/workflows)
* [Take more control of an existing project](/go-further)
