Show a live character preview beside the emote panel on hover - #140
Merged
Julian-adv merged 3 commits intoAug 25, 2026
Merged
Conversation
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
marked this pull request as draft
August 23, 2026 08:44
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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.glbby name, so a new server emote gets a working preview with zero extra client work — no per-emote images to produce or record.How it works
EmotePreview.sveltemounts 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 approachCharacterPreviewtakes on the select screen.EmotePreviewScene.svelteloads 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.AnimationMixer, same asPlayerModeldoes in-world; one-shots loop in the preview so the motion stays readable.emote-preview-layout.tsdecides 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
createRenderCadence, and takes its pixel ratio from the preset cap through the Canvasdprprop.createPreviewWebGPURendererreuses 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 racesinit()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.