* feat(studio): motion blur panel with Off / Subtle / Strong
One three-way control in the right dock. Subtle is the default blur
(240 deg shutter, samples sized to the smear); Strong is 720 deg and 64
samples. Writes the root data-motion-blur attributes through the SDK so
renders match and undo works; reads live attributes from the preview.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* fix(studio): motion blur panel edits the file's own root
The edits were worked out against the preview DOM but written to the file,
so a stale preview (undo inside the hold, a slow reload) could save a level
other than the one picked. They are now computed inside the mutation from
the candidate's root. While locked to a sub-composition no level is shown.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* fix(studio): shorten the motion blur tab so the dock strip fits
Five tabs overflowed the default dock width by 10px and clipped a label
(Studio: load smoke). The tab is now titled Blur.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
html-in-canvas captures a few things wrongly, and a wrong blurred frame is
worse than a sharp one. Add a defect list the runtime checks before it wraps
a composition: a WebGL or WebGPU canvas (captured frozen), backface-hidden
faces in a 3D context (drawn mirrored), an iframe (reloaded by the wrap and
not recoverable), a transform or opacity below 1 on the root (ignored by the
capture) and a canvas that is already a capture canvas (nesting hangs the
renderer). Any hit turns blur off for the whole composition and the runtime
says which one.
The backface scan reads rules nested inside a style rule (CSS nesting),
sheets pulled in with @import and adopted style sheets. Shadow DOM stays a
documented limit.
A face whose backface-visibility: hidden arrives through an animation counts
too: the scan also takes the elements of running CSS or WAAPI animations that
key it, and elements naming a @keyframes rule that does, as candidates.
Support Chrome 157's html-in-canvas API alongside the older one: a canvas
marked content="drawable" with a drawable attribute on captured children
replaces layoutsubtree. Blur, the VFX runtime and Studio's browser check
detect both through the new @hyperframes/core/html-in-canvas subpath.
Folded review fixes: R14, R9 (the iframe defect entry).
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
* chore: release v0.8.182
* chore: list #5953 in the v0.8.182 notes
* chore: add #5843, #5932 and #5945 to the v0.8.182 notes
* chore: add #5923, #5943, #5956, #5960, #5961, #5963, #5964 and #5966 to the v0.8.182 notes
* chore: add #5972, #5977, #5981 and #5982 to the v0.8.182 notes
* chore: add the 13 PRs merged since the last refresh to the v0.8.182 notes
* chore: add #6003, #6005 and #5579 to the v0.8.182 notes
* chore: add #6006 to the v0.8.182 notes
* chore: release v0.8.180
* docs(release): describe chunk sub-ranges as groundwork for split retries
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* chore: release v0.8.180 notes list the empty-frame fix
---------
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-authored-by: Miguel Ángel <miguel.sierra@heygen.com>
* fix(gcp-cloud-run): the BeginFrame contract image resolves the engine's core import
The engine config imports the distributed fps set from @hyperframes/core, and the contract stage of the GCP image copies only parsers and engine sources, so the import failed at build time. The set now lives in a small core subpath (@hyperframes/core/distributed-fps) that the engine imports like its other light core imports, and the contract stage copies core like parsers. The core root export is unchanged.
* style(core): format package.json
* feat(cli): audio answers beats, downbeats, bpm, sections and describe through a provider ladder
Beats and downbeats try api, apple-mu, beat-this, then signal; sections try api, apple-mu,
then signal; describe tries api, then apple-sound. bpm is read off whichever provider placed
the beats. Each provider says whether it can run here and why not, and the ladder warns
when it passes one over.
The signal rung is a TypeScript tracker: log-band spectral flux, autocorrelation tempo with
a log-normal prior, Ellis dynamic-programming beats with a windowed local tempo, a low-band
bar phase, and Foote novelty sections snapped to beats. It reports no steady beat when the
onsets do not repeat.
The api rung posts the audio to HYPERFRAMES_AUDIO_API_URL with HYPERFRAMES_AUDIO_API_KEY and
moves each answered time onto the nearest signal beat within a quarter beat, reporting what
moved and what did not. beat-this runs Beat This! from a configured Python and local weights
and never downloads. apple-sound runs macOS's sound classifier through a Swift helper.
apple-mu reports that Music Understanding needs macOS 27.
words are no longer accepted: they come from hyperframes transcribe.
* docs(cli): audio's beats, downbeats, bpm, sections, describe and their provider ladder
* feat(cli): beats tracks the music with the TypeScript beat tracker, without Chrome
hyperframes beats and Studio's beat detection now share analyzeMusicFromSignal: band energies,
then the spectral-flux tracker in audioRhythm.ts, then each beat's loudness for its strength.
The CLI decodes with the same ffmpeg path as hyperframes audio, so beats no longer launches
headless Chrome. Studio still decodes in the browser and passes every channel.
The old analyzer averaged bpm-detective's tempo with an energy-peak tempo it had only compared
after folding both into one octave, and phased a rigid grid from noisy peaks. On 22 real tracks
with exact script grids it found the tempo 18% of the time with beat F 0.437; this finds it
91% of the time with F 0.887, and refuses the other two rather than guess.
Below a tracker confidence of 0.2 no beats are reported, so ambient or rubato music gets no
invented grid and the CLI still writes nothing. bpm-detective, the energy-peak grid, the
headless analyzer and its prebuilt bundle are gone.
* fix(core): tempo reads 96 BPM off beats on 10 ms frames, not 95.2
The tracker places beats on 10 ms frames, so a 625 ms beat lands 620 or 630 ms apart and the
median interval reported 95.2 or 96.8 BPM. Tempo now averages the intervals within 10% of the
median: as robust to a skipped or doubled beat, without the frame-sized step. Both
hyperframes audio and hyperframes beats read it.
* fix(cli): remove redundant audio context comment
* fix(core): remove redundant tempo interval comment
* fix(ci): record beat analyzer removals and browser evidence
* fix(core): keep flat audio novelty in one section
* chore: keep PR capture evidence out of the source tree
* fix(cli): honor BPM provider pins before analysis
* fix(build): ship NodeNext-resolvable declarations from every package
tsc copies relative specifiers verbatim, so packages whose sources use
bundler-style extensionless imports (core, inspector) published .d.ts files
that a module/moduleResolution nodenext consumer cannot resolve. Core already
rewrote its emitted JavaScript for this, but not its declarations.
Move that rewrite to scripts/rewrite-relative-import-extensions.mjs, make it
cover .d.ts too (adding .js or /index.js, and failing the build when a
declaration import has no declaration), and run it after every tsc emit:
core, engine, sdk, producer, aws-lambda, gcp-cloud-run and inspector.
verify:packed-manifests now typechecks the clean packed consumer with
skipLibCheck false and type-only imports of every typed subpath, so an
unresolvable import inside a published declaration fails the gate and is
attributed to its package.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* chore(ci): allow the moved core rewrite-esm-extensions script deletion
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* ci: rebuild the edit-accuracy bench when the import-extension rewriter changes
The package builds now run scripts/rewrite-relative-import-extensions.mjs, so main's
edit_accuracy paths filter has to include it (check-edit-accuracy-filter test).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* feat(cli): hyperframes audio measures where sounds land, in film time
Adds @hyperframes/core/audio-analysis: an energy envelope, spectral-flux
and energy onsets, the moments a sound lands (onset, steepest rise, peak,
tail end), BS.1770 loudness with true peak, silence spans and a first
guess at sfx/voice/music, all pure functions of PCM. Each capability runs
through a provider ladder so model or service providers can join later.
The radix-2 FFT moves out of audioCarve so both share it.
`hyperframes audio` decodes with FFmpeg and analyzes files, or the film's
own audio and video clips by element, second, range or boundary, reading
only the part of each file that plays. Timeline rows now carry
`mediaStart`, moved by an in-point host's cut, so file time maps to film
time.
* feat(cli): audio placements carry the element's own data-start, local to its composition
* feat(cli): audio placements carry the element's own data-start, local to its composition
* feat(cli): audio places a file with --place-at, drops --boundary
--start becomes --place-at and --media-start becomes --skip-file-seconds,
which only goes with --place-at, refuses to skip the whole file, and warns
when the skipped part held the file's loudest moment. --boundary is gone:
--at and --range cover the sounds a cut touches.
* fix(cli): pass a sound file to ffprobe as a file, on every platform
ffprobe treated a name starting with a dash as an option because the argument
list never ended the flags. The path check compared a Windows drive path to a
POSIX string, and a new timeline comment raised that file's comment share.
* fix(cli): hear a reversed stereo channel, and warn when its peak was skipped
Averaging the channels cancelled a reversed one, so a real hit was reported
as silence. The skip warning also compared against the decode margin, so a
peak just before the kept window produced no warning.
* chore: release v0.8.159
* chore: v0.8.159 notes list the first-frame fix
* chore: v0.8.159 notes list the sandboxed GPU check fix
* chore: v0.8.159 notes list the hidden-scene fix and the offline font test
* chore: release v0.8.154
* ci: give the studio test job fifteen minutes
The queue run installs and builds inside the same ten minute cap.
It was still inside the studio tests when that cap cancelled the job.
* fix(inspector): stop audio waveform caches from making every live question stale
* fix(inspector): drop the comment and format the test
* refactor(core): share the folders Studio's preview ignores
* fix(core): register the project-folders subpath and bundle it into the inspector
* chore: release v0.8.152
* docs(changelog): correct the v0.8.152 release notes
The summary overstated snapshot retention, history turns, and video state.
It also left out the new artifact paths and nullable keyframe times.
Move the keyframe rest-value table into @hyperframes/core/keyframe-defaults
and import it from the SDK backfill and Studio's gsapShared, replacing the two
hand-synced copies.
Closes#5387
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>