Skip to content

Show a live character preview beside the emote panel on hover - #140

Merged
Julian-adv merged 3 commits into
Julian-adv:masterfrom
darkdarkcocoa:feature/emote-live-preview
Aug 25, 2026
Merged

Show a live character preview beside the emote panel on hover#140
Julian-adv merged 3 commits into
Julian-adv:masterfrom
darkdarkcocoa:feature/emote-live-preview

Conversation

@darkdarkcocoa

@darkdarkcocoa darkdarkcocoa commented Aug 23, 2026

Copy link
Copy Markdown
Contributor

Summary

Hovering an emote row now shows the player's own character performing that emote in a small box beside the panel — inspired by the emote previews in Once Human and other MMORPGs. The preview renders the live character model with its equipped look, so what you see is exactly what everyone else will see.

Clicking a row performs the emote for real — the server broadcasts it to everyone nearby. Hovering costs nothing, so a player can check what a dance actually looks like, at portrait scale, before deciding to perform it in public; the top-down camera keeps the in-world character small, so the preview also doubles as a close-up of what everyone else is seeing.

Like the panel itself, the preview is fully derived from the server's emote list: it plays clips from social.glb by name, so a new server emote gets a working preview with zero extra client work — no per-emote images to produce or record.

Preview box beside the emote panel, playing the Twist dance on the player's own character Chicken emote clicked: the active row is highlighted and both the preview and the in-world character dance

How it works

  • EmotePreview.svelte mounts a second small Threlte <Canvas> (own WebGPU renderer) inside the panel — the shared main canvas is busy rendering the world, so a self-contained canvas keeps its render pipeline untouched, the same approach CharacterPreview takes on the select screen. EmotePreviewScene.svelte loads the character model and the social animation pack through the shared GLB cache — everything is already in memory in-game, so there is no extra download.
  • Clips play by name on an AnimationMixer, same as PlayerModel does in-world; one-shots loop in the preview so the motion stays readable.
  • The box follows the hovered row, keeps the last emote playing when the pointer leaves, and hides while dead/disconnected or while the panel is being dragged.
  • emote-preview-layout.ts decides which side of the panel has room (flipping right when the panel is dragged against the left edge) and hides the preview entirely on viewports where neither side fits (< 415 px), where it also skips mounting the canvas at all.

Performance notes

  • The preview renders only while an emote is actually playing (manual invalidation), steps at the graphics preset's frame cap via the shared createRenderCadence, and takes its pixel ratio from the preset cap through the Canvas dpr prop.
  • A dedicated createPreviewWebGPURenderer reuses the main renderer's applied antialias without touching the stored applied-AA record (which drives the settings panel's restart notice), and defers a dispose that races init() so the GPU device is actually released.

Testing

  • npm test (client): 513 passing, including new placement tests covering the side flip and the narrow-viewport cutoff.
  • npm run check / npm run lint: clean.
  • Verified live: hover/switch/click-to-play, panel close/reopen, click-through over the hidden box, no console errors.

Hovering an emote row renders the player's own character performing
that emote in a small box beside the panel, on a second self-contained
Threlte canvas (same approach as the select screen's CharacterPreview).
Clips come from social.glb by name, so new server emotes get a working
preview with no extra client work.

The box follows the hovered row, keeps the last emote playing on
pointer leave, hides while dead/disconnected or mid-drag, flips sides
when the panel is dragged against the left edge, and skips mounting
entirely on viewports where neither side fits. The preview renders only
while a clip is playing, at the graphics preset's frame cap and pixel
ratio; its renderer factory leaves the main canvas's applied-AA record
untouched and defers a dispose that races init() so the GPU device is
released.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@darkdarkcocoa
darkdarkcocoa marked this pull request as draft August 23, 2026 08:44
darkdarkcocoa and others added 2 commits August 23, 2026 17:51
The panel's default position was top 16% of the screen, far from the
bottom-right social button a player just clicked. Anchor it above the
HUD corner buttons instead, matching where the flyout appears.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The stop-vs-play toggle compared the clicked emote with the
server-confirmed active anim, which lags a round trip behind. Clicking
fast turned plays into stops (killing the dance commanded a moment
earlier) and re-clicks of a just-stopped dance into dead re-stops.

Track the last commanded intent and judge clicks against it, falling
back to the echo once it confirms or a TTL assumes the server rejected
the command. Covered by emoteClickCommand tests.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@darkdarkcocoa
darkdarkcocoa marked this pull request as ready for review August 23, 2026 14:38
@Julian-adv
Julian-adv merged commit 17d8ba5 into Julian-adv:master Aug 25, 2026
3 checks passed
@github-actions github-actions Bot locked and limited conversation to collaborators Aug 25, 2026
@darkdarkcocoa
darkdarkcocoa deleted the feature/emote-live-preview branch August 26, 2026 00:22
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants