Skip to content

feat(player): add customizable branding to the preview player - #1

Draft
robinebers wants to merge 13 commits into
mainfrom
player-host-icons-loader-mark
Draft

robinebers wants to merge 13 commits into
mainfrom
player-host-icons-loader-mark

Conversation

@robinebers

Copy link
Copy Markdown
Owner

Why

Teams that put the HyperFrames player inside their own product get the HyperFrames play button, volume icons and green HyperFrames logo on the loading screen, and there is no supported way to change them. The player looks like someone else's product sitting inside yours. The only workaround today is reaching into the player's internals, which can break with any release.

What changes

A page can now give the player its own artwork. You put an icon inside the <hyperframes-player> tag and label which part it replaces: the play icon, the pause icon, the three volume icons (loud, quiet, muted) or the loading logo. The player shows it in place of its own.

  • Anything you don't replace keeps today's HyperFrames look, so existing players look exactly as they did.
  • Your icons take on the control bar's color, so they match the rest of the bar.
  • A custom loading logo replaces the HyperFrames logo together with its light sweep and green glow. You can animate your own logo with ordinary CSS.
  • The package README and the docs page have a short guide with a copy-paste example.

Before

The page supplies its own icons and logo, and the player ignores them.

Before: the player shows HyperFrames icons and logo

After

The same page: the player shows the page's icons in the control bar and its logo on the loading screen.

After: the player shows the page's own icons and logo

Test plan

Checked in Chrome 157 against a build of main on the same pages:

  • Default look unchanged: when the page supplies nothing, screenshots of six player states (paused, playing, quiet volume, muted, loading assets, shader prep) are byte-for-byte identical to main.
  • The page's artwork shows in all six states, and the player still shows it with sandbox-origin="opaque".
  • Clicking a custom play icon plays once, clicking again pauses once, the Space key still works, and the play and pause icons still swap.
  • A custom play icon sits centered in its button and follows the bar's color (--hfp-color).
  • Custom icons come back after controls is turned off and on and after the player is moved in the page, and stay hidden while controls is off.
  • A custom loading logo hides the HyperFrames logo, sweep and glow. Without one, the HyperFrames logo keeps them.
  • bun run --cwd packages/player test (584 tests, 5 new), typecheck, oxlint, oxfmt, fallow, the comment checks and bun run verify:packed-manifests pass.

How

  • Each replaceable glyph now sits in a named <slot> in the player's shadow DOM, with today's SVG as the fallback: play-icon, pause-icon, volume-high-icon, volume-low-icon, volume-muted-icon and loading-mark. No new attributes, CSS variables, parts or styles.
  • The volume button holds one of three slots. Each is created once and swapped in only when the state changes, with the same thresholds as before: muted, volume below 0.5, otherwise high.
  • The HyperFrames sweep and glow now live inside the loading-mark fallback, so a custom logo never gets them.
  • Raises the player's package size budget (packed 157,145 → 159,700, unpacked 541,601 → 547,900). With CI's pnpm 10.17.1, main packs to 158,705 bytes, 11 under the old limit. This change adds about 380 bytes of code and 600 bytes of README (packed 159,691).

Related work

No earlier issue or PR covers player icons or branding. heygen-com#241 added the speed menu and the control bar's CSS theming, whose color these icons pick up.

Open in Capy

