mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-10-30 17:27:09 +00:00
* perf(aws-lambda): run chunks through a Distributed Map to lift the 40-chunk cap The inline Map runs at most 40 chunk renders at once. Both state machine definitions (CDK and SAM) now use a Distributed Map with STANDARD child executions, capped at the function's reserved concurrency so chunks past it queue instead of being throttled. Progress reads each chunk's child execution history so frames, cost and chunk errors still show up. * fix(aws-lambda): retry Lambda throttles for minutes and stop on oversized chunks A throttled task used to burn its four catch-all retries in about 30 s. It now gets its own jittered backoff of several minutes, because another render on the same function can still hold the reserved slots. Plan treats CHUNK_EXCEEDS_WORKER_TIMEOUT as terminal in both definitions. * chore(aws-lambda): trim stack comments to keep the comment share flat * fix(aws-lambda): keep render --wait working at hundreds of chunks Progress polls read the chunk Map Run's counts (one DescribeMapRun) instead of every child execution's history, take exact chunk frames and cost from the Map output once it exits, and read failed children only when the counts show a failure. The SDK client retries throttled calls up to 8 times with backoff. * feat(aws-lambda): size each render's concurrency to the account instead of a fixed 8 * fix(lambda): keep the concurrency SDK external in published modules * fix(cli): keep an existing stack's reserved concurrency on redeploy Redeploying without --concurrency sent ReservedConcurrency=-1, which removed the cap of 8 that earlier CLI versions reserved by default and stopped recording it in the state file. The override is now left out when the flag is omitted, so SAM keeps the stack's current value (a new stack still gets the template's unreserved default). The state file records the deployed value, and deploy prints the kept reservation. * fix(aws-lambda): keep progress working for callers without DescribeMapRun Callers whose IAM policy came from an earlier `lambda policies user` lack states:DescribeMapRun, so reading a running Distributed Map threw AccessDeniedException out of `lambda progress` and `render --wait` while the render itself went on to succeed. getRenderProgress now catches only that error around the map-run read, returns the parent snapshot (status, plan and assemble progress) and adds a warning to re-apply the policy, which both commands print. Other errors still throw. * fix(cli): let the documented deploy policy drop a reserved concurrency When a redeploy removes a function's reserved concurrency, CloudFormation calls lambda:DeleteFunctionConcurrency. The policy printed by `lambda policies role|user` granted only PutFunctionConcurrency, so the update failed and rolled back. Grant it, and pin it with states:DescribeMapRun in the required-actions test. * fix(aws-lambda): read smoke chunk hashes from the Distributed Map output With chunks in a Distributed Map, their task results live in child executions, so the smoke gate's chunk-hash check found 0 chunks in the parent history and failed every v1/v2 comparison. Read Chunks[] from the RenderChunks / RenderChunksV2 MapStateExited output instead, as getRenderProgress does. The stack this script deploys only has the Distributed Map, so the inline-Map path is gone. * fix(producer): cap a pinned chunk size at the worker concurrency, not 16 With --chunk-size or --target-chunk-frames and no --max-parallel-chunks, the plan still capped the render at 16 chunks however many workers the account allowed, so --chunk-size 30 on a 60 s render gave 16 chunks of 113 frames. The cap is now one wave across the worker concurrency, as the --max-parallel-chunks docs say; 16 remains when the concurrency is unknown. * feat(cli): remove a stack's reserved concurrency with --concurrency=none Since an omitted --concurrency now keeps the stack's reservation, there was no CLI way back to the unreserved default. --concurrency=none sends ReservedConcurrency=-1, the state file records the stack as unreserved and deploy says the reservation was removed. Positive N and an omitted flag behave as before; any other value is rejected instead of being silently ignored. * chore(aws-lambda): raise the package size budget for account-sized concurrency Measured with pnpm@10.17.1 pack after rebasing on main with the S3 assembly change: 67,763 packed / 244,685 unpacked; the budget is the check's own 5% headroom over that.
133 lines
13 KiB
Plaintext
133 lines
13 KiB
Plaintext
---
|
|
title: Migrating to HyperFrames Lambda
|
|
description: "Side-by-side mapping for adopters coming to HyperFrames from another one-command-deploy video renderer."
|
|
---
|
|
|
|
If you're already running a different framework that deploys a serverless video renderer with one command, the muscle memory translates cleanly: a single `deploy` provisions the stack, a single `render` starts a render, a single `progress` polls it, and a single `destroy` tears the stack down. This page maps your existing concepts onto HyperFrames' equivalents so you can spend the migration on the parts that actually differ instead of relearning the workflow.
|
|
|
|
## Concept mapping
|
|
|
|
| In your current framework you call... | In HyperFrames you call... | Notes |
|
|
| ------------------------------------- | ---------------------------------------------------------------- | -------------------------------------------------------------------------------------------- |
|
|
| One-shot deploy command | `hyperframes lambda deploy` | Builds `packages/aws-lambda/dist/handler.zip` and runs `sam deploy`. Idempotent. |
|
|
| One-shot site upload | `hyperframes lambda sites create ./project` | Content-addressed S3 key — re-uploads of an unchanged tree are skipped via a HeadObject 200. |
|
|
| Trigger a render | `hyperframes lambda render ./project --width 1920 --height 1080` | Returns immediately with a `renderId`; add `--wait` to stream per-chunk progress. |
|
|
| Poll render progress | `hyperframes lambda progress <renderId>` | Includes accrued cost in the same response. |
|
|
| Tear down | `hyperframes lambda destroy` | The S3 bucket is `Retain`'d — documented in the deploy guide. |
|
|
| Print/validate IAM policy | `hyperframes lambda policies user`/`role`/`validate` | Wire `validate` into CI to catch policy drift before the next deploy fails. |
|
|
|
|
## Composition format
|
|
|
|
If your current framework is **React-based**, you write JSX components, register them in a `Composition`, and the renderer compiles them at render time.
|
|
|
|
In HyperFrames, **compositions are plain HTML files**. A composition element
|
|
declares its ID and canvas, while clips declare their own timing. There is no
|
|
JSX compilation step.
|
|
|
|
```html
|
|
<!doctype html>
|
|
<html lang="en">
|
|
<body>
|
|
<div
|
|
id="stage"
|
|
data-composition-id="intro"
|
|
data-start="0"
|
|
data-width="1920"
|
|
data-height="1080"
|
|
data-duration="10"
|
|
data-fps="30"
|
|
data-no-timeline
|
|
>
|
|
<h1 id="title" class="clip" data-start="0" data-duration="10" data-track-index="0">Hello</h1>
|
|
</div>
|
|
</body>
|
|
</html>
|
|
```
|
|
|
|
For framework-agnostic animation, HyperFrames supports first-party adapters for GSAP, Anime.js, CSS keyframes, Lottie, Three.js, and the Web Animations API — covered in the [Concepts](/concepts) and per-skill docs.
|
|
|
|
## Render config
|
|
|
|
Most adopters' render config maps directly:
|
|
|
|
| Concept | HyperFrames equivalent | Where it lives |
|
|
| ------------------------ | ----------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
| `fps` | `--fps=30` (CLI) or `config.fps` (SDK) | 24, 25, 30, 60 only — non-integer NTSC rationals are an in-process-only feature. |
|
|
| `width` / `height` | `--width` / `--height` flags, or `config.width` / `config.height` | Even integers ≤ 7680 (yuv420p parity). |
|
|
| `codec: 'h264' / 'h265'` | `--codec=h264` or `--codec=h265` (mp4 only) | h265 uses libx265 with closed-GOP keyint params so chunked concat-copy round-trips losslessly. |
|
|
| Output format | `--format=mp4 / mov / webm / png-sequence` | webm uses libvpx-vp9 + closed-GOP concat-copy. Distributed mode still refuses HDR mp4 at plan time. |
|
|
| Quality preset | `--quality=draft / standard / high` | Maps onto ffmpeg encoder presets. |
|
|
| Chunk size in frames | `--chunk-size=240` (default: auto) | By default each chunk is sized to finish inside the Lambda timeout with headroom; a render that cannot fails at plan time with `CHUNK_EXCEEDS_WORKER_TIMEOUT`. |
|
|
| Max parallel chunks | `--max-parallel-chunks=16` (default: auto) | By default one wave across the deploy's `--concurrency` (or 90% of the account's unreserved concurrency), widened when a chunk would outrun the Lambda timeout. |
|
|
| Per-chunk frame ceiling | `--target-chunk-frames=N` (optional) | Caps frames per chunk so one chunk can't run past Lambda's 15-min cap on a long video: the planner adds chunks (up to `--max-parallel-chunks`) to keep each at or below `N`, and short videos still collapse to fewer chunks. A ceiling, not a fixed size; ignored when `--chunk-size` is set. |
|
|
| Bitrate / CRF | `config.bitrate` or `config.crf` in the SDK | Mutually exclusive; the current Lambda CLI does not expose these two fields. |
|
|
|
|
## Variables (inputProps)
|
|
|
|
Render-time payloads — `inputProps` in some frameworks, `variables` in HyperFrames — are isomorphic. Declare the composition's variable shape on its root `[data-composition-id]` element via `data-composition-variables`, then pass per-render values with `hyperframes render --variables '{...}'` locally or `hyperframes lambda render --variables` on the Lambda surface. The Lambda execution input is capped at 256 KiB, so reference large assets by URL instead of embedding base64 data.
|
|
|
|
The full mapping — `defaultProps` → declarations, `useCurrentFrame()` + `props.<x>` → `__hyperframes.getVariables().<x>`, `renderMediaOnLambda({ inputProps })` → `renderToLambda({ config: { variables } })` — lives in [Templates on Lambda](/deploy/templates-on-lambda#migrating-from-remotion-lambda-inputprops).
|
|
|
|
## What HyperFrames does differently
|
|
|
|
A few areas where the contract is intentionally different from comparable frameworks. Surface them up front so the migration doesn't surprise you mid-deploy.
|
|
|
|
### Deterministic Chrome path is mandatory
|
|
|
|
HyperFrames refuses `data-gpu-mode="hardware"` in distributed mode — hardware GL is non-deterministic across chunk boundaries, and the per-chunk concat-copy assumes byte-level reproducibility. Compositions that opt into hardware GL in-process must drop it for Lambda renders. The Lambda handler trips a typed `BROWSER_GPU_NOT_SOFTWARE` non-retryable error on plan that's easy to catch in the progress output.
|
|
|
|
### Font fetching fails closed
|
|
|
|
`failClosedFontFetch` is default-on in distributed mode. A composition that references a `font-family` HyperFrames can't fetch will fail at plan time (`FONT_FETCH_FAILED`) rather than silently falling back to the OS default. If you currently lean on system-font fallbacks, list the fonts you need explicitly via `<link rel="stylesheet">` or `@fontsource/*` imports.
|
|
|
|
### HDR is not supported
|
|
|
|
`hdrMode: 'force-hdr'` is rejected at plan time. Use the in-process renderer
|
|
outside Lambda for HDR output.
|
|
|
|
### webm uses closed-GOP VP9
|
|
|
|
webm distributed renders go through libvpx-vp9 with `-g <chunkSize>`, `-keyint_min <chunkSize>`, `-auto-alt-ref 0`, and `-cpu-used 4` by default. The alt-ref disable is the load-bearing bit: libvpx-vp9's default non-displayable alt-ref frames can land anywhere in a GOP, which breaks concat-copy at chunk seams. Closed-GOP forces a keyframe at every chunk boundary so `ffmpeg -f concat -c copy` round-trips losslessly. Output is `yuva420p` to preserve alpha. Audio is muxed as Opus.
|
|
|
|
Distributed WebM can be larger than the same composition rendered in one pass
|
|
because closed-GOP encoding forces more keyframes. VP9 encode speed is
|
|
controlled by `PRODUCER_VP9_CPU_USED` (`-8` to `8`); use lower values for
|
|
quality-sensitive or long-form WebM, and higher values when wall-clock encode
|
|
time matters more than compression efficiency. Benchmark local and distributed
|
|
rendering with the actual composition before choosing a path.
|
|
|
|
### State files are local by default
|
|
|
|
`hyperframes lambda deploy` writes `<cwd>/.hyperframes/lambda-stack-<name>.json` so subsequent verbs don't re-derive the bucket / state-machine ARN. Two worktrees produce two distinct state files. If you need a shared default location across CI workers, symlink the directory or pass `--stack-name` explicitly on every call.
|
|
|
|
### IAM policy is print-then-narrow
|
|
|
|
The default policy doc emitted by `hyperframes lambda policies user/role` uses `Resource: "*"` because the CloudFormation stack creates new ARNs on every adopter's first deploy. After your first successful deploy, narrow the `Resource` to the deployed ARNs — they're predictable from the CFN outputs. CI users typically check the narrowed policy into source and run `hyperframes lambda policies validate ./infra/policy.json` as a pre-deploy gate.
|
|
|
|
## Migration checklist
|
|
|
|
1. **Inventory** the compositions you want to migrate. Filter out anything that needs HDR — that stays on your current framework for now. webm renders distributed via closed-GOP VP9 + concat-copy (see the webm section above).
|
|
2. **Translate** each composition to plain HTML. The `[Concepts](/concepts)` page covers the data-attribute conventions; installing the skills (`npx hyperframes skills update`) makes Claude / Cursor / Codex aware of them too — start at `/hyperframes`, which routes to `/hyperframes-core` for the composition contract.
|
|
3. **Wire** the new composition into your build pipeline alongside the old one. HyperFrames doesn't need an external bundler — you can `npx hyperframes preview` against the HTML directly.
|
|
4. **Deploy** in a separate AWS account or with a `--stack-name=hyperframes-staging` first. Run a real render with `--wait`; verify the output bytes.
|
|
5. **Add the policy** to your CI. `hyperframes lambda policies user > infra/iam/hyperframes.json` then `hyperframes lambda policies validate infra/iam/hyperframes.json` on every PR.
|
|
6. **Cut over** by pointing your existing automation at the new render endpoint. Keep the old deployment alive until you've verified rolling renders for a release cycle, then `hyperframes lambda destroy` the staging stack and decommission the previous one.
|
|
|
|
## Non-Lambda runtimes
|
|
|
|
If you don't want Lambda specifically, the same `@hyperframes/producer/distributed` primitives run anywhere Node + Chrome + ffmpeg + S3 are available. A reference Dockerfile lives at `examples/k8s-jobs/Dockerfile.example` for adopters running on:
|
|
|
|
- Google Cloud Run Jobs
|
|
- Azure Container Apps Jobs
|
|
- AWS ECS Fargate
|
|
- Kubernetes Jobs / Argo Workflows
|
|
- Plain Docker on a beefy VM
|
|
|
|
Build it yourself — we don't publish a Docker image to a registry. The Dockerfile is documented inline and bakes Node 22 + chrome-headless-shell + ffmpeg + the producer at the version your checkout is on.
|
|
|
|
## Related topics
|
|
|
|
- [Deploy HyperFrames on AWS Lambda](/deploy/aws-lambda)
|
|
- [Render templates on Lambda](/deploy/templates-on-lambda)
|
|
- [Use the lower-level Producer pipeline](/packages/producer)
|