She stops rendering after any mode switch, and nothing but a restart brings her back #68
Labels
No labels
needs-info
needs-triage
ready-for-agent
ready-for-human
v2
wayfinder:grilling
wayfinder:map
wayfinder:prototype
wayfinder:research
wayfinder:task
wayfinder:ticket
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
aiko/denpa#68
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Found on 2026-07-31 by switching modes on the desktop. Two failed fix attempts are recorded below so nobody repeats them.
Reproduction
Confirmed by the operator across several restarts. Cold start always works; the first mode switch always loses her permanently.
The tray does not rescue it. The tray menu has window/pet items and Show/Hide, and none of them restored the model or produced a visible window. So a lost model in pet mode currently requires killing the process — arguably worse than the render bug, because the tray is supposed to be the way back from an invisible window.
What is established
<Live2D />is mounted at three different places in the tree, one per mode:App.tsx:44(insideWindowReceiver→ReceiverChrome),:160(Android, insideOverlayPalContainer),:165(desktop pet, inside a positionedBox). React therefore unmounts one canvas and mounts another on every mode change.The Cubism layer underneath is all module singletons bound to a single global DOM id:
WebSDK/src/lappglmanager.ts:42— the constructor doescanvas = document.getElementById('canvas')once, into a module-levels_instance.WebSDK/src/main.ts:26-29—initializeLive2Dreleases onlyLAppLive2DManager. NeitherLAppDelegatenorLAppGlManageris released.WebSDK/src/main.ts:31-33— the guard is!LAppGlManager.getInstance() || !LAppDelegate.getInstance().initialize(). A surviving-but-stale GL manager satisfies it, so initialisation reports success.releaseInstance: grep acrosssrc/renderer/srcreturns exactly one hit, inside that one effect (hooks/canvas/use-live2d-model.ts:131).So the leading hypothesis remains: after a mode switch the GL manager still holds the WebGL context of a canvas React has already removed, init "succeeds" against it, and nothing is drawn. It is a hypothesis, not a finding — see below.
Two fixes that did not work
1. Clearing the re-init timer on unmount.
use-live2d-model.ts:129scheduled release-and-reinit on a 500mssetTimeoutwith no cleanup, while the effect directly below it at:175-182clears its own timer. The theory was that the departing mode's timer fired after the arriving canvas mounted and tore it down. Tested by the operator: she still disappears. The cleanup was kept anyway — an uncleaned timer is a real defect and the sibling effect proves the house style — but it fixes nothing observed. Shipped in985809c.2. Releasing the delegate and the GL manager too, so the constructor would re-bind to the live canvas:
This broke cold start. She stopped rendering at all, not just after a switch. Reverted;
main.tsis untouched onmain. WhateverinitializeLive2Drebuilds afterwards, it does not rebuild everything those two releases destroy. Do not simply retry this.What to try instead
Stop unmounting the canvas. Mount
<Live2D />once, above the mode branch, and move it between the chrome's stage and the pal's slot with CSS rather than with the React tree. That deletes the whole class of bug instead of making teardown work, and it is the only approach that does not depend on the Cubism singletons being safely re-creatable — which attempt 2 shows they are not.App.tsx:108-110already hints this was the intent:The awkward part is window mode, where the canvas is a child of
ReceiverChrome's stage band and takes its geometry from that layout. Options: a stable fixed-position canvas whose rect tracks the stage band, or a portal into a slot the chrome renders. This wants a considered run, not a patch at the end of a session.Notes for whoever takes it
:160) is affected by the same cause is unverified. The Pixel shows an invisible avatar too, but it is running an APK from 2026-07-30 23:30 and has not been retested since.Related
denpa#35,denpa#67, design § 1b / § 2aMerged to
maininee25aaa(Merge the #62-#68 batch).Fix.
App.tsxnow portals the model into alive2dHostthat survives both the mode branch and the onboarding gate, so React no longer unmounts one canvas and mounts another on a mode change. The Cubism singletons keep the canvas they were bound to instead of being re-initialised against a removed one.The uncleaned 500ms timer in
use-live2d-model.tswas also given its cleanup — that was a real defect even though, as recorded on this issue, it was not the cause.Tests.
live2d-one-canvas.test.tspins that there is exactly one canvas across mode switches. Gate on merged main:Test Files 62 passed (62) / Tests 854 passed (854).Still open, deliberately. The release-and-reinitialise path this fix did not touch is #75 — switching 姿 still loses her, reproduced by the operator on 2026-07-31. Same singletons, different trigger.