Live demo: hyperframes-on-cloudflare.jdrusso1020.workers.dev
A HyperFrames template that previews HTML video compositions in the browser and renders MP4s server-side using a Cloudflare Container (Chromium + FFmpeg) and stores them in R2.
Demonstrates Worker-to-Container fetching via Durable Object bindings, streaming response bodies through the Worker into R2, and bundling sub-compositions into a single self-contained preview HTML at build time.
Deploying provisions a Worker, the RenderContainer Durable Object, and an R2 bucket (hyperframes-renders). Cloudflare Containers requires a Workers Paid plan.
- Preview a bundled composition (
ui-3d-reveal) in the browser using<hyperframes-player>, the zero-dependency web component from@hyperframes/player. - Render the composition to an MP4 by POSTing to
/api/render. The Worker streams the composition to a Cloudflare Container running a pre-built image with Chromium + FFmpeg + HyperFrames, streams the rendered MP4 directly into R2, and returns a URL.
Authoring happens locally. This template ships with one pre-authored composition. To build your own, use the HyperFrames CLI on your machine:
npx hyperframes init my-video
cd my-video
npx hyperframes preview # live-reload editor in your browserThen swap it into this template (see Swapping the composition below).
Browser Worker Container DO (instance_type: standard-4)
┌──────────────────┐ ┌────────────────────────┐ ┌──────────────────────────────────┐
│ <hyperframes- │ ─────▶ │ /api/render │ ────▶ │ Node HTTP server (port 8080) │
│ player> │ │ - load files from │ │ - writes files to /tmp/ │
│ preview iframe │ │ ASSETS │ │ - hyperframes render │
│ │ │ - POST → container │ │ (Chromium + ffmpeg) │
│ │ ◀──── │ - stream → R2 bucket │ ◀──── │ - streams mp4 in response │
│ │ url │ - return /r/<key> │ mp4 │ │
└──────────────────┘ └────────────────────────┘ └──────────────────────────────────┘
│
├─▶ R2 (hyperframes-renders)
│
└─▶ ASSETS (preview HTML, composition files)
Cold-start of a render container is faster than installing dependencies on every request because the renderer is baked into the image at build time, not installed at runtime:
node:22-bookworm-slimbaseapt-get installChromium system libs (libnss3,libxcomposite1,pango, …)npm install hyperframes ffmpeg-static- Symlink
ffmpeg-static/ffmpegto/usr/local/bin/ffmpeg npx hyperframes browser ensureto downloadchrome-headless-shell- Copy
container/server.mjs(a small Node HTTP server) andCMD ["node", "server.mjs"]
At render time, the Worker sends composition files in the request body, the container writes them to a tmp dir, runs hyperframes render, and streams the MP4 back. Container instances sleep after 10 minutes of inactivity (sleepAfter on the Container class).
Cloudflare's Browser Rendering is a hosted Chromium API — great for screenshots and PDFs, but you can't install FFmpeg into it. HyperFrames needs full control of the Chromium process plus an FFmpeg binary on the same filesystem, which is exactly what Cloudflare Containers gives you: an OCI container in a Worker-bound Durable Object, with up to 4 vCPUs and 12 GiB of RAM on standard-4.
With 4 vCPUs, hyperframes render --workers auto launches 3 parallel Chrome workers, cutting the render time roughly 2× vs. the single-worker default.
npm install
npm run devwrangler dev runs the Worker locally and builds + runs the container against your local Docker daemon (Docker is required for local container dev). The browser preview works without Docker; only /api/render needs the container.
If you want to iterate on the Dockerfile or container/server.mjs without booting Wrangler, you can hit the container directly:
docker build -t hf-render .
docker run -d --rm --name hf-test -p 18080:8080 hf-render
node scripts/test-render.mjs 18080 /tmp/out.mp4
docker stop hf-testThe script reads src/composition-manifest.json, base64-encodes the composition files, POSTs them to the container, and writes the MP4 it returns. The bundled 13s composition renders in ~25s on a 6-vCPU host.
src/
index.ts # Worker entry — preview + /api/render + /r/<key>
container.ts # RenderContainer Durable Object
composition-manifest.json # Generated by scripts/build.mjs
container/
server.mjs # Node HTTP server inside the container
package.json # Container deps (hyperframes + ffmpeg-static)
public/
index.html # Preview UI + Render button
compositions/
ui-3d-reveal/ # The bundled example composition
index.html
compositions/*.html
scripts/
build.mjs # Run via wrangler.jsonc → build.command
bundle-preview.ts # Bundles composition into single HTML via @hyperframes/core
test-render.mjs # Local container E2E
Dockerfile # Render container image
wrangler.jsonc # Worker + Container + R2 bindings
- Drop your composition bundle into
public/compositions/<your-name>/. - Set
PREVIEW_COMPOSITION_DIRenv var when running build/deploy:Or edit the default inPREVIEW_COMPOSITION_DIR=compositions/<your-name> npm run deploy
scripts/build.mjs(line 16). - Optionally update the player dimensions in
public/index.htmlif your composition isn't 1920×1080. - Re-run
npm run devornpm run deploy—scripts/build.mjsregenerates the manifest and bundle.
Cloudflare Containers pricing — pay-per-10ms for memory, CPU, and disk. A 70-second render on standard-4 (4 vCPU, 12 GiB) costs ~$0.008. R2 storage is $0.015/GB-month with no egress fees within Cloudflare's network.
Apache-2.0 — same license as HyperFrames itself.