miguel-heygen and others added 9 commits November 14, 2026 03:05
…pacity (heygen-com#7583)

* fix(studio): menus and popovers ease their scale in, not only their opacity

* style(studio): format the floatingMotion test
…en-com#7689)

skills/gsap was folded into the animation skill in heygen-com#1349, but build-copy
still listed it, so every CLI build printed "[build-copy] skill not found,
skipping: skills/gsap". Drop the entry, and add a test that every bundled
skill name has a SKILL.md so the next rename fails CI instead of warning.

Co-authored-by: Claude Opus 5.5 <[email protected]>
…indings (heygen-com#7699)

A color-graded <video> is painted into a canvas the runtime inserts after it,
with a runtime-generated id (__hf_color_grading_<video id>). Layout findings on
that canvas (e.g. container_overflow for a punch-in inside an overflow:hidden
box) named the canvas, so the fix hint "mark intentional overflow with
data-layout-allow-overflow" pointed at an element the author never wrote.
The audit already reads flags from the authored video (authoredElement);
uniqueSelectorFor now names that element too, so the finding's selector,
data attributes and source file are the video's.

Co-authored-by: Claude Opus 5.5 <[email protected]>
…es with (heygen-com#7697)

The settings line printed the encoder tier, so the default render said
"30fps · standard · …" while it encoded the looks preset at CRF 16, and
-q delivery said "high". A long-form render that came out at ~40 Mbps
read as "standard quality" in its own log.

The line now prints the --quality name as asked for and the CRF or
bitrate in effect: "looks (CRF 16)" by default, "looks (CRF 20)" with
--crf 20, "looks (10M)" with --video-bitrate 10M, "draft", "delivery".

Co-authored-by: Claude Opus 5.5 <[email protected]>
…gen-com#7608)

`hyperframes init --non-interactive` closes with `cd <name> && npm run …`
lines for the person or agent to paste. The name went in as typed, so a
name with a space or a quote printed a command that fails as written:
`cd my first video` (bash: "cd: my: No such file or directory", zsh: "too
many arguments") and `cd it's mine` (an unterminated quote).

A new `shellArg` quotes a word only when a shell would split or expand
it: double quotes, or single quotes when the text holds `$`, a backtick,
`\`, `"` or `!`; on Windows, double quotes, with `\` paths left bare.
`hyperframes open` / `preview --stop` hints (desktopApp's `shownDir`)
already quoted whitespace with JSON.stringify and now use it too, so a
name like `it's` or `a&b` is covered there as well.

Co-authored-by: Claude Opus 5.5 <[email protected]>
heygen-com#7700)

duplicate_audio_track said two overlapping clips on one data-track-index
"cause layered audio playback" and suggested moving one to another track.
The render's mixer never reads data-track-index (every audible clip is its own
amix input) and the docs call the attribute a Studio lane only, so the two
clips are layered whatever their tracks: rendering a whoosh and a hit on
track 6, then with the hit on track 7, gives byte-identical audio. The message
now says both play and that the overlap is two clips sharing one Studio lane;
the hint says splitting tracks leaves the mix unchanged, and that a leftover
duplicate should be removed or moved. lint-rules.mdx said the audio "doubles
up"; it now says the same as the message.

Co-authored-by: Claude Opus 5.5 <[email protected]>
A page that embeds <hyperframes-player> can now give it its own play,
pause and volume icons and its own loading logo, by putting elements with
a slot attribute inside the player. Anything the page leaves out keeps
today's HyperFrames look.

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
@github-actions

github-actions Bot commented Nov 14, 2026 •

Copy link
Copy Markdown

Edit accuracy: accurate 267 (base branch 267), smooth 123 of those

The gate passes.
Smoothness is reported in the artifact, not gated. A case fails only if it fails 2 of 3 runs.

Quarantined, measured but not gated (8)

miguel-heygen and others added 4 commits November 14, 2026 04:15
…#7714)

* feat(studio): toasts show as a centred pill that rises in

* fix(studio): error toasts stay up on a click so their text can be copied
…gen-com#7716)

`hyperframes docs rendering` said workers run 1-8, but the engine
clamps an explicit value to 24, and it never mentioned --format or
--gif-loop. The options now match render.ts and the engine limit.

Fixes heygen-com#7668
…ygen-com#7715)

capture read --timeout with parseInt, so `--timeout 1s` became a 1 ms
page-load timeout. grade-compare dropped an invalid value without a
warning. Both now use the shared parseTimeoutMs helper that check and
snapshot already use, keeping their defaults of 120000 ms and 5000 ms.

Fixes heygen-com#7671
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants