What ships is listed under "Status" in the README, and how it works is in ARCHITECTURE.md. This file holds what is still open, the gates that keep heavier work from starting too early, and migration notes for shape changes.
- Real-display check of the stage frame and picture-in-picture. Headless traces measure main-thread load only. Frame pacing of the stage frame and of PiP (beta) on a real monitor has not been confirmed.
- Chat observation when YouTube replaces the chat. Re-attaching after
YouTube recreates
#items, and the stand-in chat frame used while the chat is closed, have no automated test. Both are checked by hand on a real stream. - PWA on real devices. Run the checklist below on the deployed PWA.
- Pin and drag in the PWA. The YouTube iframe owns pointer events there, so pinning needs a capture layer first. It works in the extension.
Devices: iOS Safari (current, low-power mode off and then on), Android Chrome (current, mid-range device), optionally an older Android device if frame p95 is close to the gate.
- Open the deployed PWA.
- Start a live stream from the URL field and from the Web Share Target.
- Tap-to-play, pause, and the custom controls work.
- Live hides the seekbar; replay enables it.
- Media Session metadata shows, and play/pause works where supported.
- Wake Lock keeps the screen on while in front and releases on stop.
- Open
?perf=1and watch an active chat for at least 3 minutes. Recordfps,frameP95,frameP99,longTasks,active, anddropped.
If a device fails, lower renderScalePct, spawnPerFrame, or maxActive
first. Give iOS and Android different renderScalePct defaults only if both
measurements support it. Put the final values and HUD captures in the release
notes.
Do not start these until the evidence exists.
The scorer is plain JavaScript in the content script (chat frame) and the PWA, and it is not the bottleneck. Add another transport only if all hold:
- a batch or stateful scorer exists outside the extension hot path first;
- Chrome/V8 benchmarks show a clear end-to-end win over the JS scorer;
- CONTRACT.md is updated before the extension depends on the new shape;
- the manifest CSP and web-accessible resources are reviewed again.
Keep Canvas2D unless a real device in the checklist above shows one of:
frameP95 > 33 msfor more than 30 s whileactive < maxActive;- long tasks growing repeatedly during normal chat bursts;
- touch or video controls visibly delayed while comments flow.
A prototype must include before/after HUD captures on those devices and must not regress emoji, Super Chat color, or comment-list scrolling.
The relay stays stateless HTTP polling (edge cache, in-flight collapse, rate limit; see "Relay robustness and cost" in ARCHITECTURE.md). Move on only when relay metrics show:
- a high MISS rate among many viewers of the same video;
- upstream 429/5xx bursts despite cache tuning;
- client-visible latency from repeated re-resolve or continuation churn.
Candidate: one Durable Object per video ID owns a single upstream poll loop and
broadcasts PollEnvelope batches to clients over WebSocket; clients send their
playback offset for replay. HTTP /api/livechat stays as the fallback until the
WebSocket path is measured. Acceptance: no scoring or rendering moves into the
Worker; per-video memory stays bounded under disconnect churn; replay offsets
are grouped or sampled so one scrubbing viewer cannot multiply upstream calls;
metrics compare upstream calls, latency, and errors between the two paths.
Shapes (see CONTRACT.md):
- The relay decodes one more layer of percent escapes in continuation tokens before validation and cache lookup. Envelope and client encoding are unchanged.
paidColoris an optional, display-only field onChatMessageand the render payload. Messages without it stay valid;amountstays nullable.- Replay polls send
replay=1withcontandoffset.cont+offsetwithoutreplay=1is a 400. The relay keeps replayended: false; the player decides when a replay is over.
Stored settings:
lineHeight(px) is no longer read. Stored values normalize to thelineHeightScaledefault; the engine still receives px (fontPx * scale).- The filter store gained
mode(drop/censor/replace) andreplacement. Older records normalize todropand the default replacement. - The
scaleWithPlayerboolean became thefontScaleModeselect (fixed/up/relative); a storedtruereads asup,falseasfixed.relativetreatsfontPxas the size at a 720 px player and also shrinks the text in smaller windows (down to 0.5x).
Record rejection reasons here.
Several settings and interactions (per-type visibility and size, author name
mode, outline, flow direction, density, maximum width, wrapping, user layer CSS,
pinning, time shift) follow ideas from ys-j/YoutubeLiveChatFlusher. They are
independent implementations; no code was copied.