Chrome MV3 extension for a Nico-style YouTube live chat overlay. It scores messages locally in JavaScript and uses that score to decide whether a comment should move quickly, normally, or slowly across the video.
The goal is to keep useful comments visible without letting short repeated reactions, emoji floods, or low-value bursts bury everything else.
The extension is published on the Chrome Web Store:
https://chromewebstore.google.com/detail/nkphcfhnfjceplpgcjccnpfdkheafohp
- extension ID:
nkphcfhnfjceplpgcjccnpfdkheafohp - store category: Entertainment
- listing languages: English and Japanese
- support and bug reports: https://github.com/hjosugi/smart-youtube-comment/issues
- privacy policy: docs/PRIVACY.md
The store listing tracks the last version that passed Chrome Web Store review,
so it can trail the version in this repository. To run the newest source, load
extension/ unpacked as described in Load In Chrome.
Published on the Chrome Web Store. Implemented:
- Chrome MV3 extension scaffold
- mobile PWA under
web/ - Cloudflare Worker live-chat relay under
worker/ - YouTube live-chat extraction from all frames
- top-frame canvas danmaku renderer
- JavaScript-only local scorer in
extension/scoring.js - settings and filter UI
- local sandbox and renderer performance probes
- release zip packaging
- security/supply-chain checks
Browser extraction and rendering are the practical bottlenecks, so scoring is kept small and local.
The extension runs extension/content.js in every YouTube frame.
- Chat frames watch YouTube live-chat renderer nodes.
- Each new chat message is normalized into a
ScoreInput. extension/scoring.jsreturns a localScoreResult.buildRenderPlan()maps the result into fast / normal / slow display timing.- A background service worker relays messages from chat frames to the top video frame.
- The top frame renders comments over the YouTube player.
The extension does not fetch remote code.
The extension scores, filters, and renders comments locally in the browser. Display, behavior, and performance settings are saved with Chrome Sync storage when available, so Chrome may sync those settings across the signed-in profile. Blocked users and blocked words are saved only in local extension storage on the current device. Chat text, author names, and block lists are not sent to the developer.
- Chrome or Chromium for loading the extension
- Node.js 22+ and npm 10+ for scripts
- Optional: Bun 1.3+ for faster local scripts
Install JS tooling:
npm installRun the security gate, typecheck, web build, unit suites, browser e2e suites when Chromium is installed, and sandbox smoke checks:
npm testRun the security gate directly:
npm run securityBun equivalents:
bun run test:bun
bun run security:bunRenderer performance probe:
npm run test:e2eReal extension smoke test in Chromium:
npm run test:extOpt-in real YouTube smoke:
SYC_REAL_YOUTUBE_URL="https://www.youtube.com/watch?v=..." npm run test:ext:youtubetest:ext opens a real browser locally. In CI it skips by default unless
SYC_REQUIRE_EXTENSION_E2E=1 is set. To make npm test fail instead of
skipping missing Chromium, set SYC_REQUIRE_E2E=1.
The Cloudflare Worker under worker/ relays YouTube InnerTube live-chat calls.
It defaults to the checked-in WEB client version, but production can override it
without a code change:
wrangler deploy --var INNERTUBE_CLIENT_VERSION:2.20260705.00.00GET /health returns the effective and default InnerTube client versions.
GET /health?deep=1&video=<11-char-id> runs a canary next probe with the same
effective client version; use it from an external scheduled monitor or Cloudflare
Cron Trigger to detect client-version rejection before viewers hit it.
Optional relay controls:
ALLOWED_ORIGINS=https://your-pwa.examplerejects browser callers from other origins.RATE_LIMIT_PER_MINUTE=120limits each client IP per Worker isolate; set0to disable.
Run:
npm run sandboxThen open:
http://127.0.0.1:4173/
The sandbox serves sandbox/index.html, loads the shared JS scorer, simulates
live chat, and renders comments over a fake video surface.
Use this path to run the current source instead of the published store build.
- Open
chrome://extensions. - Enable Developer mode.
- Click
Load unpacked. - Select the
extensiondirectory. - Open a YouTube live stream with live chat.
After changing content scripts or manifest files, reload the extension in
chrome://extensions and reload the YouTube tab.
Expected behavior:
- comments appear over the video in danmaku style
- short or spammy messages move faster
- higher-quality or emphasized messages move slower
- the seekbar-area toggle can hide/show danmaku
- default YouTube chat hide/show follows settings
Known limitation:
- YouTube pop-out chat runs in a separate tab without the video player, so this extension does not render pop-out chat messages over the original video tab. Use the normal embedded chat on the watch page for the overlay.
Create a tester zip:
npm run release:zipBun path:
bun run release:zip:bunArtifacts are written to .release/, which is ignored by Git. See
docs/RELEASE.md.
Store releases go out from a vX.Y.Z tag through
.github/workflows/chrome-webstore-release.yml.
The store listing and publisher setup is already done; see
docs/STORE_AUTOMATION.md for the credentials the
workflow needs.
Set package and manifest versions together:
npm run version:set -- 0.1.1This updates the root, web/, worker/, their lockfile root metadata, and
extension/manifest.json.
.
├── extension/
│ ├── manifest.json
│ ├── background.js
│ ├── scoring.js
│ ├── danmaku.js
│ ├── settings.js
│ ├── filter.js
│ ├── content.js
│ ├── options.html
│ ├── options.js
│ └── icons/
├── bench/
│ ├── danmaku-bench.html
│ └── e2e/
├── web/
│ ├── app.ts
│ ├── test/
│ └── dist/
├── worker/
│ ├── src/
│ ├── test/
│ └── wrangler.jsonc
├── sandbox/
│ └── index.html
├── docs/
│ ├── CONTRACT.md
│ ├── PERFORMANCE.md
│ ├── RELEASE.md
│ └── SECURITY.md
└── scripts/
├── check-sandbox.mjs
├── package-extension.mjs
├── security-check.mjs
├── serve-sandbox.mjs
└── set-version.mjs
Scoring is intentionally small and local. The next wins are in:
- YouTube chat extraction robustness
- queue/ring-buffer behavior
- text rasterization budget
- canvas draw loop
- Long Task and frame p95/p99 diagnostics
- keeping
maxActive=2000responsive through admission control
More notes are in docs/PERFORMANCE.md.
0BSD. You can use, copy, modify, and distribute this project for almost any purpose.