Files
hyperframes/packages/cli
dd97478175 fix(media-use): remove clicks and ~25ms gaps at every transcript-cut splice (#4179)
* fix(media-use): remove clicks and ~25ms gaps at every transcript-cut splice

transcript-cut extracts each kept range to its own final-codec file, then
concat-copies them. That produces two audible artifacts at every cut:

1. No boundary fades. Concat splices raw segment edges together, so the
   waveform steps discontinuously and each cut clicks.

2. Encoder priming silence. Each segment is encoded to AAC independently,
   so the encoder pads every one with priming samples. Stream-copy concat
   bakes that padding in as a silent gap at each splice. Measured 24.4 ms
   and 24.1 ms at the two splices of a three-range cut. It accumulates:
   the nth splice lands roughly 25n ms late, so splice positions drift
   away from the sum of the preceding segment durations.

The second survives the first -- adding fades alone leaves the gap.

Fix: intermediates carry PCM (.mkv for video, .wav for audio-only) instead
of the final codec, so audio is encoded exactly once, at concat. Boundary
fades of 30 ms are applied per segment, scaled down on segments shorter
than 4x the ramp and skipped on degenerate ones.

Measured on the same three-range cut, widest run of digital silence at
each splice:

  before  24.4 ms / 24.1 ms
  after    0.1 ms /  0.1 ms

--copy is unchanged: stream copy cannot filter, so it keeps producing
final-codec segments and a copy concat. Audio-only output is byte-identical
in duration before and after. scripts/lib test suite: 34/35, the one
failure (lut-preset-provider) pre-exists on main and is unrelated.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* chore(skills): regenerate manifest for media-use

* fix(media-use): honor the fadeIn/fadeOut flags fadeFilterFor was passed

The caller already computed {fadeIn, fadeOut} per segment, but
fadeFilterFor still took one parameter and always emitted both
afade ramps -- so a single kept segment (no splice at all) and every
segment's true start/end got faded regardless. Extracted the pure
function into lib/transcriptCutFade.mjs, made it honor both flags,
and added its first unit test: first/middle/last/single-segment
filter strings, no ffmpeg needed, fails against the previous version.

Also installs ffmpeg in the Test: skills CI job (same action the
Producer job uses), so transcript-cut.test.mjs's real-tone RED/GREEN
proof runs in CI instead of skipping silently for lack of the binary.

* fix(media-use): fix the packed-CLI import and lint on transcript-cut.mjs

build-copy.mjs replaces dist/skills/media-use/scripts/lib with a copy
of packages/cli/src/media-use/lib, so transcriptCutFade.mjs needed a
byte-identical twin there (and in MEDIA_USE_COPY_NAMES) or the packed
CLI's transcript-cut.mjs imported a file that no longer existed.
Moved its test alongside, matching every other lib helper's tests.

Also drops three redundant Boolean(args.copy) wrappers (a ternary
condition already coerces) that oxlint's no-extra-boolean-cast
flagged.

* style(media-use): reflow the shortened ext ternary to oxfmt's shape

Dropping the Boolean() wrapper shortened the line enough that oxfmt
0.41 now collapses the ternary onto one line; format:check was failing
on the stale multi-line version.

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Miguel Angel Simon Sierra <miguel.sierra@heygen.com>
2026-09-22 20:28:50 -04:00
..
2026-09-22 16:12:35 -04:00

hyperframes

CLI for creating, previewing, and rendering HTML video compositions.

Install

npm install -g hyperframes

Or use directly with npx:

npx hyperframes <command>

Requirements: Node.js >= 22, FFmpeg

Commands

init

Scaffold a new Hyperframes project from a template:

npx hyperframes init my-video
cd my-video

preview

Start the live preview studio in your browser:

npx hyperframes preview
# Studio: http://localhost:3002/#project/my-video
# Server: http://localhost:3002

npx hyperframes preview --port 4567

In an interactive terminal, the preview stays attached until you press Ctrl+C. In a non-interactive shell such as a coding-agent session, the same command starts a managed preview that survives after the command returns. Use --background or --foreground to choose explicitly, and manage persistent previews with --status, --stop, --list, and --kill-all. Add --json to managed lifecycle commands for machine-readable output. --foreground --json prints the ready-session envelope once, then remains attached until stopped.

normalize-audio

Measure two local authored audio clips with integrated LUFS and match the target to the unchanged reference. The command is a dry run unless --write is passed:

npx hyperframes normalize-audio --reference target-audio --target user-audio
npx hyperframes normalize-audio --reference target-audio --target user-audio --write

It updates only the target element's data-volume and refuses unsafe boosts that exceed Studio's +12 dB ceiling or would clip.

render

Render a composition to MP4. Run from the project directory; the positional argument is the project directory (not a file), so render the project's index.html directly, or point at a specific composition file with -c:

npx hyperframes render -o output.mp4
npx hyperframes render -c ./my-composition.html -o output.mp4

Set HYPERFRAMES_RENDER_DETACHED=1 when starting a render with nohup or disown so it keeps running after the invoking shell exits.

publish

Upload a project directory and get a hosted URL that keeps working after the CLI exits. Published projects are private by default:

npx hyperframes publish
npx hyperframes publish ./my-video
npx hyperframes publish --public
npx hyperframes publish --yes

Signed-out publishing returns an authentication-required claim URL; opening it lets someone sign in and claim the project. Sign in first with npx hyperframes auth login to publish an owned project you can update. Use --public to make the claimed project visible to anyone. --yes only skips the confirmation prompt and does not change visibility.

Signed-in publishers can use --update <url-or-id> to target an existing project or --space <space-id> to publish into a shared team space. If the requested project is missing or inaccessible, publishing can create a new project instead; check the printed URL and status.

See the publish reference for all options, including video proxy settings.

lint

Validate your Hyperframes HTML:

npx hyperframes lint ./my-composition
npx hyperframes lint ./my-composition --json      # JSON output for CI/tooling
npx hyperframes lint ./my-composition --verbose   # Include info-level findings

By default only errors and warnings are shown. Use --verbose to also display informational findings (e.g., external script dependency notices). Use --json for machine-readable output with errorCount, warningCount, infoCount, and a findings array.

compositions

List compositions found in the current project:

npx hyperframes compositions

benchmark

Run rendering benchmarks:

npx hyperframes benchmark ./my-composition.html

doctor

Check your environment for required dependencies (Chrome, FFmpeg, Node.js):

npx hyperframes doctor

browser

Manage the bundled Chrome/Chromium installation:

npx hyperframes browser

info

Print version and environment info:

npx hyperframes info

docs

Open the documentation in your browser:

npx hyperframes docs

upgrade

Check for updates and show upgrade instructions:

npx hyperframes upgrade
npx hyperframes upgrade --check --json  # machine-readable for agents

Documentation

Full documentation: hyperframes.heygen.com/packages/cli